By default Scharf checks whether workflow references use full commit SHAs and
resolves mutable refs for pinning. Immutability alone does not verify upstream
provenance. Add --verify-provenance when current upstream evidence is required:
scharf audit . --verify-provenance --out json
scharf audit . --verify-provenance --out sarif --output provenance.sarif
scharf autofix . --verify-provenance --dry-run
scharf upgrade actions/checkout@v4 --verify-provenance
scharf upgrade-all-sha . --verify-provenance --dry-run
This mode checks references directly present in the scanned workflow files. Recursive/transitive dependency provenance is not included; combining this mode with a dependency-graph audit requires a separate integration and is unsupported.
Verification uses fresh GitHub API responses, optionally authenticated with
GITHUB_TOKEN. It never downloads or executes action payloads. A commit must be
reachable from a current branch in the intended upstream repository. Evidence
records the canonical repository name, stable numeric repository ID, check time,
SHA and exact supporting branch tip. Tags are resolved through annotated tag
objects when needed; tags alone are not the membership-proof set.
Before accepting evidence, Scharf rechecks the requested repository path and any
distinct canonical path against the starting identity. A changed ID blocks the
result; an unavailable recheck or a concurrent rename requires a fresh run.
Existing full-SHA pins are checked too. A fork-only commit being addressable
through /repos/owner/repo/commits/<sha> is not sufficient proof. Conversely,
removed branches, inaccessible history, rate limits and offline operation can
leave legitimate commits unverified. “Verified” does not mean benign code.
The first successful check records a historical observation in
~/.scharf/provenance.json. It is a trust-on-first-use record, not a signed
identity authority and not a verification cache. All later checks contact
GitHub again. A renamed repository retaining the same ID can be verified; a
replacement with a different ID requires review, including when requesting a
new version. Expected forward movement of a branch or major tag such as v4 is
reported as moved-reference with requires_review: false. A moved patch tag,
non-forward change, tag/branch namespace change, or uncheckable prior ancestry requires review.
Autofix and upgrades refuse unknown or review-required evidence before writing
workflow changes. They also recheck the complete scanned workflow file set and
contents after verification; concurrent edits, added files or removed files
require a fresh run before any planned writes begin. Each edited file is checked
again before writing. Dry runs enforce the same checks. These checks do not lock
the working tree or make writes across multiple files transactional: avoid
concurrent editors, and inspect the working tree after a filesystem write error.
Audit includes the evidence
and reports unsafe provenance as a violation; use --raise-error to make audit
policy violations fail CI.
To review a suspicious change, inspect the upstream repository identity, old/new
commit histories and maintainer release information independently. Scharf does
not automatically accept the change. If you deliberately choose a new baseline,
back up the observation file and manually remove only the relevant reviewed
observation(s), then rerun verification. For an intentional repository identity
replacement, all observations binding that path to the prior repository ID must
be reviewed. Deleting the entire file discards identity and moved-reference
history for every repository and should not be a routine workaround. A leftover
.lock directory should be removed only after confirming no Scharf invocation is
writing the file.
Each verification is bounded to 128 API calls, five branch-list pages of 100, 2 MiB per response, eight annotated-tag objects, 15 seconds per request and 45 seconds overall. Only same-host HTTPS GitHub API redirects are followed, at most three. Upgrade tag listing is limited to five pages and fails if incomplete. An exact positive branch proof can be returned before enumeration finishes; exhausted limits without proof return unverified. Tag-only release histories, deleted branches and history rewrites can therefore need manual investigation. No cached or stale evidence is promoted to a current result. GitHub’s separate identity, ref and branch responses are not an atomic snapshot. The final identity checks detect lasting path replacement, but cannot detect a path that changes to another repository and back between those checks.