Skip to content

[security-audit] FAIL on 2026-06-21 #165

Description

@github-actions

Audit failed at 2026-06-21T06:10Z. Run

Security Audit Report

Audited against SECURITY.md on 2026-06-21.


FAIL IF results

Dependency Supply Chain

Check 1 — FAIL node website/scripts/generate-deps.js changes dependency files when run from a clean checkout.

website/src/data/dependencies-cargo.json was modified. Three Cargo crates that became direct dependencies were missing from the committed file: objc2 v0.6.4, objc2-app-kit v0.3.2, and objc2-foundation v0.3.2 each gained a declaredName field and moved to the direct-dependency section. git diff --stat reports 45 lines changed (24 insertions, 21 deletions). The npm and runtime files were unchanged.

Check 2 — PASS package.json has devEngines.runtime.version: "22.22.3" (MAJOR.MINOR.PATCH).

Check 3 — PASS standalone/src-tauri/build.rs calls read_pinned_node_version() and verify_node_version() at lines 37–38, failing the build if the bundled binary's version string does not match.

Check 4 — PASS release.yml build-standalone job reads devEngines.runtime.version via jq -r '.devEngines.runtime.version' package.json, validates the MAJOR.MINOR.PATCH pattern with a regex, and passes the result to actions/setup-node (node-version: ${{ steps.node-pin.outputs.version }}).

Check 5 — PASS pnpm-workspace.yaml contains minimumReleaseAge: 1440 at the top level.

Check 6 — PASS .github/renovate.json enabledManagers is ["github-actions", "npm", "cargo"].

Check 7 — PASS .github/renovate.json has three minimumReleaseAge package rules matching ["npm", "cargo"]: "1 day" for patch, "3 days" for minor, "14 days" for major.

GitHub Actions Policies

Check 8 — PASS pull_request_target appears only in tend-review.yaml, which is an agent-managed (tend-*.yaml) workflow. No other workflow file contains the trigger.

Check 9 — PASS Non-agent-managed workflows (ci.yml, chromatic.yml, release.yml) grant only permitted write permissions:

  • ci.yml: contents: read only.
  • chromatic.yml: contents: read only.
  • release.yml build-standalone / build-vscode: id-token: write, attestations: write (both explicitly allowed). security-audit job: actions: write (explicitly allowed by the "Release audit dispatch" exception). publish-vscode: inherits top-level contents: read.

Automated Maintainer (tend)

