Skip to content

security(attestation): reject same-inode manifest/predicate receipt pairs #2316

Open

Description

@seonghobae

Finding

The new signer-facing receipt reader from #2315 rejects only textually identical absolute paths before it reads the manifest and predicate. Two different pathnames can still be hard links to the same regular inode. Because the reader closes the first descriptor before opening the second, that inode can be rewritten between reads so one physical file first supplies a valid manifest and then a valid predicate. The manifest can pre-commit the later predicate hash, so semantic validation can succeed even though the two receipt roles were never represented by distinct inodes at intake time.

This violates the existing signer-handoff intent that one leaf must not masquerade as both completion marker and predicate. It is a filesystem-identity defect, not a psychometric or signature claim.

RED / acceptance

  • Add a hostile hard-link fixture using distinct pathnames that resolve to the same inode and require deterministic EvidenceError refusal before semantic validation.
  • Derive receipt identity from the already-open descriptor (fstat) rather than a pathname stat, so the check is not reopened as a TOCTOU.
  • Preserve the existing one-open, bounded, O_NOFOLLOW | O_NONBLOCK, regular-file and exact-byte contracts.
  • Do not require st_nlink == 1; legitimate files may have other hard links. Only reject when the manifest and predicate descriptors resolve to the same (st_dev, st_ino) identity.
  • Keep the reader uncredentialed. Do not add signer/OIDC authority or weaken validate_receipt_manifest().

Refs #2299 #2300 #2315 ContextualWisdomLab/TEPP#637.

Activity

  1. seonghobae commented on Sep 21, 2026

    @seonghobae
    ContributorAuthor

    Owner repair is ordinary-forward on #2300. Source-level RED dc9a4da2b523d4c9c295fa2b9ad69a9d6736efae adds a distinct-path hard-link fixture and requires refusal before semantic validation. Causal production repair 6ff01498102dd841f8f04feeaaa644591327ad8e captures (st_dev, st_ino) from the same open descriptor used for the bounded read and rejects equal manifest/predicate identities without adding a pathname stat() race. CHANGELOG db517e96d5a4b35880100c309702ef334d86253e and doctoring/current head 2beaea8754d8d8fd1e3766ece1904fdbc86001cc record the boundary. Current exact-head Scientific Validation Evidence Quality run 35551011522 is queued; verifier-quality job 106185643090 has no assigned runner and no steps, so no hosted GREEN is claimed. Keep this issue open until exact-head settlement/review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions