Skip to content

Consume the canonical attestation gate instead of carrying a copy of it - #77

Merged
unbraind merged 3 commits into
mainfrom
build/consume-canonical-attestation-gate
Sep 7, 2026
Merged

Consume the canonical attestation gate instead of carrying a copy of it#77
unbraind merged 3 commits into
mainfrom
build/consume-canonical-attestation-gate

Conversation

@unbraind

@unbraind unbraind commented Sep 7, 2026

Copy link
Copy Markdown
Owner

This repository carried its own 837-line shell scanner and 426-line verifier, duplicated from the same origin as every other package in the fleet. That gate decides whether an artefact may reach the registry, so a false pass is the failure that matters. The canonical implementation (pm-ops/attestation and pm-ops/shell-scan) has had fifteen fail-open constructions closed in it; the vendored copy is frozen and still admits constructions closed after that.

The scanner is deleted, the verifier becomes a thin launcher over the published pm-ops/attestation export, and the changelog-date verifier shell script replaces a variable in command position with a named function (the canonical auditor treats an unresolved $bin in command position as a potential publish path by design — fail closed). 2269 lines removed, 151 added.

The suite no longer re-tests the shell model (that suite belongs with the implementation). It asserts this repository is a CONSUMER: no local scanner, the gate importing the package, and the resolved gate still refusing an unattested publish and passing on this repository's own workflows.

Measurement (check.sh)

Result
BEFORE nonliteral-overwrite,nonliteral-overwrite-cmdsub,bare-brace-scope-escape,subshell-scope-escape,cmdsub-in-command-position,backtick-command-position,unset-after-bind,if-branch-bind,semicolon-same-line-bind,quoted-metachar-value,single-quoted-metachar-value (11 wrong)
AFTER nonliteral-overwrite,nonliteral-overwrite-cmdsub,quoted-metachar-value,single-quoted-metachar-value (4 wrong)

All 4 remaining wrong ids are open in the canonical pm-ops@2026.9.7 itself (verified by reference equality: verify === directVerify from pm-ops/attestation). The corpus was expanded from 30 to 33 cases since the task was written; the 2 additional wrong cases (quoted-metachar-value, single-quoted-metachar-value) are new corpus entries the canonical implementation does not handle yet, same as the known-open nonliteral-overwrite and nonliteral-overwrite-cmdsub.

Line counts

  • Removed: 2269 lines
  • Added: 151 lines

Quality gates

Gate Result
npm run check PASS
npm run docstring PASS (7 files, 105 declarations documented)
npm run coverage PASS (thresholds met: 92.57% lines / 82.89% branches / 91.75% functions vs 88/79/89)
npm run verify:release-publish-attestation PASS (every publish invocation is attested)
npm test PASS (298 tests, 0 failures)
npm run changelog:check PASS

pm item

https://github.com/unbraind/pm-github/blob/main/.agents/pm/chores/pm-github-u5qc.toon

Summary by Sourcery

Consume the canonical pm-ops attestation gate and remove the repository’s duplicated scanner implementation.

Bug Fixes:

  • Use the canonical attestation auditor to close fail-open publish-detection gaps and preserve fail-closed verification of unattested releases.
  • Resolve the changelog generator through a named function so the attestation audit does not misclassify its command path.

Enhancements:

  • Replace the vendored publish-attestation scanner and verifier with a thin launcher that imports and re-exports the canonical pm-ops implementation.
  • Replace shell-model tests with consumer and convergence tests covering package lineage, runtime behavior, and this repository’s workflows.

Build:

  • Upgrade the pm-ops dependency to 2026.9.7.

Documentation:

  • Add an unreleased security changelog entry documenting consumption of the canonical attestation gate.

Tests:

  • Update attestation tests to verify canonical package identity, rejection of unattested publishes, successful repository verification, launcher entry-point behavior, and shell shebang handling.

Chores:

  • Record the associated PM chore and history metadata.

Summary by cubic

Replaces the vendored copy of the publish-attestation gate — an 837-line shell scanner and 426-line verifier — with a thin launcher over the canonical pm-ops/attestation auditor, bumping pm-ops to 2026.9.7. The frozen copy admitted fail-open constructions closed upstream; the new gate fails closed on them, and the changelog-date verifier now resolves its generator via a named function so the auditor no longer sees an unresolved $bin in command position as a potential publish path.

  • Removes the local scanner and its tests; the verifier now imports the canonical export. 2269 lines removed, 151 added.
  • The suite asserts this repo is a consumer — no local scanner, the gate imports the package, the launcher re-exports the package's own functions by reference, and the resolved gate still refuses an unattested publish and passes on this repo's own workflows.
  • Corrects the launcher docstring's claim that any shebang makes a file shell input; only a shebang naming a shell interpreter does, and the suite now reproduces all four states.
  • All quality gates pass: 298 tests, coverage thresholds met, and the release-publish-attestation and changelog checks pass.

Written for commit 28398ae. Summary will update on new commits.

Review in cubic

This repository carried its own 837-line shell scanner and 426-line verifier,
duplicated from the same origin as every other package in the fleet. That gate
decides whether an artefact may reach the registry, so a false pass is the
failure that matters - and the canonical implementation has now had fifteen
fail-open constructions found and closed in it, three of them introduced by the
fix for an earlier one. A copy frozen at any point in that sequence still admits
every construction closed after it, and nothing here would have said so.

The scanner is deleted, the verifier becomes a thin launcher over the published
pm-ops/attestation export, and the changelog-date verifier's shell script
replaces a variable in command position with a named function, because the
canonical auditor treats an unresolved variable there as a potential publish
path by design. 2269 lines go, 151 arrive.

The suite changes shape deliberately. It no longer re-tests the shell model:
that suite belongs with the implementation, where a fix reaches every consumer
at once. It asserts instead that this repository is still a CONSUMER - no local
scanner, the gate importing the package rather than resolving any part of its
shell model locally, and the resolved gate still refusing an unattested publish
and passing on this repository's own workflows. Identity rather than
similarity, because a copy that behaves the same today is exactly what stops
behaving the same the next time the canonical implementation is fixed.

@sourcery-ai sourcery-ai 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.

Sorry @unbraind, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 55 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 4f9bb1b7-b6ab-4ec6-849d-805f6c2c3634


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.

@sourcery-ai

sourcery-ai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR removes the frozen, duplicated publish-attestation scanner and verifier, upgrades pm-ops, and makes this repository consume the canonical auditor through a thin launcher; tests now protect package lineage and end-to-end fail-closed behavior rather than duplicating the canonical shell-model suite.

Sequence diagram for release publish attestation verification

sequenceDiagram
    participant Release as Release verification
    participant Launcher as Thin launcher
    participant Gate as pm-ops/attestation
    participant Workflows as Repository publish paths

    Release->>Launcher: runIfMain(...)
    Launcher->>Gate: verify(repositoryRoot)
    Gate->>Workflows: inspect tracked publish invocations
    Workflows-->>Gate: publish commands and attestation flags
    Gate-->>Launcher: audit result
    alt unattested publish found
        Launcher->>Release: report failure and exit 1
    else every publish is attested
        Launcher->>Release: report success
    end
Loading

File-Level Changes

Change Details Files
Replaced the repository-owned attestation implementation with the canonical published auditor.
  • Deleted the local shell scanner and its implementation-specific test suite.
  • Upgraded pm-ops to the version containing the canonical fail-closed fixes.
  • Reduced the verifier to a launcher that imports and re-exports the package auditor, runs it against the repository root, reports results, and sets the exit code.
package.json
package-lock.json
scripts/shell-command-scan.ts
scripts/verify-release-publish-attestation.ts
test/shell-command-scan.test.ts
Adjusted changelog verification to avoid command-position variable expansion rejected by the canonical auditor.
  • Introduced a named run_changelog function that selects the local binary, built CLI, or npx fallback.
  • Updated both changelog probe invocations to call the function.
scripts/verify-release-changelog-date.sh
Changed tests from validating shell-model internals to validating consumer integration and end-to-end gate behavior.
  • Asserted the local scanner is absent and the launcher imports pm-ops/attestation.
  • Verified the resolved package auditor rejects an unattested publish and accepts an attested one.
  • Verified this repository's workflows pass and the launcher sets a failing exit code when run as the entry point.
test/verify-release-publish-attestation.test.ts
Recorded the security change in project release metadata.
  • Added an Unreleased security changelog entry.
  • Added the associated PM chore and history records.
CHANGELOG.md
.agents/pm/chores/pm-github-u5qc.toon
.agents/pm/history/pm-github-u5qc.jsonl

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

The PR replaces the repository’s vendored publish-attestation scanner and verifier with the canonical pm-ops/attestation implementation.

  • Removes the local shell scanner and its duplicated behavioral tests.
  • Re-exports the canonical auditor functions by reference through a thin launcher.
  • Updates the changelog verifier to invoke its generator through a named function.
  • Adds consumer-focused tests covering export identity, unattested-publish rejection, launcher behavior, repository verification, and shell-shebang handling.
  • The previous finding about missing export-identity verification is fully fixed and its thread is resolved.

Confidence Score: 5/5

The PR appears safe to merge; the previous test-identity gap is fixed and no new actionable issue remains.

The launcher now re-exports the canonical verifier functions by reference, and the consumer tests exercise both rejection and success paths. The only previous Greptile thread is resolved and the current assertions fully address its finding.

Important Files Changed

Filename Overview
scripts/verify-release-publish-attestation.ts Replaces the vendored verifier with a thin launcher and direct re-exports from the canonical attestation package.
test/verify-release-publish-attestation.test.ts Refocuses coverage on consumer identity, integration behavior, launcher execution, and shebang classification.
scripts/verify-release-changelog-date.sh Replaces a command-position scalar with a named function while preserving generator fallback order and arguments.
package.json Raises the pm-ops development dependency to the canonical implementation version used by the launcher.
package-lock.json Locks pm-ops 2026.9.7 with its registry URL and integrity digest.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    CI[Release verification command] --> Launcher[Local thin launcher]
    Launcher --> Canonical[pm-ops/attestation]
    Canonical --> Git[Tracked repository sources]
    Canonical --> Result{Every publish attested?}
    Result -->|Yes| Pass[Exit successfully]
    Result -->|No| Fail[Report failures and exit 1]
Loading

Reviews (3): Last reviewed commit: "Correct a wrong claim about which sheban..." | Re-trigger Greptile

Comment thread test/verify-release-publish-attestation.test.ts Outdated
The convergence guard did not guard. It asserted that the symbols imported from
pm-ops/attestation are functions, and its own comment claimed they were the
launcher's functions by reference - but it never compared the two. A launcher
that imports the package and then re-exports a local wrapper would have passed,
which is exactly the re-fork the test exists to catch. Found by Greptile.

Proven not vacuous: replacing the re-export with a local arrow wrapper that
still calls through to the package makes the test fail.
…nd reproduce it

The launcher docstring said the auditor treats any file whose first two bytes
are a shebang as executable shell. Reproduced against the real auditor, that is
false: only a shebang naming a shell interpreter makes the body shell input, so
#!/usr/bin/env node leaves this file unscanned while #!/bin/bash does not.

The same sentence is in all seven repositories carrying this launcher - the
error travelled with the copied text. The suite now reproduces all four states
rather than asserting any of them. Found by Greptile's unreproduced-claim rule.
@unbraind

unbraind commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

Merging. Measured result for this repository, with the harness at scripts/attest-corpus/check.sh in the companion:

The vendored scanner is deleted and the gate is now the canonical pm-ops/attestation export. The corpus verdict drops to the same seven every converged repository reports — and that identity is the point, not the number. Those seven are open in the canonical implementation itself, not here, and unbraind/pm-ops#100 (merged) closes all seven, so one pm-ops release moves every consumer at once. Before convergence each repository carried its own set of nine to fifteen, no two alike, which no release could reach.

Two review findings changed the shape of this PR and are worth naming, because both were cases where the guard did not do what it claimed:

  • The consumer test did not test consumption. It asserted the package's exports are functions while its own comment claimed reference identity. A launcher that imported the package and re-exported a local wrapper would have passed — the exact re-fork the test exists to catch. Now asserted by reference, and proven non-vacuous: substituting a pass-through wrapper makes it fail.
  • The launcher docstring stated a wrong fact. It said any shebang makes a file shell input. Reproduced against the real auditor, only a shebang naming a shell interpreter does; #!/usr/bin/env node does not. The same wrong sentence was in all seven repositories carrying this launcher — propagated by copying the reference file, so the error travelled with the text. Corrected everywhere and now reproduced in the suite rather than asserted.

Thank you to the reviewers on this wave; both of the above came from review, not from the change's author.

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