Audit failed at 2026-07-20T07:35Z. Run
Security Audit Report
Audited against SECURITY.md as of commit 5b42136 (2026-07-20). $AUDIT_PAT (fine-grained,
read-only Administration + Secrets + Environments) was available and used for all admin-scoped
checks below.
FAIL IF results
Dependency Supply Chain
- PASS — Ran
node website/scripts/generate-deps.js from a clean checkout (git status was
clean before and after). git diff --stat against website/src/data/dependencies-{npm,cargo,runtime}.json
produced no output — no drift.
- PASS —
package.json:6-12 has devEngines.runtime.version: "24.18.0" — exact MAJOR.MINOR.PATCH.
- PASS —
standalone/src-tauri/build.rs:42-43,149-227 (read_pinned_node_version +
verify_node_version) reads devEngines.runtime.version and fails the build (Err(...)) if the
bundled binary's node --version doesn't match.
- PASS —
.github/workflows/release.yml:39-41 (build-standalone job):
actions/setup-node@... with node-version-file: package.json, which resolves
devEngines.runtime.version.
- PASS —
pnpm-workspace.yaml:23 has minimumReleaseAge: 1440.
- PASS —
.github/renovate.json:3 — enabledManagers: ["github-actions", "npm", "cargo"].
- PASS —
.github/renovate.json:68-82 — minimumReleaseAge package rules for npm/cargo at
patch (1 day), minor (3 days), major (14 days).
GitHub Actions Policies
- PASS —
grep -rn pull_request_target .github/workflows matched only
.github/workflows/tend-review.yaml:11.
- PASS — Non-agent-managed workflows (
ci.yml, chromatic.yml, release.yml) checked:
ci.yml:8-9, chromatic.yml:13-14: contents: read only, no writes.
release.yml build-standalone/build-vscode jobs (lines 14-17, 162-165):
contents: read, id-token: write, attestations: write — exactly the allowed release
provenance set.
release.yml security-audit job (lines 230-239): contents: read, actions: write — the
explicitly documented exception.
release.yml publish-vscode job: no job-level permissions: block, inherits the workflow's
top-level contents: read (no write).
Automated Maintainer (tend)
- PASS — Ruleset
Merge access (id 16757376, GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/rulesets/16757376): target: branch, conditions.ref_name.include: ["~DEFAULT_BRANCH"], rules: [{"type":"update"}], bypass_actors: [{"actor_id":5, "actor_type":"RepositoryRole","bypass_mode":"exempt"}] — sole bypass actor is admin (role id 5).
- PASS — Ruleset
Tag operations (id 16757382): target: tag, conditions.ref_name.include: ["~ALL"], rules: [{"type":"creation"},{"type":"update"}], same sole admin-only bypass actor.
- PASS —
GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/collaborators/dormouse-bot/permission
→ {"permission":"write","role_name":"write"} — exactly push/write, not higher.
- PASS —
GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/actions/secrets → repo-level
secrets are CHROMATIC_PROJECT_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, TEND_BOT_TOKEN only. Neither
OVSX_PAT nor VSCE_PAT present at repo level.
- PASS — Environments (
gh api repos/diffplug/dormouse/environments): only security-audit
and vscode-extension-publish exist.
security-audit deployment-branch-policies: main (branch) + v* (tag) — both admin-gated
(Merge access / Tag operations).
vscode-extension-publish deployment-branch-policies: v* (tag) only — admin-gated
(Tag operations).
- PASS —
AUDIT_PAT is present in the security-audit environment secrets
(gh api .../environments/security-audit/secrets → {"total_count":1,"secrets":[{"name":"AUDIT_PAT",...}]})
and absent from the repo-level secret list (check 13's output).
- PASS —
.config/tend.yaml:4-5 — secrets.allowed: [CHROMATIC_PROJECT_TOKEN].
- PASS —
.github/workflows/workflow-audit.yaml exists, is not disabled (on: schedule +
workflow_dispatch), and its most recent run
(gh api repos/diffplug/dormouse/actions/workflows/workflow-audit.yaml/runs) completed
success at 2026-07-19T09:13:32Z, ~22h before this audit (date -u → 2026-07-20T07:32:25Z),
well inside 48h.
- PASS — All
tend-*.yaml action refs are tag-pinned (actions/checkout@v7,
max-sixty/tend/claude@0.1.11) — none use @main or an unpinned ref. Tag pins are explicitly
accepted in this file class.
- FAIL —
.github/workflows/tend-mention.yaml's verify job (lines 34-205) declares no
permissions: block (only the handle job, line 213, has one; grep confirms no top-level
permissions: key exists in the file either). The repo's default Actions token permission is
write (GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/actions/permissions/workflow →
{"default_workflow_permissions":"write","can_approve_pull_request_reviews":true}). That default
grants the job's ambient GITHUB_TOKEN write access to every scope (contents, issues,
pull-requests, actions, checks, deployments, packages, security-events, statuses, …), which
exceeds the allowed set (contents: write, pull-requests: write, issues: write,
id-token: write, actions: read, or any read). See Qualitative findings for exploitability
discussion.
VS Code Extension Releases
- PASS —
release.yml:294-296 — publish-vscode job has environment: {name: vscode-extension-publish}.
- PASS —
grep -rn 'VSCE_PAT|OVSX_PAT' .github/workflows matched only
release.yml:325,337, both inside the publish-vscode job (the only job with the
vscode-extension-publish environment).
- PASS — No production desktop signing secret names (
APPLE_SIGN_PASS, EV_SIGN_PIN,
TAURI_SIGNING_PRIVATE_KEY from a repo/org secret) appear in release.yml; grep -n 'secrets\.' release.yml returns only GITHUB_TOKEN, VSCE_PAT, OVSX_PAT.
- PASS —
release.yml:63-74 ("Generate ephemeral Tauri updater key") runs tauri signer generate --ci --write-keys ... --force for every CI build; no persistent/production updater key
is injected.
Desktop Releases
- PASS —
scripts/sign-and-deploy.sh:392-399 (verify_downloaded_artifact) calls gh attestation verify with --cert-identity/--cert-oidc-issuer/--source-ref/--source-digest
before any signing proceeds.
- PASS —
scripts/sign-and-deploy.sh:401-403 calls check_sha256_manifest (sha256sum -c /
shasum -a 256 -c) after attestation verification.
- PASS —
scripts/sign-and-deploy.sh:683-718 (sign_windows) uses jsign --storetype PIV.
CI Validation Contract
- PASS —
.github/workflows/security-audit.yaml exists, is enabled (schedule +
workflow_dispatch), and release.yml's publish-vscode job has needs: [..., security-audit]
(line 290-293), with the security-audit job (227-286) dispatching and gating on this workflow.
- PASS —
security-audit.yaml still requires the qualitative pass (prompt lines 90-93),
treats the FAIL IF list as non-exhaustive (lines 83-85), keeps the failure-reporting step that
opens/updates a security-audit-failure issue and exits non-zero (lines 123-172), and keeps the
AUDIT_PAT pre-check that fails the job if the secret is absent (lines 47-55).
Qualitative findings
- BLOCKER —
.github/workflows/tend-mention.yaml, verify job (no permissions: block,
lines 34-205). This job runs on issues (edited), issue_comment (created/edited),
pull_request_review (submitted), and pull_request_review_comment (edited) — all
attacker-influenceable triggers per SECURITY.md's own "Prompt-injection through user-supplied
content" threat model. Because the repo's default_workflow_permissions is write, this job's
ambient GITHUB_TOKEN carries full write scope across contents/issues/PRs/actions/checks/
deployments/packages/security-events/statuses. In the current file, every authenticated step in
this job explicitly overrides GITHUB_TOKEN with secrets.TEND_BOT_TOKEN for its own gh calls,
so the ambient token is not consumed today — but it still exists as a live, over-scoped credential
in the runner for the job's duration, contradicts the least-privilege pattern every sibling job in
this repo follows (all other jobs, including handle in the same file, declare an explicit
permissions: block), and would silently regrant broad write access to any future step added to
this job (or to any action/dependency it pulls in) without anyone touching a permissions: line.
Fix: add an explicit permissions: {contents: read} (or the minimal read set the job actually
needs) to the verify job, matching the pattern used everywhere else in this repo. Note this file
is machine-generated by tend (# Do not edit this file directly); the fix belongs upstream in
the tend-ci-runner plugin's job template, or the regeneration needs a local override mechanism.
- INFO —
.github/workflows/chromatic.yml:39 pins chromaui/action by full commit SHA
(94713c544284a14195de3b50ef24301579f1877e) but trails it with a stale # latest comment instead
of a version number. Not a security issue (the ref is a real SHA pin, satisfying the "pinned by
commit hash" rule) — just a misleading comment for future readers/Renovate diffs.
- INFO — Reviewed all
run: steps across every workflow for classic GitHub Actions script
injection (untrusted ${{ github.event.* }} interpolated directly into a shell string). Found
none: tend-mention.yaml and all other workflows correctly route event-derived strings (comment
bodies, issue titles, URLs) through env: maps and reference them as shell variables
($COMMENT_BODY, etc.), which is the safe pattern. No action needed.
- INFO —
vscode-extension-publish environment's required_reviewers protection rule lists two
named human reviewers (nedtwigg, edgartwigg) with prevent_self_review: true, matching
SECURITY.md's description that the publish approval "can't be the same account which triggered the
publish." Confirmed via gh api repos/diffplug/dormouse/environments.
- INFO —
pnpm-workspace.yaml's allowBuilds disables native postinstall scripts for
@vscode/vsce-sign and keytar while allowing them for @swc/core, esbuild, node-pty,
sharp — a deliberate, narrow allowlist consistent with the "small dependency surface" posture in
SECURITY.md §Dependency Supply Chain. No action needed.
Summary
FAIL. All 23 previously-passing FAIL IF checks that could be mechanically verified with the
provided tokens remain PASS, including every ruleset, environment, secret-placement, and signing
check that requires $AUDIT_PAT. However, one FAIL IF is newly violated: the verify job in
.github/workflows/tend-mention.yaml — a job triggered by attacker-influenceable events (issue and
PR comments) — has no explicit permissions: block, and the repository's default_workflow_permissions
is write, so that job's GITHUB_TOKEN silently carries full write scope across every permission
category instead of the bounded set SECURITY.md requires for agent-managed workflows. No currently-
written step in the job spends that ambient token (all authenticated calls use the separately-scoped
TEND_BOT_TOKEN instead), so there is no active exploit path today, but the drift from the repo's
own declared policy is real, mechanically confirmed, and sits exactly on the threat surface
SECURITY.md calls out for this bot. This alone is sufficient to fail the audit per the "FAIL IF any
FAIL IF is violated" rule in the CI Validation Contract.
Audit failed at 2026-07-20T07:35Z. Run
Security Audit Report
Audited against
SECURITY.mdas of commit5b42136(2026-07-20).$AUDIT_PAT(fine-grained,read-only Administration + Secrets + Environments) was available and used for all admin-scoped
checks below.
FAIL IF results
Dependency Supply Chain
node website/scripts/generate-deps.jsfrom a clean checkout (git statuswasclean before and after).
git diff --statagainstwebsite/src/data/dependencies-{npm,cargo,runtime}.jsonproduced no output — no drift.
package.json:6-12hasdevEngines.runtime.version: "24.18.0"— exactMAJOR.MINOR.PATCH.standalone/src-tauri/build.rs:42-43,149-227(read_pinned_node_version+verify_node_version) readsdevEngines.runtime.versionand fails the build (Err(...)) if thebundled binary's
node --versiondoesn't match..github/workflows/release.yml:39-41(build-standalonejob):actions/setup-node@...withnode-version-file: package.json, which resolvesdevEngines.runtime.version.pnpm-workspace.yaml:23hasminimumReleaseAge: 1440..github/renovate.json:3—enabledManagers: ["github-actions", "npm", "cargo"]..github/renovate.json:68-82—minimumReleaseAgepackage rules fornpm/cargoatpatch(1 day),minor(3 days),major(14 days).GitHub Actions Policies
grep -rn pull_request_target .github/workflowsmatched only.github/workflows/tend-review.yaml:11.ci.yml,chromatic.yml,release.yml) checked:ci.yml:8-9,chromatic.yml:13-14:contents: readonly, no writes.release.ymlbuild-standalone/build-vscodejobs (lines 14-17, 162-165):contents: read,id-token: write,attestations: write— exactly the allowed releaseprovenance set.
release.ymlsecurity-auditjob (lines 230-239):contents: read,actions: write— theexplicitly documented exception.
release.ymlpublish-vscodejob: no job-levelpermissions:block, inherits the workflow'stop-level
contents: read(no write).Automated Maintainer (tend)
Merge access(id16757376,GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/rulesets/16757376):target: branch,conditions.ref_name.include: ["~DEFAULT_BRANCH"],rules: [{"type":"update"}],bypass_actors: [{"actor_id":5, "actor_type":"RepositoryRole","bypass_mode":"exempt"}]— sole bypass actor is admin (role id 5).Tag operations(id16757382):target: tag,conditions.ref_name.include: ["~ALL"],rules: [{"type":"creation"},{"type":"update"}], same sole admin-only bypass actor.GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/collaborators/dormouse-bot/permission→
{"permission":"write","role_name":"write"}— exactlypush/write, not higher.GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/actions/secrets→ repo-levelsecrets are
CHROMATIC_PROJECT_TOKEN,CLAUDE_CODE_OAUTH_TOKEN,TEND_BOT_TOKENonly. NeitherOVSX_PATnorVSCE_PATpresent at repo level.gh api repos/diffplug/dormouse/environments): onlysecurity-auditand
vscode-extension-publishexist.security-auditdeployment-branch-policies:main(branch) +v*(tag) — both admin-gated(
Merge access/Tag operations).vscode-extension-publishdeployment-branch-policies:v*(tag) only — admin-gated(
Tag operations).AUDIT_PATis present in thesecurity-auditenvironment secrets(
gh api .../environments/security-audit/secrets→{"total_count":1,"secrets":[{"name":"AUDIT_PAT",...}]})and absent from the repo-level secret list (check 13's output).
.config/tend.yaml:4-5—secrets.allowed: [CHROMATIC_PROJECT_TOKEN]..github/workflows/workflow-audit.yamlexists, is not disabled (on: schedule+workflow_dispatch), and its most recent run(
gh api repos/diffplug/dormouse/actions/workflows/workflow-audit.yaml/runs) completedsuccessat2026-07-19T09:13:32Z, ~22h before this audit (date -u→2026-07-20T07:32:25Z),well inside 48h.
tend-*.yamlaction refs are tag-pinned (actions/checkout@v7,max-sixty/tend/claude@0.1.11) — none use@mainor an unpinned ref. Tag pins are explicitlyaccepted in this file class.
.github/workflows/tend-mention.yaml'sverifyjob (lines 34-205) declares nopermissions:block (only thehandlejob, line 213, has one; grep confirms no top-levelpermissions:key exists in the file either). The repo's default Actions token permission iswrite(GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/actions/permissions/workflow→{"default_workflow_permissions":"write","can_approve_pull_request_reviews":true}). That defaultgrants the job's ambient
GITHUB_TOKENwrite access to every scope (contents, issues,pull-requests, actions, checks, deployments, packages, security-events, statuses, …), which
exceeds the allowed set (
contents: write,pull-requests: write,issues: write,id-token: write,actions: read, or anyread). See Qualitative findings for exploitabilitydiscussion.
VS Code Extension Releases
release.yml:294-296—publish-vscodejob hasenvironment: {name: vscode-extension-publish}.grep -rn 'VSCE_PAT|OVSX_PAT' .github/workflowsmatched onlyrelease.yml:325,337, both inside thepublish-vscodejob (the only job with thevscode-extension-publishenvironment).APPLE_SIGN_PASS,EV_SIGN_PIN,TAURI_SIGNING_PRIVATE_KEYfrom a repo/org secret) appear inrelease.yml;grep -n 'secrets\.' release.ymlreturns onlyGITHUB_TOKEN,VSCE_PAT,OVSX_PAT.release.yml:63-74("Generate ephemeral Tauri updater key") runstauri signer generate --ci --write-keys ... --forcefor every CI build; no persistent/production updater keyis injected.
Desktop Releases
scripts/sign-and-deploy.sh:392-399(verify_downloaded_artifact) callsgh attestation verifywith--cert-identity/--cert-oidc-issuer/--source-ref/--source-digestbefore any signing proceeds.
scripts/sign-and-deploy.sh:401-403callscheck_sha256_manifest(sha256sum -c/shasum -a 256 -c) after attestation verification.scripts/sign-and-deploy.sh:683-718(sign_windows) usesjsign --storetype PIV.CI Validation Contract
.github/workflows/security-audit.yamlexists, is enabled (schedule+workflow_dispatch), andrelease.yml'spublish-vscodejob hasneeds: [..., security-audit](line 290-293), with the
security-auditjob (227-286) dispatching and gating on this workflow.security-audit.yamlstill requires the qualitative pass (prompt lines 90-93),treats the
FAIL IFlist as non-exhaustive (lines 83-85), keeps the failure-reporting step thatopens/updates a
security-audit-failureissue and exits non-zero (lines 123-172), and keeps theAUDIT_PATpre-check that fails the job if the secret is absent (lines 47-55).Qualitative findings
.github/workflows/tend-mention.yaml,verifyjob (nopermissions:block,lines 34-205). This job runs on
issues(edited),issue_comment(created/edited),pull_request_review(submitted), andpull_request_review_comment(edited) — allattacker-influenceable triggers per SECURITY.md's own "Prompt-injection through user-supplied
content" threat model. Because the repo's
default_workflow_permissionsiswrite, this job'sambient
GITHUB_TOKENcarries full write scope across contents/issues/PRs/actions/checks/deployments/packages/security-events/statuses. In the current file, every authenticated step in
this job explicitly overrides
GITHUB_TOKENwithsecrets.TEND_BOT_TOKENfor its ownghcalls,so the ambient token is not consumed today — but it still exists as a live, over-scoped credential
in the runner for the job's duration, contradicts the least-privilege pattern every sibling job in
this repo follows (all other jobs, including
handlein the same file, declare an explicitpermissions:block), and would silently regrant broad write access to any future step added tothis job (or to any action/dependency it pulls in) without anyone touching a
permissions:line.Fix: add an explicit
permissions: {contents: read}(or the minimal read set the job actuallyneeds) to the
verifyjob, matching the pattern used everywhere else in this repo. Note this fileis machine-generated by
tend(# Do not edit this file directly); the fix belongs upstream inthe
tend-ci-runnerplugin's job template, or the regeneration needs a local override mechanism..github/workflows/chromatic.yml:39pinschromaui/actionby full commit SHA(
94713c544284a14195de3b50ef24301579f1877e) but trails it with a stale# latestcomment insteadof a version number. Not a security issue (the ref is a real SHA pin, satisfying the "pinned by
commit hash" rule) — just a misleading comment for future readers/Renovate diffs.
run:steps across every workflow for classic GitHub Actions scriptinjection (untrusted
${{ github.event.* }}interpolated directly into a shell string). Foundnone:
tend-mention.yamland all other workflows correctly route event-derived strings (commentbodies, issue titles, URLs) through
env:maps and reference them as shell variables(
$COMMENT_BODY, etc.), which is the safe pattern. No action needed.vscode-extension-publishenvironment'srequired_reviewersprotection rule lists twonamed human reviewers (
nedtwigg,edgartwigg) withprevent_self_review: true, matchingSECURITY.md's description that the publish approval "can't be the same account which triggered the
publish." Confirmed via
gh api repos/diffplug/dormouse/environments.pnpm-workspace.yaml'sallowBuildsdisables native postinstall scripts for@vscode/vsce-signandkeytarwhile allowing them for@swc/core,esbuild,node-pty,sharp— a deliberate, narrow allowlist consistent with the "small dependency surface" posture inSECURITY.md §Dependency Supply Chain. No action needed.
Summary
FAIL. All 23 previously-passing
FAIL IFchecks that could be mechanically verified with theprovided tokens remain PASS, including every ruleset, environment, secret-placement, and signing
check that requires
$AUDIT_PAT. However, oneFAIL IFis newly violated: theverifyjob in.github/workflows/tend-mention.yaml— a job triggered by attacker-influenceable events (issue andPR comments) — has no explicit
permissions:block, and the repository'sdefault_workflow_permissionsis
write, so that job'sGITHUB_TOKENsilently carries full write scope across every permissioncategory instead of the bounded set SECURITY.md requires for agent-managed workflows. No currently-
written step in the job spends that ambient token (all authenticated calls use the separately-scoped
TEND_BOT_TOKENinstead), so there is no active exploit path today, but the drift from the repo'sown declared policy is real, mechanically confirmed, and sits exactly on the threat surface
SECURITY.md calls out for this bot. This alone is sufficient to fail the audit per the "FAIL IF any
FAIL IFis violated" rule in the CI Validation Contract.