Check 10 — PASS Ruleset "Merge access" (ID 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 is admin.

Check 11 — PASS Ruleset "Tag operations" (ID 16757382): target=tag, conditions.ref_name.include=["~ALL"], rules=[{"type":"creation"},{"type":"update"}], bypass_actors=[{"actor_id":5,"actor_type":"RepositoryRole","bypass_mode":"exempt"}]. Blocks both creation and update; sole bypass is admin.

Check 12 — PASS dormouse-bot collaborator permission API response: "permission":"write", "role_name":"write". Not above push level.

Check 13 — PASS Repo-level secrets (via GH_TOKEN=$AUDIT_PAT gh api): ["CHROMATIC_PROJECT_TOKEN","CLAUDE_CODE_OAUTH_TOKEN","TEND_BOT_TOKEN"]. Neither OVSX_PAT nor VSCE_PAT is present.

Check 14 — PASS Environment deployment branch policies:

  • vscode-extension-publish: one policy, {"name":"v*","type":"tag"} — admin-gated via "Tag operations" ruleset.
  • security-audit: two policies, {"name":"main","type":"branch"} (admin-gated via "Merge access") and {"name":"v*","type":"tag"} (admin-gated via "Tag operations").
    No non-admin-gated ref is admitted by either environment.

Check 15 — PASS security-audit environment secrets: ["AUDIT_PAT"]. AUDIT_PAT does not appear in the repo-level secret list.

Check 16 — PASS .config/tend.yaml contains secrets: allowed: - CHROMATIC_PROJECT_TOKEN.

Check 17 — PASS workflow-audit.yaml exists and is not disabled. Most recent successful run API response: {"conclusion":"success","created_at":"2026-06-20T08:19:39Z","head_branch":"main","status":"completed"} — within the 48-hour window (current date: 2026-06-21).

Check 18 — PASS All tend-*.yaml workflows use only tag-pinned references: actions/checkout@v6 and max-sixty/tend@0.1.6. No @main or unversioned refs appear in any tend workflow.

Check 19 — PASS Agent-managed workflows declare only these permissions (all within the allowed set):

  • tend-ci-fix.yaml: contents: write, pull-requests: write, id-token: write, actions: read
  • tend-review.yaml: contents: write, pull-requests: write, issues: write, id-token: write, actions: read
  • tend-review-runs.yaml, tend-triage.yaml, tend-nightly.yaml, tend-weekly.yaml, tend-notifications.yaml, tend-mention.yaml (handle job): same as tend-review.yaml
  • 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 20 — PASS release.yml publish-vscode job declares environment: name: vscode-extension-publish.

Check 21 — PASS VSCE_PAT and OVSX_PAT are referenced only inside the publish-vscode job, which is gated by the vscode-extension-publish environment. No other job or workflow file references these secrets.

Check 22 — PASS release.yml does not reference any production desktop signing secret (APPLE_SIGN_PASS, EV_SIGN_PIN, TAURI_SIGNING_PRIVATE_KEY). CI artifacts are built with an ephemeral key only.

Check 23 — PASS release.yml build-standalone job contains the "Generate ephemeral Tauri updater key" step (tauri signer generate --ci), setting TAURI_SIGNING_PRIVATE_KEY in GITHUB_ENV.

Desktop Releases

Check 24 — PASS scripts/sign-and-deploy.sh verify_downloaded_artifact() function (lines 390–399) calls gh attestation verify with --cert-identity, --cert-oidc-issuer, --source-ref, and --source-digest flags, failing hard if verification fails.

Check 25 — PASS scripts/sign-and-deploy.sh verify_downloaded_artifact() calls check_sha256_manifest() (lines 359–370), which runs sha256sum -c or shasum -a 256 -c against the manifest, failing hard if any hash mismatches.

Check 26 — PASS scripts/sign-and-deploy.sh sign_windows() function (lines 694–702) calls jsign --storetype PIV --storepass "$EV_SIGN_PIN" for both the executable and the installer.

CI Validation Contract

Check 27 — PASS security-audit.yaml exists and is not disabled. release.yml security-audit job dispatches security-audit.yaml and watches it; publish-vscode job declares needs: [build-standalone, build-vscode, security-audit].

Check 28 — PASS security-audit.yaml retains all required guards:

  • Prompt (line 84) includes "flag any other security hole you find" (qualitative pass required).
  • "Verify AUDIT_PAT is provisioned" step (line 49) exits 1 and writes FAIL if the secret is absent.
  • "Surface result, file or close issue" step (lines 125–174) opens/updates a security-audit-failure issue and calls exit 1 when status is not PASS.
  • No FAIL IF can be suppressed by the prompt or environment.

Qualitative findings

WARNING — pnpm install without --frozen-lockfile in ci.yml

ci.yml build-and-test job (line 28) and standalone-smoketest job (line 81, working-directory: standalone) run pnpm install without --frozen-lockfile. On a fresh checkout, if the lockfile has drifted from package.json declarations, pnpm will silently update the lockfile rather than failing. This means CI tests could exercise dependency resolutions that have never been reviewed. The release builds in release.yml correctly use --frozen-lockfile so production artifacts are not affected, but CI test coverage may silently shift under unreviewed versions.

Recommendation: add --frozen-lockfile to both pnpm install calls in ci.yml (or rely on the pnpm.installFrozen workspace setting).

INFO — ANTHROPIC_API_KEY referenced in tend-*.yaml but not acknowledged in SECURITY.md

All eight tend-*.yaml workflows pass anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} to the tend action. The repo-level secrets list (confirmed via GH_TOKEN=$AUDIT_PAT gh api) does not currently include ANTHROPIC_API_KEY (["CHROMATIC_PROJECT_TOKEN","CLAUDE_CODE_OAUTH_TOKEN","TEND_BOT_TOKEN"]), so the secret is not currently reachable. However, SECURITY.md's "Reachable repo-level secrets" section lists only three secrets and does not mention ANTHROPIC_API_KEY. If this secret is provisioned in the future, it would become a fourth reachable secret without a corresponding acknowledgment in the spec. The spec should be updated to either document that ANTHROPIC_API_KEY is intentionally absent, or acknowledge it as a reachable secret if it is ever provisioned.

INFO — TAURI_SIGNING_PRIVATE_KEY passed as both env var and CLI argument in sign-and-deploy.sh

scripts/sign-and-deploy.sh sign_updates() function (line 763) sets TAURI_SIGNING_PRIVATE_KEY as an environment variable and simultaneously passes --private-key "$TAURI_SIGNING_PRIVATE_KEY" as a CLI argument (line 766). On Linux and macOS, process argument lists are visible in /proc/PID/cmdline and ps aux output for the brief duration of the tauri signer sign invocation, exposing the private key in the process table. Since the Tauri CLI already reads TAURI_SIGNING_PRIVATE_KEY from the environment, the --private-key CLI flag is redundant and can be removed to eliminate this momentary exposure.


Summary

Overall status: FAIL

The audit found one violated FAIL IF check (Check 1): node website/scripts/generate-deps.js modifies website/src/data/dependencies-cargo.json when run from a clean checkout. Three Cargo crates — objc2 v0.6.4, objc2-app-kit v0.3.2, and objc2-foundation v0.3.2 — recently became direct dependencies (gained declaredName entries and moved to the direct-dependency section) but the committed supply-chain page snapshot was not regenerated. This breaks the invariant that the published dependency list at dormouse.sh/supply-chain provably reflects what ships. All other 27 FAIL IF checks pass, and no qualitative finding rises to BLOCKER severity. The two non-FAIL IF findings are a WARNING (non-frozen lockfile in CI) and two INFO items (undocumented secret reference, redundant CLI secret argument in the local signing script).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions