Repository navigation
v0.14.0
Track T Phase 4 (T-E1): trust conditions you prohibit now fail CI deterministically, with no model involved, because advisory answers to an agent are not a security boundary.
- Trust policy:
.openpulse/policy.ymlprohibits trust conditions, read bycheck,attestand the GitHub Action. It is backed by measured false-positive rates: lookalike names fire on 18.8% of real popular npm names, so indicators must name the packages they apply to. - The Action on pull requests: it checks trust for the dependencies a pull request adds or changes, and uses the base branch's policy so a pull request cannot weaken its own gate.
- Exports: trust results appear in SARIF, the job summary and DefectDojo, with levels set by your policy, never by a risk score.
- Bypass tests: they found and closed two pull-request bypasses, and document the routes the gate cannot cover.
Nothing new blocks without a policy, and verdicts and the frozen evaluation are unchanged. One upgrade note: dependency_status drops the v0.12 top-level trust fields, which were deprecated in v0.13.0; read trust.checks.
Upgrade notes
dependency_statusno longer repeatsregistry_existence,provenanceandlookalikeat the top level (v0.14.0 item Y6). They were deprecated in v0.13.0. Read them fromtrust.checks, where they have been since v0.13.0 with the same values. Thedeprecatedfield is gone too. Nothing else in the answer changes, and verdicts are unchanged.
Security
- A lockfile hash edited in place no longer passes the pull-request gate (v0.14.0 item Y5). With
--base-lockfile(the Action'strust: pull-request), the digest check ran only for entries with a new name or version. A pull request that kept an entry's name and version but changed its hash, swapping the artifact, was never checked. Digests are now checked for every entry whose locked digests differ from the base. Found by the new bypass tests. - A pull request can no longer weaken its own trust gate in the Action (v0.14.0 item Y5). With
trust: pull-request, the action used the pull request's own.openpulse/policy.ymland.openpulse/private-packages.txt, so a pull request could delete its prohibitions or list a malicious name as private. The action now uses the base branch's copies when they exist. A policy the pull request adds is still used, because there was nothing to weaken. If the base copies can't be read, the action warns and uses the pull request's.
Added
-
Gate bypass tests (track T evaluation category 8, v0.14.0 item Y5).
tests/test_gate_bypass.pypins each route around the trust gate.docs/TRUST_POLICY.md, "Bypass routes", lists each route as covered or not, with what to do instead.- Covered: lockfile-only additions, hashes edited in place, respelled blocklisted names, an agent ignoring the MCP warning, and a pull request weakening its own policy.
- Not covered: removing the workflow step (use a required status check), unpinned entries, version-pinned blocklist entries, sources that don't answer, installs outside the gated files, and internal names missing from the private-name list.
-
Trust records in SARIF, the job summary and DefectDojo (track T, T-E1, v0.14.0 item Y4). This answers an open question from the Phase 0 trust design: how T-F1 statuses map onto these outputs without implying a risk score.
-
Where they appear:
attest --sarifand--defectdojo(and the GitHub Action) report trust records from contract 2.2.0trust_assurance, plus trust policy violations, as their own results underopenpulse/trust/<check>. Each appears once per file, dependency, check and status, with the https references it cites. -
Levels follow your policy, not a score:
- prohibited by your policy (including the blocklist): SARIF
error, DefectDojo High; - otherwise CONFIRMED_MALICIOUS or VERIFIED_FAILURE:
warning, Low; - otherwise SUSPICIOUS_INCONCLUSIVE:
note, Info; - other statuses are omitted unless prohibited.
No
security-severityis emitted. - prohibited by your policy (including the blocklist): SARIF
-
Job summary: a separate Trust checks table.
-
Consistency: SARIF and DefectDojo share one item list and one identity per record. Verdict results are unchanged (tested).
-
-
The GitHub Action checks trust on pull requests (track T, T-E1, v0.14.0 item Y3). New action inputs, both off by default so existing workflows stay offline and unchanged:
trust:off,pull-requestorall. Withpull-request, the action reads the base branch's copy ofmanifestandlockfilethrough the GitHub contents API withgithub-token. The token is in the environment of that one step only. Trust checks then run only for the dependencies the pull request adds or changes.- New files: a file the pull request adds counts entirely as added.
- Unreadable base: if a base copy can't be read, every dependency is checked; a failure never narrows the checks.
policy: a trust policy file (default.openpulse/policy.ymlwhen present). A violation fails the step even withfail-on: none.- CLI:
openpulse attest --base-manifest PATH --base-lockfile PATHdoes the same from the command line. Trust checks and the policy's trust conditions apply to added or changed dependencies, and the blocklist to all of them. - CI: the action job now dogfoods a blocklist policy that must fail the step.
-
Trust policy: prohibited conditions fail the run (track T, T-E1, v0.14.0 item Y2).
.openpulse/policy.yml(or--policy PATH) is read byopenpulse checkandopenpulse attestand evaluated without any model. Seedocs/TRUST_POLICY.md.prohibit: for any package,confirmed_malicious,not_found_in_registry,digest_mismatch,provenance_failureandrepository_mismatch. For named packages only,missing_provenance,lookalike_name,public_private_nameandunconfirmed_repository. A policy that names an indicator without packages is refused, because the Y1 measurements show lookalike fires on 18.8% of real popular npm names. Conditions with no organic false-positive set warn that they are experimental.blocklist:ECOSYSTEM:NAME[==VERSION]patterns; no lookups needed.- What never blocks: silence.
NOT_PERFORMEDandMISSING_EVIDENCEnever fail a run unless a scoped condition says so. - When it fails: a violation exits 1 after every output is written, alongside
--fail-on. With no policy, gates are unchanged.
-
Track T measurements (v0.14.0 item Y1).
python benchmarks/trust.pymeasures every trust check offline and writesbenchmarks/results/trust.jsonanddocs/research/TRACK_T_MEASUREMENTS.md. The owner accepted these measurements as the false-positive prerequisite for policy gates (T-E1, decision 11). They are not the registered study.- Synthetic cases: 33 cases with 37 labelled expectations, from the roadmap's evaluation plan (existence, lookalike, dependency confusion, provenance and repository linkage, digest integrity, known-malicious records). They run against a stub world in the reserved
opfx-namespace. Every case gets its expected status, with no false positives on the synthetic negatives; that shows the checks do what they claim, not how often real attacks are caught. - Degraded sources: with every source unreachable, or the budget spent, no live check claims anything (0 of 284 records).
- Untrusted text: hostile names are refused before any request and never echoed. An instruction in an OSV summary never reaches a record.
- Organic control (the informative number): leave-one-out lookalike over 1000 real popular npm names. 18.8% look like another popular name; 9.7% without the
scoperule, which fires on legitimate@types/<name>-style packages. So lookalike stays an indicator: a policy should prohibit it only for a named scope. - Labels per policy condition: each condition a policy can prohibit is labelled measured, measured; indicator or experimental (no organic set yet: dependency confusion, unconfirmed repository linkage).
- Tests run a quick sample and check that the committed report matches the committed results.
- Synthetic cases: 33 cases with 37 labelled expectations, from the roadmap's evaluation plan (existence, lookalike, dependency confusion, provenance and repository linkage, digest integrity, known-malicious records). They run against a stub world in the reserved