Skip to content

security: add mock signer authority core - #23

Merged
stacknil merged 12 commits into
mainfrom
stacknil/signer-v1-mock-core
Sep 18, 2026
Merged

stacknil merged 12 commits into
mainfrom
stacknil/signer-v1-mock-core

Conversation

@stacknil

@stacknil stacknil commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

Problem

The signer v1 reference core needs to bind publication authority to the reviewed GitHub App/source and to GitHub's case-insensitive Commit Status context namespace. It also needs finalization token replay protection to survive later malformed controller evidence.

The reviewed head 0930522f2e5e02e631c03c349207b42ad7a59176 left three Business B findings:

  • PUBLICATION_SOURCE_BINDING;
  • PUBLICATION_NAMESPACE_BINDING;
  • JTI_GLOBAL_SINGLE_USE.

This head remediates those three findings. Final approval remains subject to delayed independent review.

Invariant

  • (repository_id, policy_epoch) freezes exactly one (policy_digest, exact status_context, publisher_identity) tuple.
  • (repository_id, normalized_status_context) is historically reserved to one policy epoch, including after revocation.
  • Commit Status contexts are non-empty printable ASCII, at most 100 characters, and compared using explicit ASCII lowercase normalization.
  • The publication slot key remains (repository_id, head_oid, policy_epoch); publisher identity is immutable slot provenance, not an additional parallel slot dimension.
  • Publication receipts bind provider record ID, payload digest, and publisher identity.
  • A verified finalization JTI is consumed after exact execution binding and before expiry, PR freshness, or controller-evidence parsing.
  • This PR remains mock-only and cannot publish a real GitHub status.

Design decision

Epoch authority and physical context

The registry replaces digest-only epoch binding with an immutable epoch authority identity containing policy digest, exact status context, and publisher identity. Operational revisions may still change reviewed execution-only fields such as workflow SHA when that authority tuple is unchanged.

A second append-only mapping reserves each normalized physical status context to one epoch. Revocation releases neither mapping. Publisher/App rotation therefore requires an explicit reviewed epoch/context migration.

Publication source provenance

PublicationSlot stores publisher identity while retaining the existing semantic slot key. Reservation requires exact payload, digest, and source equality before any existing-published shortcut. A source mismatch returns publication_source_conflict before provider access or evaluation publication.

PublicationReceipt now records publisher identity. Store transitions reject a matching payload digest from the wrong source with publication_receipt_source_mismatch. MockPublisher creates receipts from its own identity and uses the normalized physical context key for publish and lookup.

Publisher identity remains internal provenance and is not added to GitHub Commit Status payload fields or the canonical payload digest.

Finalization JTI consumption

After verified OIDC authorization, exact execution-tuple matching, and admission-token freshness checks, finalization atomically consumes the JTI before later evidence validation. Malformed evidence cannot leave that token reusable.

The design record states that a future durable store must commit JTI consumption as an independent security side effect rather than roll it back with a later parser refusal.

Threat / failure model

The focused regressions cover:

  • same epoch with a changed digest, context, or publisher;
  • same physical context reused by another epoch, including case-only variants and post-revocation reuse;
  • a permissive provider that would accept repeated opposite writes;
  • a published publisher-A slot presented to a publisher-B evaluation before the shortcut;
  • correct payload digest with wrong receipt source;
  • UNKNOWN lookup reconciliation using a foreign-source receipt;
  • malformed controller evidence followed by same-JTI and fresh-JTI retries;
  • existing same-H sharing, uncertainty, monotonic-state, race, and verdict semantics.

Rejected alternatives

  1. Add publisher identity to the slot key.
    Rejected because it would permit parallel A/B slots for one semantic authority epoch.

  2. Encode publisher identity into GitHub status fields.
    Rejected because source provenance is not part of the provider request payload.

  3. Allow context reuse after revocation.
    Rejected because historical provider statuses remain and could acquire a new authority meaning.

  4. Depend on MockPublisher rejecting opposite payloads.
    Rejected because GitHub accepts repeated status writes; registry/service invariants are tested with a permissive provider.

  5. Consume finalization JTI after evidence parsing.
    Rejected because authenticated malformed evidence would leave a reusable token.

Compatibility

The slot key, same-H cross-PR sharing, semantic verdict mapping, UNKNOWN state machine, admission idempotency, controller parser, OIDC architecture, acquisition, reader, materializer, scanner, baseline, report schemas, workflows, branch rules, and PR #22 remain unchanged.

Status contexts are deliberately narrowed to printable ASCII because the production authority context is ASCII and GitHub's physical namespace is case-insensitive.

Original exact base: 8e9e4c53a123bc285dd3f7c523427747326ab281

Reviewed remediation base: 0930522f2e5e02e631c03c349207b42ad7a59176

Exact head: feb40fdbaf178a7688ecac3258384f0608f59dd9

Validation

  • signer-focused unit tests: 76 passed;
  • full repository suite: 254 passed, 4 skipped;
  • Ruff check and format check: 9 changed Python files passed;
  • taxonomy, README snapshot, pattern-library contract, and 157-file Markdown validation passed;
  • markdownlint-cli2 0.18.1: design record passed with 0 errors;
  • pre-commit changed-file hooks passed;
  • pinned repo-sentinel-lite==0.8.1 changed-file gate: 10 files, 0 findings;
  • candidate digest, canary App IDs, credential/private-key/token patterns, local paths, privacy identifiers, and forbidden-scope scans: 0 matches;
  • git diff --check: passed.

Rollback

Revert the five remediation commits after 0930522f. No workflow, rule, credential, external status, production registry, or durable store depends on this mock core.

PR #23 remains Draft and unmerged pending delayed final security review.

@stacknil

Copy link
Copy Markdown
Owner Author

Post-CI final diff review completed on exact head 0930522f2e5e02e631c03c349207b42ad7a59176.

  • Design decision: keep signer v1 as an isolated mock-only authority core with server-selected PR/policy identity, independent result parsing, monotonic transaction-safe evaluation state, and explicit indeterminate-publication reconciliation.
  • Main risk: production OIDC verification, durable storage, GitHub App publication, and operational recovery are intentionally absent; none should be inferred from the mock interfaces.
  • Compatibility: additive 13-file change only; no acquisition, reader/materializer, scanner, baseline, report schema, workflow, branch-rule, or candidate-policy behavior changed.
  • Rollback: revert this PR; no production workflow, credential, external status, or migrated data depends on it.
  • Validation: all configured CI checks are green; local signer tests (64), full suite (242 with 4 skips), Ruff, repository contracts, pre-commit, diff check, and pinned changed-file gate passed.

The PR remains Draft for delayed final security review. No workflow activation or merge is requested.

@stacknil

Copy link
Copy Markdown
Owner Author

Post-CI remediation diff review completed on exact head feb40fdbaf178a7688ecac3258384f0608f59dd9.

  • Design decision: freeze (policy_digest, exact status_context, publisher_identity) per epoch, reserve normalized physical contexts historically across epochs, bind publisher provenance inside the unchanged semantic slot, and consume authenticated finalization JTIs before later evidence checks.
  • Main risk: production durability must commit JTI consumption independently of later parser refusal; this PR documents but does not implement a production database, GitHub App adapter, or remote status write.
  • Compatibility: the remediation changes 10 signer/test/design files only. OIDC architecture, server-side H selection, parser and verdict semantics, UNKNOWN transitions, same-H sharing, acquisition, policy, workflows, rules, and PR security: construct candidate v2 policy bundle #22 remain unchanged.
  • Rollback: revert commits 463d829 through feb40fd; no workflow, credential, external status, or production registry depends on them.
  • Validation: 76 signer tests and 254 full-suite tests passed; Ruff, pre-commit, repository contracts, markdownlint, privacy/credential scans, diff check, and the pinned repo-sentinel-lite==0.8.1 10-file changed gate passed with 0 findings.

The three Business B findings are remediated in this head, pending delayed independent security review. PR #23 remains Draft; no merge or workflow activation is requested.

@stacknil
stacknil marked this pull request as ready for review September 18, 2026 15:43
@stacknil
stacknil merged commit d35e5a6 into main Sep 18, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant