Skip to content

feat(npm)!: Implement final npm provenance contract per ADR 0103-0112 - #151

Merged
yunseo-kim merged 70 commits into
mainfrom
issue-143-npm-provenance-contract-migration
Nov 12, 2026
Merged

yunseo-kim merged 70 commits into
mainfrom
issue-143-npm-provenance-contract-migration

Conversation

@yunseo-kim

@yunseo-kim yunseo-kim commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Summary

Implements the final npm SLSA provenance contract decided in ADR 0103-0112 (the issue #143 provenance-compatibility follow-up). The branch carries the accepted ADR set, the spec revisions (docs/architecture/), and the trusted-core implementation in SDD order: specification commits land before the implementation commits that realize them.

What changed:

  • Invocation identity (ADR 0103, 0104): externalParameters.workflow records exactly the called workflow's {ref, repository, path}; the legacy environment snapshots leave the signed record. internalParameters.github is a closed triple of event_name, repository_id, repository_owner_id, bound to the OIDC Build Trigger and Build Config URI claims (certificate OIDs .16/.18) and to the signing certificate's platform-fixed source claims. A builder identity migration guard rejects bundles that mix old and new identity shapes.
  • Dependency chain (ADR 0105, 0109, 0110): resolvedDependencies records the ordered chain invocation-source, built-source, package-manifest, lockfile, node-runtime, plus package-manager-distribution for non-npm managers and runner-image, with the governed mediaType matrix. The signed package-manifest descriptor carries offline manifest_evidence (path + base64 content, 16 MiB bound) so verifiers can recompute it without repository access; the selected manifest is snapshotted before any build script runs.
  • Builder record (ADR 0106, 0107): builder.version records the observed {nodejs, npm, corepack?} triple, and build script invocations are recorded as runDetails.metadata.windlass_build_script_disposition.
  • Runner environment (ADR 0111, 0112): The runner-image descriptor's annotation set is exactly deployment_version, operating_system, image_version, runner_label; operating_system comes from the same bounded on-disk metadata snapshot as image_version, and ambient ImageOS is diagnostic-only.
  • Public contract (ADR 0108): The release-tag and provenance-sidecar optional inputs are removed, shrinking the npm reusable workflow contract to one required input (package-directory) and six optional inputs.
  • Diagnostics and fixtures: Seven new closed-registry diagnostic IDs (resolved-dependencies-invocation-source, resolved-dependencies-built-source, resolved-dependencies-package-manifest, package-manifest-evidence, build-script-disposition-mismatch, build-trigger-mismatch, build-config-uri-mismatch); the conformance corpus is regenerated with 47 registrations across 17 requirement IDs, and new fuzz targets (FuzzDecodeInternalParameters, FuzzParseCallerWorkflowPath, FuzzManifestEvidence, FuzzFixtureManifestEvidence, FuzzBindOIDCSignerIdentity) are registered in both the lint and scheduled fuzz workflows.

Why: the issue #143 investigation fixed npm's registry-side provenance handling (npm/cli#9882, shipped in npm 11.20.0/12.1.0); this migration lands the slsa-builder-side contract that pairs with it, completing the ADR 0103-0112 decision set.

How to test: go test ./... (full suite and race), go run ./cmd/slsa-builder-internal fixture-check --index testdata/fixtures/index.json, go run ./cmd/slsa-builder-internal workflow-check --workflow .github/workflows/js-ts-npm-package-slsa3.yml --profile npm-only, plus the eight scenario contracts (happy tag push, dispatch retry, offline manifest evidence, closed-schema regression, runner evidence, builder migration guard, seven-input contract, historical P02 cryptographic verification).

Related Issues

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Test/CI
  • Breaking change
  • Other:

Changelog

  • Category: Changed / Removed (both entries carry Breaking: markers)
  • User-facing note: The npm provenance contract is finalized per ADR 0103-0112 (workflow identity, closed github triple, ordered dependency chain with offline manifest evidence, builder versions, script disposition, runner operating system), and the release-tag / provenance-sidecar workflow inputs are removed.

Changelog update:

  • CHANGELOG.md [Unreleased] updated
  • Not needed because this change is not user-facing

Checklist

General

  • PR title follows Conventional Commits format:
    type(scope): Summary
  • This PR does not expose backend/internal implementation details in a public repo.
  • No secrets, tokens, keys, or private endpoints are included.
  • Changes stay within this repository's intended scope.

CI/Workflow Changes (if applicable)

If this PR modifies GitHub Actions workflows or CI/CD configuration, it must comply with our
Supply Chain Integrity requirements:

  • All uses: references are pinned to full 40-character commit SHAs (with # vX.Y.Z comment)
  • step-security/harden-runner is included as the first step in every job
  • Job-level permissions are used instead of top-level permissions

Protocol / Compatibility Impact

  • No protocol/spec impact
  • Protocol/spec updated
  • Conformance tests updated
  • Breaking change is versioned and migration notes are included

Compatibility impact: this is a breaking change to the signed npm provenance shape and the public workflow input contract, delivered while the project is pre-release (no stable version exists). The specification revisions in docs/architecture/ define the new observable behavior, the ADR set 0103-0112 records the decisions, and CHANGELOG.md [Unreleased] carries the Breaking: entries. Callers pinned by workflow SHA are unaffected until they deliberately re-pin; the builder identity migration guard rejects mixed-shape bundles rather than misclassifying them.

Testing

  • Unit tests added/updated
  • Integration or conformance tests added/updated
  • Tests pass
  • Lint and format pass
  • Type check passes
  • Manual verification performed

Test evidence: go test ./... and go test -race ./... fully green; golangci-lint run ./... reports 0 issues and golangci-lint fmt --diff is clean; fixture-check and workflow-check --profile npm-only pass; actionlint passes; Prettier and markdownlint pass. All five new fuzz targets pass 30-second smokes. The eight migration scenario contracts pass individually (including the historical P02 production signer fixture and the dispatch-retry signed invocation context). cubic review was run per commit and over the full branch diff against origin/main; final result: no issues (seven findings were fixed during the review loop, two were rejected with documented grounds).

Documentation

  • README updated (public input contract wording, both English and Korean)
  • Spec/docs updated (seven docs/architecture/ specifications plus the ADR index)
  • Changelog decision completed above

Rollout / Risk

  • Risk level: Medium
  • Rollback plan: Pre-release contract change with no stable release to break. Callers pin the reusable workflow by commit SHA, so existing pins keep the prior behavior; rollback of this branch is git revert of the implementation commits (spec and ADR commits are decision records and stay). The M1 live dogfood (issue [P06] Live M1 dogfood #30, attempt 5) verifies the migrated contract against the real npm registry before any release.

Reviewer Checklist

  • Scope is clear and minimal
  • Security and boundary checks passed
  • Tests and docs are sufficient
  • Compatibility impact is correctly handled

Summary by cubic

Lands the final npm SLSA provenance contract from ADRs 0103-0112 as the first-release record of the signed provenance shape and public workflow inputs, pairing with the registry-side handling fixed in issue #143.

  • Invocation identity is now externalParameters.workflow {ref, repository, path} plus a closed internalParameters.github triple, both bound to OIDC certificate claims with a migration guard rejecting mixed-shape bundles.
  • resolvedDependencies carries the ordered invocation-source, built-source, package-manifest, lockfile, node-runtime chain, with offline manifest_evidence on the signed package-manifest so verifiers recompute it without repository access.
  • The public contract is one required and six optional inputs; release-tag and provenance-sidecar were never shipped.
  • Runner OS comes from the same on-disk metadata snapshot as image version; ambient ImageOS is diagnostic-only.
  • The source manifest is snapshotted before build and re-checked before install and pack; any byte drift fails closed with source-manifest-drift.
  • Review hardening: corrected the Build Trigger OID constant, enforced positive decimal repository IDs, classified publish-input failures, tightened the canonical runner software URI spelling and rejected whitespace-padded detail values at capture, and reported packed-tarball inspection failures under package-pack-failed, plus fuzz fixes including the .git repository rejection and module-proxy storage allowlist.

Verification impact

  • New OIDC bindings (Build Trigger, Build Config URI) fail closed before registry or release mutation.
  • Existing callers pinned by workflow SHA keep prior behavior; re-pinning is required to adopt the new contract.
  • Conformance corpus regenerated with 47 registrations across 17 requirement IDs, plus six new fuzz targets.

Written for commit cfda119. Summary will update on new commits.

View guided diff Turn on auto-fix

Redefine externalParameters.workflow as the common invocation-interface
identity for every SLSA producer profile. Preserve the built-source and
SHA-pinned builder identity models, and partially supersede only ADR
0080's omitted-source-ref byte-for-byte compatibility promise.

Require migrations to retain equivalent builder-identity verification
against runDetails.builder.id and the authenticated signer identity.
The former external-parameter builder record's final placement remains
a focused follow-up decision.

Related #30, #143

Signed-off-by: Yunseo Kim <[email protected]>
Add reciprocal see-also relations from ADRs 0028, 0029, and 0079 to
ADR 0103.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Update the ADR sequence, topic tables, and accepted traceability
mappings in the agent and bilingual decision indexes.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
End the Context and Problem Statement sections of ADRs 0102 and 0103
with explicit decision questions, matching the established MADR
convention.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Accept ADR 0104 to remove the duplicate builder path, SHA, and URI
representation while retaining canonical builder identity,
authenticated signer identity, and trusted-policy verification. Add
the reciprocal see-also relations to the predecessor ADRs.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Update the ADR sequence, topic tables, and accepted traceability
mappings in the agent and bilingual decision indexes.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
…dependencies

Accept ADR 0105, adding mandatory role-named invocation-source and
built-source descriptors with gitCommit digests to the npm profile's
closed resolvedDependencies set. The invocation-source descriptor
satisfies the npm registry's observed first-entry comparisons and is
serialized at index 0 for emission compatibility, while semantic
verification remains unordered and name-keyed. Existing
externalParameters.source fields are retained under equality
invariants until the follow-up consolidation decision.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Add the reverse see-also relations from ADRs 0079, 0080, 0103, and
0104 to ADR 0105, keeping the relations graph symmetric per ADR 0065.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Update the ADR sequence, structure listing, topic tables, and
accepted traceability mappings in the agent and bilingual decision
indexes.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
…escriptor bindings

Accept ADR 0106, removing derived and resolution-output members from
externalParameters.source while retaining the caller's explicit
full-ref selector as optional source.ref. Release policy bindings
move to workflow.repository and the built-source descriptor, and
certificate invocation comparisons move to workflow.ref and the
invocation-source descriptor with no fallback. The built-source
descriptor gains a required annotations.ref_type member under ADR
0105's extension path, and the coexistence invariants that referenced
the removed fields are retired.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Add the reverse relations from ADRs 0079, 0080, 0103, 0104, and 0105
to ADR 0106, including the partially-superseded-by entries for ADR
0080's field locations and ADR 0105's retired coexistence invariants
with the built-source annotation extension, keeping the relations
graph symmetric per ADR 0065.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Update the ADR sequence, structure listing, topic tables, and
accepted traceability mappings in the agent and bilingual decision
indexes.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
…hub only

Accept ADR 0107, making internalParameters.github.event_name the sole
statement field for the invocation event and removing
source.event_name. The source group is now emitted only when the
caller supplied an explicit source-ref selector, so group presence
itself distinguishes explicit selection from defaulting. The
supported-event admission policy and the certificate Build Trigger
comparison are preserved at the new location, with actor fields still
excluded.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Add the reverse see-also relations from ADRs 0026, 0079, 0103, and
0106 to ADR 0107, keeping the relations graph symmetric per ADR 0065.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Update the ADR sequence, structure listing, topic tables, and
accepted traceability mappings in the agent and bilingual decision
indexes.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Signed-off-by: Yunseo Kim <[email protected]>
Signed-off-by: Yunseo Kim <[email protected]>
Signed-off-by: Yunseo Kim <[email protected]>
Revise the common SLSA provenance specification and the identity and
build types specification to the contract decided by ADRs 0103-0112.

The common specification now defines externalParameters.workflow as the
required caller invocation identity {ref, repository, path}, the closed
internalParameters.github triple {event_name, repository_id,
repository_owner_id}, the role-named invocation-source and built-source
descriptors, the package-manifest descriptor with its offline evidence
contract, the deterministic mediaType matrix, the runner-image reported
identity with the on-disk operating_system annotation, the
builder.version {nodejs, npm, corepack?} closed key set, and the npm
windlass_build_script_disposition run metadata. The claim-source and
rejection matrices bind certificate claims to the workflow group and
the descriptors with no source.invocation_* fallback.

The identity specification retains canonical runDetails.builder.id as
the sole builder identity, removes the redundant external-parameter
builder record, and adds the npm profile-path migration guard that
re-proves the exact builder path restriction through canonical builder
validation, same-certificate signer binding, and trusted policy
authorization.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Revise the npm profile and composition specifications to the contract
decided by ADRs 0103-0112.

The package profile contract now exposes exactly seven public inputs
and rejects the removed release-tag and provenance-sidecar inputs, with
the release target derived from the effective built tag and sidecar
publication mandatory in release-asset mode. The provenance and publish
specification defines the minimized externalParameters schema, the
invocation-source and built-source descriptors with the npm emission
order, the package-manifest descriptor, the manifest_evidence offline
evidence contract, the deterministic mediaType matrix, the runner-image
operating_system annotation, the effective built ref derivation rule,
and the re-pointed certificate and policy bindings.

The handoff and publisher specifications carry manifest_evidence to the
publisher for offline manifest-dependent checks while preserving the
internal release-tag contract and the mandatory sidecar distribution.
The core profile contract and build/pack specification record the
revised shared source model, the original-manifest snapshot captured
before caller scripts, and the on-disk runner OS capture. The release
manifest specification required no changes: its trusted producer
mapping is distinct from the removed external-parameter builder record.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Rebind the verification policy to the ADR 0103-0112 statement
contract: certificate source claims bind to the workflow invocation
group and the invocation-source descriptor with no source.invocation_*
fallback, Build Trigger and Build Config URI bind to
internalParameters.github.event_name and workflow.path, release
expectations bind to the effective built ref and the built-source
descriptor, and manifest-dependent checks run against the offline
manifest-evidence contract. The npm package verification policy, the
descriptor cardinalities and mediaType matrix, the runner-image
annotation set, the builder.version closed key set, and the signed
windlass_build_script_disposition metadata are specified together with
their rejection families, while the demoted pre-existing package flags
move to unsigned diagnostic metadata.

Register the seven new diagnostic categories
(resolved-dependencies-invocation-source, resolved-dependencies-
built-source, resolved-dependencies-package-manifest,
package-manifest-evidence, build-script-disposition-mismatch,
build-trigger-mismatch, build-config-uri-mismatch) as declarations with
their specification tables in lockstep; no runtime emission is added
here. Update the architecture index traceability through ADR 0112 in
both languages, the repository README input count to one required and
six optional inputs, and the workflow interface note to 7 inputs.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Capture the selected package.json bytes exactly once after verified
checkout and package selection, before any caller-controlled install,
build, or pack script, and thread the snapshot through package-manager
selection, build-script detection and disposition, and publish-intent
resolution. Workspace discovery reuses the captured manifest for the
selected directory instead of re-reading it.

This prepares the ADR 0109 manifest-evidence contract: the original
selected source-manifest bytes, never a build-mutated or packed
manifest, become the single evidence source that the later
package-manifest descriptor and manifest_evidence handoff bind to.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Remove the public release-tag and provenance-sidecar inputs from the
JS/TS npm reusable workflow per ADR 0108. The workflow now declares
exactly seven inputs, the release target derives from the effective
built tag, and sidecar publication remains mandatory in release-asset
mode. workflow-check rejects the removed names as unknown inputs, and
the reserved-mode rejection for release-asset-mode and
linked-artifact-metadata is unchanged.

The internal handoff release tag and the standalone publisher's
release-tag contract are preserved.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
The original source-manifest snapshot is the signed evidence, but
install, build, and pack ran against the mutable on-disk manifest, so a
post-selection rewrite (external edit or a caller-controlled install or
build script) could produce an artifact the signed evidence does not
cover. The profile now re-reads the selected package.json immediately
before install and before pack and fails closed with the new registered
windlass.verify.error.source-manifest-drift diagnostic (policy phase,
exit 1, no mutation) on any byte drift. The pack window stays outside
the byte boundary by design: managers legitimately rewrite fields at
pack time (workspace protocol resolution, publishConfig overrides) and
caller-declared prepack scripts run inside pack, so the packed manifest
is never evidence and the artifact stays bounded by packed identity,
consumer-surface validation, and the tarball digests. The build-pack
specification records the guard and the boundary.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
ADR 0035 and ADR 0055 carry status "superseded by ADR-0077" but still
sat in the accepted traceability table. Move both rows into the
superseded/deprecated table so the index agrees with their frontmatter.

Signed-off-by: Yunseo Kim <[email protected]>
The new identity and source binding diagnostics inherited the default
level 7, so a co-occurring package or handoff error could become
primary_id ahead of a higher-priority identity failure. The
verification policy's precedence contract places issuer, workflow,
source numeric identity, source content, run invocation, and runner
identity at level 4; register build-trigger-mismatch,
build-config-uri-mismatch, resolved-dependencies-invocation-source, and
resolved-dependencies-built-source there, with ordering tests against a
level-6 control.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Missing --metadata-* flags are invocation errors at the dispatcher
(exit 2), not rejection-report verification failures. Mismatches
between caller arguments or tarball handoff digests and the
authenticated build metadata now persist a rejection report and exit 1:
the argument comparison reports package-manifest-evidence, the
metadata/handoff disagreement reports digest-mismatch, and a
VerifyPackedArtifact failure keeps its own diagnostic ID through
writePublishInputRejection instead of being dropped to a bare exit 2.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
validateStatement's source-repository, subject-cardinality, and
Corepack-version rejections were plain errors, so the offline
verification command reported them as invocation failures (exit 2)
instead of verification failures (exit 1). They now carry
source-identity-mismatch, subject-cardinality-error, and
builder-version-mismatch respectively, so the command's
diagnostic-bearing path classifies them correctly. The remaining ID-less
path is the nil-verifier programmer error, which is genuinely an
invocation failure.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
DecodeInternalParameters accepted non-numeric, zero, leading-zero,
signed, and whitespace-padded repository and owner IDs, so a malformed
internalParameters.github record could be signed or accepted. Both
identifier fields now require GitHub's positive base-10 form
(^[1-9][0-9]*$), and the common provenance contract states the grammar.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
A missing build_script_disposition carried the
build-script-disposition-mismatch diagnostic while an explicit null
fell through to a plain non-null decode error. Both now use the
diagnostic, and the generic non-null loop no longer rechecks the
member.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
The Included Software URI is signed verbatim, so percent-encoded or
otherwise non-canonical forms are no longer interchangeable with the
plain path (RawPath must be empty, alongside the existing scheme, host,
userinfo, query, and fragment rules). The captured Image Release
reference is now validated as the platform's release URL shape with its
percent-encoded tag, and its tag must equal the offline image_version
projection, matching the consumer-side runner-image rule. The new
FuzzParseRunnerMetadata target covers the trust-boundary parser per the
fuzz policy and is registered in both the lint smoke matrix and the
scheduled deep fuzz matrix.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
The unconditional InvocationRef fallback to the built ref made the
source-ref completeness check unreachable for the ref component: a
source-ref run missing the invocation ref silently recorded the built
tag as the invocation identity. Both the command adapter and the
metadata finalizer now apply the fallback only when no source-ref is
set, so incomplete source-ref invocations fail closed. Production is
unchanged: the workflow always passes --invocation-ref from github.ref.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
ResolveStatementSources and validateFinalDependencies each reimplemented
the invocation-source/built-source validation, and their
omitted-selector messages had already drifted. Both now call one
validateSourceDependencies helper so the rule cannot diverge again.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
The producer and the evidence consumer each tested scripts.build
presence; both agreed on the non-empty rule but nothing kept them from
drifting. One manifestDeclaresBuildScript helper now defines declared as
a non-empty scripts.build command, and the publish specification states
that an explicit empty string counts as not declared.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
A builder ID with a valid immutable reference but the wrong workflow
path carried builder-id-not-immutable, which the verification policy
reserves for mutable references; the untrusted-identity case is
wrong-builder-id. ValidateBuilderIDForWorkflow now returns
wrong-builder-id for path mismatches, and the previously uncovered
function gains happy-path, wrong-path, mutable-ref, malformed, and
untrusted-repository tests.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
BuildPackConfig.RunnerOverride is a cross-package test seam (the command
adapter uses it), so it stays exported, but it is now documented as
test-only and a supplied capture must satisfy the same identity
invariants as a parsed one. The parser's derived-identity checks moved
into one validateRunnerCapture shared by both sources, so the override
path cannot drift from the on-disk contract.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
The common contract's examples showed builder.version nodejs v24.0.0 and
npm 11.0.0 while the same documents pin the runtime at Node.js 24.22.1
with bundled npm 11.19.1. Align all four example pairs with the pinned
pair so readers copy a consistent shape.

Signed-off-by: Yunseo Kim <[email protected]>
The offline verification command derived the Build Config URI
expectation from the signed statement unconditionally, silently
discarding a consumer-supplied pin; a conflicting non-empty pin now
fails with build-config-uri-mismatch before derivation. The same
verification path also reported npm validation failures under a generic
"npm-evidence" check label; the report now keeps the failing field path
from the error's DiagnosticCheck.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
ParseCallerWorkflowPath returned plain errors, so a malformed or
non-canonical workflow_ref reached the caller without a diagnostic ID.
The npm trusted-publisher contract requires
windlass.verify.error.trusted-publisher-mismatch for an unavailable or
conflicting caller workflow filename, so the parser now carries it.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Add real negative tests for the publish-side producer evidence
contract: manifest digest/identity/privacy/repository/disposition
rejections through VerifyProducerPackage, packed-artifact digest and
truncation failures through VerifyPackedArtifact, and the binding
checks (packed identity, release event, push invocation ref, runtime
pin, built and invocation commits) through NewProvenanceSigningInput.
Each case mutates one trust-boundary operand and asserts the registered
diagnostic. VerifyPackedArtifact is now fully covered and
validateProducerBindings rises to 89%.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Exercise the closed external-parameters boundary: wrong member types,
an empty source selector, absolute and traversing package directories,
the unimplemented release-asset mode flag, and a whitespace dist-tag
that is not an omission. Both validateFinalExternalShape and
validateFinalExternalParameters are now fully covered.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Extend the conformance harness evidence binding tests with empty
content, traversing package directories, duplicate dependency names, a
built-source descriptor without its commit, and malformed bundle
documents, each asserting the fail-closed package-manifest-evidence
diagnostic.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>

@cubic-dev-ai cubic-dev-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.

All reported issues were addressed across 64 files (changes from recent commits).

Requires human review: Auto-approval blocked because this review re-detected 2 unresolved issues already reported by Cubic.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread internal/command/npm_profile_publish.go Resolved
Comment thread internal/npmprofile/runner_capture.go Resolved
Comment thread docs/architecture/slsa-provenance-v1.md Resolved
Restore the missing space in the slsa-provenance-v1 fail-closed
sentence ("registry or release mutation"), add the JS/TS npm package
profile to the ADR 0090 row of the decisions index so the ADR-to-spec
mapping stays bidirectional, and narrow the source-manifest-drift doc
comment to the on-disk bytes the guard actually checks.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
Tighten the runner image metadata boundary in three ways, matching the
descriptor-validation grammar that consumers already enforce. The
Included Software URI now rejects any raw query or fragment spelling,
including trailing ? or # delimiters that url.Parse would otherwise
drop from the reconstructed form. Capture also requires the URI's
release-tag component to agree with the full image version's offline
projection, the same rule descriptor validation applies. Detail lines
with surrounding whitespace, including whitespace-only lines, are
rejected as malformed instead of being silently trimmed into the
signed record. The build-pack specification pins the canonical
spelling and padding rules alongside the capture requirements.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
…k-failed

VerifyPackedArtifact returned inspectTarball's errors unwrapped, so a
corrupt or truncated tarball fell back to the handoff-schema-mismatch
primary ID in the publish report even though the errors already carry
the package-pack-failed prefix. Wrap them in the registered diagnostic
so the report names the real failure. The publish command tests now
cover a truncated tarball end to end, and the former packed-artifact
test is renamed to the handoff-versus-metadata disagreement it
actually exercises; packed-artifact digest verification itself remains
covered directly in the npmprofile unit tests.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>

@cubic-dev-ai cubic-dev-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.

All reported issues were addressed across 8 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread internal/npmprofile/runner_capture.go Resolved
Extend the capture boundary so each Runner Image detail value must
equal its trimmed form: an `Image:  ubuntu-24.04` style value with
interior prefix padding no longer flows into the signed runner_label
annotation. The build-pack specification now also states explicitly
that capture-time grammar or projection failures exit 2 as malformed
local evidence while the same failure in an already-built descriptor
exits 1 from descriptor validation.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>
The project has never been released, so the [Unreleased] section may
not carry Changed or Removed categories, which presuppose previously
released behavior. Fold the contract finalization into the Added
section as the record the first release ships, revise the runner-image
entry in place for the final ADR 0111/0112 annotation set, and drop
the release-tag/provenance-sidecar removal entry: those inputs never
shipped, so their absence is invisible to every user.

Related #143

Signed-off-by: Yunseo Kim <[email protected]>

@cubic-dev-ai cubic-dev-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.

2 issues found across 1 file (changes from recent commits).

Confidence score: 5/5

  • In CHANGELOG.md, the signed package-manifest descriptor is described as carrying manifest_evidence, but that evidence is handed off separately in build metadata. Update the description so consumers are not misled about the signed format.
  • In CHANGELOG.md, externalParameters.workflow is described as the called workflow, but it records the caller workflow. Replace “called workflow” with “caller workflow.”
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="CHANGELOG.md">

<violation number="1" location="CHANGELOG.md:137">
P3: `externalParameters.workflow` records the caller workflow, not the reusable workflow being called. Replace “called workflow” with “caller workflow” to describe the signed identity accurately.</violation>

<violation number="2" location="CHANGELOG.md:143">
P3: The signed `package-manifest` descriptor does not carry `manifest_evidence`; the build-metadata handoff carries it separately. Update this description so consumers are not misled about the signed format.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread CHANGELOG.md
signing certificate's platform-fixed claims. `resolvedDependencies` records the ordered chain of
`invocation-source`, `built-source`, `package-manifest`, `lockfile`, `node-runtime`, a
`package-manager-distribution` for non-npm managers, and `runner-image`; the signed
package-manifest descriptor carries offline `manifest_evidence` (`path` plus base64 content) so

@cubic-dev-ai cubic-dev-ai Bot Nov 12, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The signed package-manifest descriptor does not carry manifest_evidence; the build-metadata handoff carries it separately. Update this description so consumers are not misled about the signed format.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At CHANGELOG.md, line 143:

<comment>The signed `package-manifest` descriptor does not carry `manifest_evidence`; the build-metadata handoff carries it separately. Update this description so consumers are not misled about the signed format.</comment>

<file context>
@@ -123,12 +123,27 @@ Era five-digit years (e.g., `## [0.1.0] - 12026-06-13`).
+  signing certificate's platform-fixed claims. `resolvedDependencies` records the ordered chain of
+  `invocation-source`, `built-source`, `package-manifest`, `lockfile`, `node-runtime`, a
+  `package-manager-distribution` for non-npm managers, and `runner-image`; the signed
+  package-manifest descriptor carries offline `manifest_evidence` (`path` plus base64 content) so
+  verifiers can recompute it without repository access. `builder.version` records the observed
+  `nodejs`, `npm`, and conditionally `corepack` versions, and build script invocations are recorded
</file context>
Fix with cubic

Comment thread CHANGELOG.md
rollout windows do not fail honest builds. The ambient `ImageOS` variable is diagnostic-only and
never feeds the signed record.
- Added the finalized JS/TS npm SLSA provenance contract (ADR 0103-0112).
`externalParameters.workflow` records exactly the called workflow's `ref`, `repository`, and

@cubic-dev-ai cubic-dev-ai Bot Nov 12, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: externalParameters.workflow records the caller workflow, not the reusable workflow being called. Replace “called workflow” with “caller workflow” to describe the signed identity accurately.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At CHANGELOG.md, line 137:

<comment>`externalParameters.workflow` records the caller workflow, not the reusable workflow being called. Replace “called workflow” with “caller workflow” to describe the signed identity accurately.</comment>

<file context>
@@ -123,12 +123,27 @@ Era five-digit years (e.g., `## [0.1.0] - 12026-06-13`).
+  rollout windows do not fail honest builds. The ambient `ImageOS` variable is diagnostic-only and
+  never feeds the signed record.
+- Added the finalized JS/TS npm SLSA provenance contract (ADR 0103-0112).
+  `externalParameters.workflow` records exactly the called workflow's `ref`, `repository`, and
+  `path`, and `internalParameters.github` is a closed triple of `event_name`, `repository_id`, and
+  `repository_owner_id`, bound to the OIDC Build Trigger and Build Config URI claims and to the
</file context>
Suggested change
`externalParameters.workflow` records exactly the called workflow's `ref`, `repository`, and
+ `externalParameters.workflow` records exactly the caller workflow's `ref`, `repository`, and
Fix with cubic

@yunseo-kim
yunseo-kim merged commit 1e1b268 into main Nov 12, 2026
64 checks passed
@yunseo-kim
yunseo-kim deleted the issue-143-npm-provenance-contract-migration branch November 12, 2026 22:19
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.

2 participants