Audit failed at 2026-06-20T05:53Z. Run
Security Audit Report
Audited against SECURITY.md on 2026-06-20.
FAIL IF results
Dependency Supply Chain
| # |
Check |
Result |
Evidence |
| 1 |
node website/scripts/generate-deps.js must not change dependency JSON files |
FAIL |
Running the script from a clean checkout modified website/src/data/dependencies-cargo.json (8 insertions, 7 deletions). The base64 0.22.1 crate moved from the transitive section to the direct section (acquiring a declaredName field), indicating a Cargo dependency was promoted to direct without regenerating the supply-chain data. git diff --stat output: website/src/data/dependencies-cargo.json | 15 ++++++++------- |
| 2 |
package.json must have devEngines.runtime.version as exact MAJOR.MINOR.PATCH |
PASS |
package.json:9 — "version": "22.22.3" |
| 3 |
standalone/src-tauri/build.rs must verify bundled Node.js matches the pin |
PASS |
build.rs:37-38 — read_pinned_node_version() then verify_node_version() called unconditionally in bundle_node_runtime(). Version mismatch → build error. |
| 4 |
build-standalone job in release.yml must install the pinned Node from devEngines.runtime.version |
PASS |
release.yml:37-51 — reads jq -r '.devEngines.runtime.version', validates MAJOR.MINOR.PATCH regex, passes result to actions/setup-node. |
| 5 |
pnpm-workspace.yaml must have minimumReleaseAge: 1440 |
PASS |
pnpm-workspace.yaml:19 — minimumReleaseAge: 1440 |
| 6 |
renovate.json must list npm and cargo in enabledManagers |
PASS |
renovate.json:3 — "enabledManagers": ["github-actions", "npm", "cargo"] |
| 7 |
renovate.json must have minimumReleaseAge package rules for npm/cargo |
PASS |
renovate.json:40-53 — three rules: patch=1 day, minor=3 days, major=14 days, each matching matchManagers: ["npm","cargo"] |
GitHub Actions Policies
| # |
Check |
Result |
Evidence |
| 8 |
pull_request_target only in tend-*.yaml |
PASS |
Only tend-review.yaml uses pull_request_target (line 11). All other workflow files checked: ci.yml, chromatic.yml, release.yml, workflow-audit.yaml, security-audit.yaml — none use pull_request_target. |
| 9 |
Non-agent-managed workflows must not grant write permissions beyond id-token: write, attestations: write, and actions: write (security-audit job only) |
PASS |
ci.yml:9 — contents: read. chromatic.yml:14 — contents: read. release.yml top-level contents: read; job-level: build-standalone/build-vscode add id-token: write and attestations: write (provenance); security-audit job adds actions: write; publish-vscode inherits top-level only. All within permitted scope. |
Automated Maintainer (tend)
| # |
Check |
Result |
Evidence |
| 10 |
Ruleset "Merge access" must exist, target ~DEFAULT_BRANCH, block only update, and have admin (RepositoryRole actor 5) as sole bypass |
PASS |
GH_TOKEN=$AUDIT_PAT gh api repos/$GITHUB_REPOSITORY/rulesets/16757376: target=branch, conditions.ref_name.include=["~DEFAULT_BRANCH"], rules=[{type:"update"}], bypass_actors=[{actor_id:5, actor_type:"RepositoryRole", bypass_mode:"exempt"}] |
| 11 |
Ruleset "Tag operations" must exist, target ~ALL, block creation and update, have admin-only bypass |
PASS |
GH_TOKEN=$AUDIT_PAT gh api repos/$GITHUB_REPOSITORY/rulesets/16757382: target=tag, conditions.ref_name.include=["~ALL"], rules=[{type:"creation"},{type:"update"}], bypass_actors=[{actor_id:5, actor_type:"RepositoryRole", bypass_mode:"exempt"}] |
| 12 |
dormouse-bot must not hold permission higher than push |
PASS |
API response: {"role_name":"write","permissions":{"admin":false,"maintain":false,"push":true,"pull":true,"triage":true}} |
| 13 |
OVSX_PAT and VSCE_PAT must not appear as repo-level secrets |
PASS |
Repo-level secrets listing: CHROMATIC_PROJECT_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, TEND_BOT_TOKEN. Neither OVSX_PAT nor VSCE_PAT present. |
| 14 |
All environment deployment-branch-policies must admit only admin-gated refs |
PASS |
vscode-extension-publish: only v* tags (admin-gated by Tag operations ruleset). security-audit: main branch (admin-gated by Merge access) and v* tags (admin-gated by Tag operations). |
| 15 |
AUDIT_PAT must be in security-audit environment only, not at repo level |
PASS |
security-audit environment secrets: AUDIT_PAT. Repo-level secrets do not include AUDIT_PAT. |
| 16 |
CHROMATIC_PROJECT_TOKEN must appear in .config/tend.yaml secrets.allowed |
PASS |
.config/tend.yaml:5 — - CHROMATIC_PROJECT_TOKEN |
| 17 |
workflow-audit.yaml must exist, be enabled, and have a successful run within 48 hours |
PASS |
File exists. API: state=active. Last successful run: 2026-06-19T08:54:41Z (~23 hours before this audit). |
| 18 |
tend-*.yaml workflows must not use unpinned action references (tag pins accepted for tend files) |
PASS |
All tend-*.yaml files use actions/checkout@v6 and max-sixty/tend@0.1.6 — both tag-pinned. SECURITY.md explicitly allows tag pins in tend-generated files. |
| 19 |
Agent-managed workflows must not exceed contents: write, pull-requests: write, issues: write, id-token: write, actions: read, or any read permission |
PASS |
All tend-*.yaml files use subsets of exactly those permissions. workflow-audit.yaml: contents: read, issues: write, actions: read. security-audit.yaml: contents: read, actions: read, issues: write, id-token: write. |
VS Code Extension Releases
| # |
Check |
Result |
Evidence |
| 20 |
release.yml must include vscode-extension-publish environment on the VS Code publish job |
PASS |
release.yml:304-305 — environment: name: vscode-extension-publish on publish-vscode job |
| 21 |
VSCE_PAT and OVSX_PAT must only be used within vscode-extension-publish environment |
PASS |
release.yml:336 — VSCE_PAT used only in publish-vscode (which has the vscode-extension-publish environment). release.yml:348 — OVSX_PAT same. No other workflow file references these secrets. |
| 22 |
release.yml must not use production desktop signing secrets in CI |
PASS |
release.yml generates an ephemeral Tauri updater key (lines 75-87). No macOS signing identity, Windows EV cert, notarization password, or production Tauri private key is referenced anywhere in release.yml. |
| 23 |
release.yml must generate an ephemeral Tauri updater key for unsigned CI artifacts |
PASS |
release.yml:75-87 — tauri signer generate --ci --write-keys "$key_path" followed by echo "TAURI_SIGNING_PRIVATE_KEY=$key_path" >> "$GITHUB_ENV" |
Desktop Releases
| # |
Check |
Result |
Evidence |
| 24 |
sign-and-deploy.sh must verify GitHub artifact attestations |
PASS |
sign-and-deploy.sh:393-399 — gh attestation verify "$manifest" --repo "$GITHUB_REPO" --cert-identity "$identity" --cert-oidc-issuer ... called in verify_downloaded_artifact(), invoked for every artifact before any signing step. |
| 25 |
sign-and-deploy.sh must verify artifact SHA-256 manifests |
PASS |
sign-and-deploy.sh:359-370 — check_sha256_manifest() using sha256sum -c (or shasum -a 256 -c), called from verify_downloaded_artifact(). |
| 26 |
sign-and-deploy.sh must use PIV-backed Windows signing |
PASS |
sign-and-deploy.sh:700-702 — jsign --storetype PIV --storepass "$EV_SIGN_PIN" --alias "$JSIGN_ALIAS" used for both the executable and the NSIS installer. |
CI Validation Contract
| # |
Check |
Result |
Evidence |
| 27 |
security-audit.yaml must exist, be enabled, and be invoked from release.yml's publish path |
PASS |
security-audit.yaml exists and gh api confirms state=active. release.yml has a security-audit job that dispatches it, and publish-vscode declares needs: [build-standalone, build-vscode, security-audit]. |
| 28 |
The audit must not be weakened (qualitative pass still required, FAIL IF checks enforceable, failure-reporting step intact, AUDIT_PAT pre-check not removed) |
PASS |
security-audit.yaml:49-57 — AUDIT_PAT pre-check present and exits 1 on failure. security-audit.yaml:125-174 — failure-reporting step opens/updates issue labeled security-audit-failure and exits 1 on FAIL. Prompt still requires qualitative pass and mechanical FAIL IF checks. |
Qualitative findings
WARNING — Tauri private key exposed in process cmdline (scripts/sign-and-deploy.sh:766)
sign_updates() passes TAURI_SIGNING_PRIVATE_KEY as both an environment variable (correct, Tauri reads it by convention) and a CLI argument (--private-key "$TAURI_SIGNING_PRIVATE_KEY"). On Linux and macOS, process arguments are world-readable via ps aux and /proc/<pid>/cmdline, while environment variables in /proc/<pid>/environ are restricted to the owning user and root. The redundant --private-key argument leaks the private key content to any process or user that runs ps during the brief signing window. The fix is to remove the --private-key CLI flag and rely solely on the TAURI_SIGNING_PRIVATE_KEY env var, which Tauri's signer already reads.
INFO — pnpm install without --frozen-lockfile in non-release workflows
ci.yml (line 35) and chromatic.yml (line 37) run pnpm install without --frozen-lockfile. If the lockfile drifts from the manifests, pnpm will silently update it and install newer package versions than expected. The release workflows (release.yml) correctly use --frozen-lockfile, so release artifacts are not affected. Risk is limited to test/CI correctness rather than shipped software.
INFO — ANTHROPIC_API_KEY not visible in repo or environment secret listings
All tend-*.yaml files reference ${{ secrets.ANTHROPIC_API_KEY }}, but this secret does not appear in the repo-level secret listing (which shows only CHROMATIC_PROJECT_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, TEND_BOT_TOKEN) or in either environment. It is likely an org-level secret. If the org secret is ever removed without adding a repo-level replacement, tend workflows would silently fail their Claude invocations. No SECURITY.md control is violated; this is an operational resilience note.
INFO — workflow-audit.yaml opens a new issue per audit rather than updating a single tracker
Each run of workflow-audit.yaml that finds changes opens a fresh GitHub issue. For repositories with frequent bot activity this can accumulate many open issues. The security-audit.yaml failure loop (update existing issue) is a better pattern. No security boundary is affected.
Summary
FAIL
The audit fails on one FAIL IF violation: running node website/scripts/generate-deps.js from a clean checkout modifies website/src/data/dependencies-cargo.json. The base64 0.22.1 crate has been promoted from a transitive to a direct Cargo dependency (it gained a declaredName field in the direct section), but the supply-chain data file was not regenerated and committed. SECURITY.md requires the generated supply-chain files to be kept in sync with every dependency change, so the disclosed dependency list on the supply-chain page no longer accurately reflects the current Cargo.lock. All other FAIL IF checks pass, and no qualitative findings rise to BLOCKER severity. The one WARNING (Tauri private key in CLI args) is a local-machine risk during the manual signing step and does not affect the CI/release pipeline integrity.
Audit failed at 2026-06-20T05:53Z. Run
Security Audit Report
Audited against SECURITY.md on 2026-06-20.
FAIL IF results
Dependency Supply Chain
node website/scripts/generate-deps.jsmust not change dependency JSON fileswebsite/src/data/dependencies-cargo.json(8 insertions, 7 deletions). Thebase64 0.22.1crate moved from the transitive section to thedirectsection (acquiring adeclaredNamefield), indicating a Cargo dependency was promoted to direct without regenerating the supply-chain data.git diff --statoutput:website/src/data/dependencies-cargo.json | 15 ++++++++-------package.jsonmust havedevEngines.runtime.versionas exact MAJOR.MINOR.PATCHpackage.json:9—"version": "22.22.3"standalone/src-tauri/build.rsmust verify bundled Node.js matches the pinbuild.rs:37-38—read_pinned_node_version()thenverify_node_version()called unconditionally inbundle_node_runtime(). Version mismatch → build error.build-standalonejob inrelease.ymlmust install the pinned Node fromdevEngines.runtime.versionrelease.yml:37-51— readsjq -r '.devEngines.runtime.version', validates MAJOR.MINOR.PATCH regex, passes result toactions/setup-node.pnpm-workspace.yamlmust haveminimumReleaseAge: 1440pnpm-workspace.yaml:19—minimumReleaseAge: 1440renovate.jsonmust listnpmandcargoinenabledManagersrenovate.json:3—"enabledManagers": ["github-actions", "npm", "cargo"]renovate.jsonmust haveminimumReleaseAgepackage rules for npm/cargorenovate.json:40-53— three rules: patch=1 day, minor=3 days, major=14 days, each matchingmatchManagers: ["npm","cargo"]GitHub Actions Policies
pull_request_targetonly intend-*.yamltend-review.yamlusespull_request_target(line 11). All other workflow files checked: ci.yml, chromatic.yml, release.yml, workflow-audit.yaml, security-audit.yaml — none usepull_request_target.id-token: write,attestations: write, andactions: write(security-audit job only)ci.yml:9—contents: read.chromatic.yml:14—contents: read.release.ymltop-levelcontents: read; job-level: build-standalone/build-vscode addid-token: writeandattestations: write(provenance); security-audit job addsactions: write; publish-vscode inherits top-level only. All within permitted scope.Automated Maintainer (tend)
~DEFAULT_BRANCH, block onlyupdate, and have admin (RepositoryRole actor 5) as sole bypassGH_TOKEN=$AUDIT_PAT gh api repos/$GITHUB_REPOSITORY/rulesets/16757376:target=branch,conditions.ref_name.include=["~DEFAULT_BRANCH"],rules=[{type:"update"}],bypass_actors=[{actor_id:5, actor_type:"RepositoryRole", bypass_mode:"exempt"}]~ALL, blockcreationandupdate, have admin-only bypassGH_TOKEN=$AUDIT_PAT gh api repos/$GITHUB_REPOSITORY/rulesets/16757382:target=tag,conditions.ref_name.include=["~ALL"],rules=[{type:"creation"},{type:"update"}],bypass_actors=[{actor_id:5, actor_type:"RepositoryRole", bypass_mode:"exempt"}]dormouse-botmust not hold permission higher thanpush{"role_name":"write","permissions":{"admin":false,"maintain":false,"push":true,"pull":true,"triage":true}}OVSX_PATandVSCE_PATmust not appear as repo-level secretsCHROMATIC_PROJECT_TOKEN,CLAUDE_CODE_OAUTH_TOKEN,TEND_BOT_TOKEN. NeitherOVSX_PATnorVSCE_PATpresent.vscode-extension-publish: onlyv*tags (admin-gated by Tag operations ruleset).security-audit:mainbranch (admin-gated by Merge access) andv*tags (admin-gated by Tag operations).AUDIT_PATmust be insecurity-auditenvironment only, not at repo levelsecurity-auditenvironment secrets:AUDIT_PAT. Repo-level secrets do not includeAUDIT_PAT.CHROMATIC_PROJECT_TOKENmust appear in.config/tend.yamlsecrets.allowed.config/tend.yaml:5—- CHROMATIC_PROJECT_TOKENworkflow-audit.yamlmust exist, be enabled, and have a successful run within 48 hoursstate=active. Last successful run:2026-06-19T08:54:41Z(~23 hours before this audit).tend-*.yamlworkflows must not use unpinned action references (tag pins accepted for tend files)actions/checkout@v6andmax-sixty/tend@0.1.6— both tag-pinned. SECURITY.md explicitly allows tag pins in tend-generated files.contents: write,pull-requests: write,issues: write,id-token: write,actions: read, or anyreadpermissionworkflow-audit.yaml:contents: read,issues: write,actions: read.security-audit.yaml:contents: read,actions: read,issues: write,id-token: write.VS Code Extension Releases
release.ymlmust includevscode-extension-publishenvironment on the VS Code publish jobrelease.yml:304-305—environment: name: vscode-extension-publishonpublish-vscodejobVSCE_PATandOVSX_PATmust only be used withinvscode-extension-publishenvironmentrelease.yml:336—VSCE_PATused only inpublish-vscode(which has thevscode-extension-publishenvironment).release.yml:348—OVSX_PATsame. No other workflow file references these secrets.release.ymlmust not use production desktop signing secrets in CIrelease.ymlgenerates an ephemeral Tauri updater key (lines 75-87). No macOS signing identity, Windows EV cert, notarization password, or production Tauri private key is referenced anywhere inrelease.yml.release.ymlmust generate an ephemeral Tauri updater key for unsigned CI artifactsrelease.yml:75-87—tauri signer generate --ci --write-keys "$key_path"followed byecho "TAURI_SIGNING_PRIVATE_KEY=$key_path" >> "$GITHUB_ENV"Desktop Releases
sign-and-deploy.shmust verify GitHub artifact attestationssign-and-deploy.sh:393-399—gh attestation verify "$manifest" --repo "$GITHUB_REPO" --cert-identity "$identity" --cert-oidc-issuer ...called inverify_downloaded_artifact(), invoked for every artifact before any signing step.sign-and-deploy.shmust verify artifact SHA-256 manifestssign-and-deploy.sh:359-370—check_sha256_manifest()usingsha256sum -c(orshasum -a 256 -c), called fromverify_downloaded_artifact().sign-and-deploy.shmust use PIV-backed Windows signingsign-and-deploy.sh:700-702—jsign --storetype PIV --storepass "$EV_SIGN_PIN" --alias "$JSIGN_ALIAS"used for both the executable and the NSIS installer.CI Validation Contract
security-audit.yamlmust exist, be enabled, and be invoked fromrelease.yml's publish pathsecurity-audit.yamlexists andgh apiconfirmsstate=active.release.ymlhas asecurity-auditjob that dispatches it, andpublish-vscodedeclaresneeds: [build-standalone, build-vscode, security-audit].security-audit.yaml:49-57— AUDIT_PAT pre-check present and exits 1 on failure.security-audit.yaml:125-174— failure-reporting step opens/updates issue labeledsecurity-audit-failureand exits 1 on FAIL. Prompt still requires qualitative pass and mechanical FAIL IF checks.Qualitative findings
WARNING — Tauri private key exposed in process cmdline (
scripts/sign-and-deploy.sh:766)sign_updates()passesTAURI_SIGNING_PRIVATE_KEYas both an environment variable (correct, Tauri reads it by convention) and a CLI argument (--private-key "$TAURI_SIGNING_PRIVATE_KEY"). On Linux and macOS, process arguments are world-readable viaps auxand/proc/<pid>/cmdline, while environment variables in/proc/<pid>/environare restricted to the owning user and root. The redundant--private-keyargument leaks the private key content to any process or user that runspsduring the brief signing window. The fix is to remove the--private-keyCLI flag and rely solely on theTAURI_SIGNING_PRIVATE_KEYenv var, which Tauri's signer already reads.INFO —
pnpm installwithout--frozen-lockfilein non-release workflowsci.yml(line 35) andchromatic.yml(line 37) runpnpm installwithout--frozen-lockfile. If the lockfile drifts from the manifests, pnpm will silently update it and install newer package versions than expected. The release workflows (release.yml) correctly use--frozen-lockfile, so release artifacts are not affected. Risk is limited to test/CI correctness rather than shipped software.INFO —
ANTHROPIC_API_KEYnot visible in repo or environment secret listingsAll
tend-*.yamlfiles reference${{ secrets.ANTHROPIC_API_KEY }}, but this secret does not appear in the repo-level secret listing (which shows onlyCHROMATIC_PROJECT_TOKEN,CLAUDE_CODE_OAUTH_TOKEN,TEND_BOT_TOKEN) or in either environment. It is likely an org-level secret. If the org secret is ever removed without adding a repo-level replacement, tend workflows would silently fail their Claude invocations. No SECURITY.md control is violated; this is an operational resilience note.INFO —
workflow-audit.yamlopens a new issue per audit rather than updating a single trackerEach run of
workflow-audit.yamlthat finds changes opens a fresh GitHub issue. For repositories with frequent bot activity this can accumulate many open issues. Thesecurity-audit.yamlfailure loop (update existing issue) is a better pattern. No security boundary is affected.Summary
FAIL
The audit fails on one
FAIL IFviolation: runningnode website/scripts/generate-deps.jsfrom a clean checkout modifieswebsite/src/data/dependencies-cargo.json. Thebase64 0.22.1crate has been promoted from a transitive to a direct Cargo dependency (it gained adeclaredNamefield in the direct section), but the supply-chain data file was not regenerated and committed. SECURITY.md requires the generated supply-chain files to be kept in sync with every dependency change, so the disclosed dependency list on the supply-chain page no longer accurately reflects the currentCargo.lock. All otherFAIL IFchecks pass, and no qualitative findings rise to BLOCKER severity. The one WARNING (Tauri private key in CLI args) is a local-machine risk during the manual signing step and does not affect the CI/release pipeline integrity.