Skip to content

feat(spec): signed waiver records for the #947 assessment chain (#947 increment 6) - #1060

Open
seonghobae wants to merge 1 commit into
feat/transitive-dependency-assessment-20260902from
feat/waiver-signature-20260902
Open

feat(spec): signed waiver records for the #947 assessment chain (#947 increment 6)#1060
seonghobae wants to merge 1 commit into
feat/transitive-dependency-assessment-20260902from
feat/waiver-signature-20260902

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

What

Sixth #947 increment. Adds app.spec.waiver_record — a pure, IO-free
function pair that makes a normalization-assessment waiver tamper-evident
so an auditor can trust one without re-reviewing it.

  • sign_waiver(waiver, *, signer, signed_at, key_id, key) deep-copies the
    waiver and returns {"waiver", "signature": {"algo", "signer", "signed_at", "key_id", "value"}}. value is an HMAC-SHA256 over the canonical JSON of
    the waiver with the signature metadata folded in as _meta, so
    changing the signer or the timestamp invalidates the signature exactly as
    changing the waiver body does.
  • verify_waiver_signature(record, *, key) recomputes that HMAC and compares
    with hmac.compare_digest (constant time). Body/metadata tamper or a wrong
    key → False; a missing signature or a non-hmac-sha256 algo
    ValueError.
  • Canonical form sorts keys at every level, so a waiver rebuilt in a
    different key order still verifies. The secret key is caller-supplied and
    never stored, logged, or echoed into the record.

Why

The assessment modules already accept caller-supplied waivers but trust them
as-is. This is the signing/verification core for an audit trail; persisting
the signed records stays deferred (storage-layer work, tracked in the
doctoring note).

Tests / checks

  • 20 test cases: round-trip verify, per-field body tamper, nested-value
    tamper, per-field signature-metadata tamper, doctored digest, wrong key,
    missing signature, unsupported algo, key-order independence, JSON
    round-trip, determinism, blank-metadata / empty-key rejection.
  • PYTHONPATH=. mypy app clean (75 files); interrogate 100%;
    tests/test_docstrings.py green.

Standards (APA 7th)

Stack

Branched off feat/transitive-dependency-assessment-20260902 (PR #1048, the
#947 chain tip). Blocked from merge by the org CI incident
ContextualWisdomLab/.github#1531 like the rest of the loop's stack.

🤖 Generated with Claude Code


Devin Review

…increment 6)

app.spec.waiver_record — a pure, IO-free function pair that makes a
normalization-assessment waiver tamper-evident:

- sign_waiver(waiver, *, signer, signed_at, key_id, key) deep-copies the
  waiver and returns {"waiver", "signature"} where signature.value is an
  HMAC-SHA256 over the canonical JSON of the waiver with the signer /
  signed_at / key_id metadata folded in as _meta, so altering the
  metadata invalidates the signature exactly as altering the body does.
- verify_waiver_signature(record, *, key) recomputes and compares with
  hmac.compare_digest. Body/metadata tamper or wrong key -> False;
  missing signature or non-hmac-sha256 algo -> ValueError.

Canonical form sorts keys at every level (key-order independent). The
secret key is caller-supplied and never stored, logged, or echoed back.
20 test cases (round-trip, per-field tamper, wrong key, JSON round-trip,
determinism, blank-metadata rejection). mypy clean, interrogate 100%.
Cites NIST FIPS 198-1 and RFC 8785; doctoring note updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013SeQS8tSee5QVeyGpJ9SaY
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 84297d56-faa8-4833-985d-ec70ce46e800

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

Devin Review

Comment on lines +27 to +34
References (APA 7th):

* National Institute of Standards and Technology. (2008). *The keyed-hash
message authentication code (HMAC)* (FIPS PUB 198-1).
https://doi.org/10.6028/NIST.FIPS.198-1
* Rundgren, A., Jordan, B., & Erdtman, S. (2020). *JSON Canonicalization
Scheme (JCS)* (RFC 8785). RFC Editor.
https://doi.org/10.17487/RFC8785

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Research grounding is incomplete

The signing feature adds standards citations but no academic paper PDF or redistribution assessment. Repository governance requires this grounding for substantive features.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

"""Compute the HMAC-SHA256 hex digest over the waiver plus its signature meta."""

return hmac.new(
key, _canonical({**waiver, "_meta": meta}), hashlib.sha256

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Reserved metadata bypasses waiver integrity

When a waiver contains _meta, sign_waiver excludes its value from the signature. Attackers can alter that field while verification still succeeds.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +61 to +63
return json.dumps(
waiver, sort_keys=True, separators=(",", ":"), default=str
).encode("utf-8")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟨 Ambiguous values share valid signatures

default=str gives distinct waiver values identical signed bytes. A numeric object can become an authenticated string without invalidating verification.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +163 to +165
meta = {field: str(signature.get(field, "")) for field in _META_FIELDS}
expected = _expected_value(waiver, meta, bytes(key))
return hmac.compare_digest(expected, str(signature.get("value", "")))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟨 Metadata type changes evade verification

verify_waiver_signature coerces metadata with str. Attackers can replace a signed string with an equal-looking non-string while verification still succeeds.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@seonghobae seonghobae added area: api API, protocol, event, or external contract priority: high High-priority or P1 work security status: blocked Blocked by conflict, dependency, or required prerequisite type: feature New or expanded product capability labels Sep 2, 2026 — with ChatGPT Codex Connector
seonghobae added a commit that referenced this pull request Sep 2, 2026
Gate still frozen (main@8dc74692; .github#1531 queue oscillating
~1700-1900, no drainage). iter36 gap-baseline consolidation: record PR
#1060 (pure sign_waiver / verify_waiver_signature HMAC-SHA256
tamper-evidence pair) in the #947 "This loop's increment PRs" list.
Stacked-PR count 17 -> 18; #947 merge-wave chain extended to
#1048 -> #1060; #947 "Remaining increments" reworded (signing core
landed, persistence remains). CHANGELOG [Docs] bullet count 17 -> 18.
MD018-clean. Docs-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013SeQS8tSee5QVeyGpJ9SaY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: api API, protocol, event, or external contract priority: high High-priority or P1 work security status: blocked Blocked by conflict, dependency, or required prerequisite type: feature New or expanded product capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant