{
 "actor": {
  "kind": "model",
  "lab": "anthropic",
  "name": "claude-opus-5-5"
 },
 "at": "2026-11-05T22:55:48Z",
 "attempt_log": [
  {
   "attempt": 1,
   "guard": {
    "added_required_params": [],
    "line_delta": 6,
    "new_imports": [],
    "ok": true,
    "path": "webhook.py",
    "public_api_preserved": true,
    "removed_lines": 0,
    "removed_public": [],
    "violations": [],
    "warnings": []
   },
   "reasons": [],
   "verdict": "VERIFIED_FIX"
  }
 ],
 "claude": {
  "attempts": 1,
  "first_attempt": {
   "input_tokens": 808,
   "model": "claude-opus-5-5",
   "output_tokens": 1400,
   "request_id": null,
   "seconds": 14.4,
   "stop_reason": "end_turn"
  },
  "input_tokens": 808,
  "model": "claude-opus-5-5",
  "output_tokens": 1400,
  "request_id": null,
  "seconds": 14.4,
  "stop_reason": "end_turn"
 },
 "claude_test_code": "import hashlib\nimport hmac\n\nimport webhook\n\n\ndef _sig(secret, body):\n    return hmac.new(secret, body, hashlib.sha256).hexdigest()\n\n\ndef test_valid_signature_accepted():\n    assert webhook.check_webhook(b\"k\", b\"payload\", _sig(b\"k\", b\"payload\")) is True\n\n\ndef test_wrong_signature_rejected():\n    assert webhook.check_webhook(b\"k\", b\"payload\", _sig(b\"other\", b\"payload\")) is False\n    assert webhook.check_webhook(b\"k\", b\"payload\", \"\") is False\n\n\ndef test_non_ascii_signature_rejected_without_error():\n    assert webhook.check_webhook(b\"k\", b\"payload\", \"\\u00e9\" * 64) is False\n\n\ndef test_uses_constant_time_compare():\n    calls = []\n    original = hmac.compare_digest\n\n    def spy(a, b):\n        calls.append((a, b))\n        return original(a, b)\n\n    hmac.compare_digest = spy\n    try:\n        result = webhook.check_webhook(b\"k\", b\"body\", _sig(b\"k\", b\"body\"))\n    finally:\n        hmac.compare_digest = original\n    assert result is True\n    assert len(calls) == 1\n",
 "diff": "--- a/webhook.py\n+++ b/webhook.py\n@@ -4,5 +4,11 @@\n \n def check_webhook(secret: bytes, body: bytes, signature: str) -> bool:\n     \"\"\"True if `signature` is the hex HMAC-SHA256 of body under secret.\"\"\"\n+    if not isinstance(signature, str):\n+        return False\n     expected = hmac.new(secret, body, hashlib.sha256).hexdigest()\n-    return signature == expected\n+    # Compare as bytes so non-ASCII input cannot raise TypeError, and use a\n+    # constant-time comparison so timing does not leak the expected digest.\n+    return hmac.compare_digest(\n+        signature.encode(\"utf-8\"), expected.encode(\"ascii\")\n+    )\n",
 "explanation": "The signature check compared the attacker-supplied signature against the expected HMAC with `==`, which short-circuits on the first differing character and can leak the expected digest through timing, so the function did not provide the safe verification a webhook check implies. The fix computes the same HMAC-SHA256 hex digest and compares it with hmac.compare_digest. Both sides are encoded to bytes first so a non-ASCII signature is rejected instead of raising TypeError, and a non-str signature returns False. The tests check that valid signatures are accepted, invalid and non-ASCII ones are rejected, and that hmac.compare_digest is actually called. The original file fails that last test.",
 "falsework_after": {
  "findings": [],
  "grade": "REAL"
 },
 "falsework_before": {
  "findings": [
   {
    "check": "secret_compare_not_constant_time",
    "detail": "`expected` is compared with ==/!=, which is not constant-time: use hmac.compare_digest.",
    "line": 8,
    "severity": "MEDIUM",
    "snippet": "return signature == expected"
   }
  ],
  "grade": "PARTIAL"
 },
 "finding": {
  "check": "secret_compare_not_constant_time",
  "detail": "`expected` is compared with ==/!=, which is not constant-time: use hmac.compare_digest.",
  "line": 8,
  "severity": "MEDIUM",
  "snippet": "return signature == expected"
 },
 "finding_fingerprint": "4a648d97ef4b57c445404a2fe4f1ab1f21e7f3f7c367634d3f580db5c995a007",
 "fixed_code": "import hashlib\nimport hmac\n\n\ndef check_webhook(secret: bytes, body: bytes, signature: str) -> bool:\n    \"\"\"True if `signature` is the hex HMAC-SHA256 of body under secret.\"\"\"\n    if not isinstance(signature, str):\n        return False\n    expected = hmac.new(secret, body, hashlib.sha256).hexdigest()\n    # Compare as bytes so non-ASCII input cannot raise TypeError, and use a\n    # constant-time comparison so timing does not leak the expected digest.\n    return hmac.compare_digest(\n        signature.encode(\"utf-8\"), expected.encode(\"ascii\")\n    )\n",
 "fixed_sha256": "017e0a778995097c4448cb6b0f7dd8f069374ec4b32e7563718109757958ab3b",
 "guard": {
  "added_required_params": [],
  "line_delta": 6,
  "new_imports": [],
  "ok": true,
  "path": "webhook.py",
  "public_api_preserved": true,
  "removed_lines": 0,
  "removed_public": [],
  "violations": [],
  "warnings": []
 },
 "lines_changed": 8,
 "original_code": "import hashlib\nimport hmac\n\n\ndef check_webhook(secret: bytes, body: bytes, signature: str) -> bool:\n    \"\"\"True if `signature` is the hex HMAC-SHA256 of body under secret.\"\"\"\n    expected = hmac.new(secret, body, hashlib.sha256).hexdigest()\n    return signature == expected\n",
 "original_sha256": "28c72a441c26f9cb26f279d9b58662595eef8079e4f0604a8b74f19be93e9d5d",
 "path": "webhook.py",
 "prev_record_hash": "92801b41518909581778f57e225c9e2e3a40186cce5d20d78041dd8191fa04ee",
 "public_key_b64": "b6hfA2rpAdV29FOdhNXGkk4t91mCV0AnfFYwq0HO0R8=",
 "reasons": [],
 "record_hash": "171e88f4ee9cf58ca0d42a3eb28423d689a3858bde07f0254e584fa3f1fcd3ff",
 "reference_test_author": "customer:live-probe",
 "reference_test_author_lab": "customer",
 "reference_test_independence": "independent",
 "reference_test_origin": "caller",
 "reference_test_sha256": "efd2ff535a84c27e0211ca6e7cf17411432a5db9848d3dbe39eb9f20567b07cd",
 "run_id": "ks-20261105T225548Z-4f35c76d",
 "sample_id": null,
 "schema": "cain42.keystone.fix-proof.v1",
 "sequence": 10,
 "signature_b64": "gj7KK9XF7VVKkQkU7K73F9BS1sAI9HYjIYkOzEhWd5BJb9SWr9g7RWwX6QfEnqpD5QX9nQXObkLXUiGnRj3TDQ==",
 "standard": "cain42.keystone.proof-standard.v2",
 "tests": {
  "claude": {
   "after": {
    "all_passed": true,
    "ran": true,
    "results": [
     {
      "file": "test_claude.py",
      "outcome": "passed",
      "test": "test_non_ascii_signature_rejected_without_error"
     },
     {
      "file": "test_claude.py",
      "outcome": "passed",
      "test": "test_uses_constant_time_compare"
     },
     {
      "file": "test_claude.py",
      "outcome": "passed",
      "test": "test_valid_signature_accepted"
     },
     {
      "file": "test_claude.py",
      "outcome": "passed",
      "test": "test_wrong_signature_rejected"
     }
    ],
    "some_not_passed": false
   },
   "before": {
    "all_passed": false,
    "ran": true,
    "results": [
     {
      "file": "test_claude.py",
      "outcome": "passed",
      "test": "test_non_ascii_signature_rejected_without_error"
     },
     {
      "detail": "assertion",
      "file": "test_claude.py",
      "outcome": "not_passed",
      "test": "test_uses_constant_time_compare"
     },
     {
      "file": "test_claude.py",
      "outcome": "passed",
      "test": "test_valid_signature_accepted"
     },
     {
      "file": "test_claude.py",
      "outcome": "passed",
      "test": "test_wrong_signature_rejected"
     }
    ],
    "some_not_passed": true
   }
  },
  "reference": {
   "after": {
    "all_passed": true,
    "ran": true,
    "results": [
     {
      "file": "test_reference.py",
      "outcome": "passed",
      "test": "test_comparison_is_constant_time"
     },
     {
      "file": "test_reference.py",
      "outcome": "passed",
      "test": "test_genuine_signature_is_accepted"
     },
     {
      "file": "test_reference.py",
      "outcome": "passed",
      "test": "test_wrong_signature_is_rejected"
     }
    ],
    "some_not_passed": false
   },
   "before": {
    "all_passed": false,
    "ran": true,
    "results": [
     {
      "detail": "assertion",
      "file": "test_reference.py",
      "outcome": "not_passed",
      "test": "test_comparison_is_constant_time"
     },
     {
      "file": "test_reference.py",
      "outcome": "passed",
      "test": "test_genuine_signature_is_accepted"
     },
     {
      "file": "test_reference.py",
      "outcome": "passed",
      "test": "test_wrong_signature_is_rejected"
     }
    ],
    "some_not_passed": true
   }
  }
 },
 "verdict": "VERIFIED_FIX"
}
