[security-audit] FAIL on 2026-09-19
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 25/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Cần làm rõ
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- github-actions, javascript, rust, typescript
Hướng nghiên cứu
Bắt đầu với lần chạy và bản chép lời GitHub Actions được liên kết, sau đó tìm phần domain được đánh dấu VERDICT: FAIL; các phần Supply chain và CI/secrets đang hiển thị có trạng thái PASS. Xem xét workflow, các tệp audit và dependency hoặc các bài kiểm thử được nêu tên cho lần kiểm tra thất bại đó. Hoàn tất có nghĩa là lỗi nền tảng được khắc phục và audit bảo mật đạt mà không phát sinh phát hiện mới.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Audit failed at 2026-09-09T09:18Z. Run · Transcript
- A domain returned
FAIL. audit-application.md opened withVERDICT: FAIL— read that domain's section first. A domain's own verdict outranks the merged one.
Security audit
Supply chain
VERDICT: PASS
FAIL IF results
Disclosure
node website/scripts/generate-deps.jsafterpnpm install --frozen-lockfileproduces no diff inwebsite/src/data/— PASS. Ranpnpm install --frozen-lockfile(already satisfied, "Already up to date"), then the generator: "Wrote 55 dependencies to src/data/dependencies-npm.json", "Wrote 13 direct and 478 transitive Cargo dependencies to src/data/dependencies-cargo.json", "Wrote 1 bundled runtime to src/data/dependencies-runtime.json".git status --porcelain -- website/src/data/andgit diff -- website/src/data/were both empty. Tree left clean, no revert needed..github/workflows/ci.ymlkeeps running the generator under the frozen-lockfile precondition and fails on a diff — PASS. Jobbuild-and-testrunspnpm install --frozen-lockfile(line 25), then step "Dependency disclosure is current" (lines 30–44) runsnode website/scripts/generate-deps.jsand doesgit diff --quiet -- website/src/data/ || exit 1.- The disclosure does not omit a shipped workspace's graph or exclude a shipped package — PASS. Derived from
pnpm-workspace.yaml's 10 packages againstproductDependencyFilters/excludedWorkspacePackagesinwebsite/scripts/generate-deps.js:dor→root,dormouse-lib(lib)→root,relay→root,dormouse-standalone(standalone)→root,dormouse-sidecar(standalone/sidecar)→root,dormouse(vscode-ext)→root,canopy→excluded,dormouse-website(website)→excluded,dor-lib-commonandremote-lib-common→reachable workspace edges (not named directly, per the spec's own note).assertWorkspaceCoverage(website/scripts/dependency-workspaces.js) enforces this and did not throw during the generator run (an "Unclassified workspace packages" or "Excluded workspace ... is reachable" error would have aborted it with exit code 1).npx vitest run scripts/dependency-workspaces.test.js(run fromwebsite/): 1 file passed, 6/6 tests passed.
Bundled runtime
- Root
package.jsonhasdevEngines.runtime.versionas an exactMAJOR.MINOR.PATCH— PASS.{"runtime":{"name":"node","version":"24.18.0","onFail":"download"}}. standalone/src-tauri/build.rsruns--versionon the binary and fails the build on mismatch, with the one permitted cross-arch skip — PASS.verify_node_version(lines 191–227) runsnode_source --version, compares toread_pinned_node_version's parse ofpackage.json, and returnsErron mismatch; itOk-returns with acargo:warningonly whenhost != target(lines 197–205), matching the allowed skip.release.yml's standalone matrix is entirely host-native (no skip is exercised in the release build) — PASS. All threebuild-standalonematrix entries pair native host/target:ubuntu-22.04→x86_64-unknown-linux-gnu,macos-latest→aarch64-apple-darwin,windows-latest→x86_64-pc-windows-msvc(.github/workflows/release.ymllines 25–36).build-standaloneinstalls the pinned runtime vianode-version-file: package.json, andpackage.jsonhas novolta.nodeorengines.node— PASS..github/workflows/release.ymlline 48 (and lines 193, 324 for the other jobs) usenode-version-file: package.json;grep -n "volta\|\"engines\"" package.jsonreturned nothing.
Cooldown and alerts
pnpm-workspace.yamlhasminimumReleaseAge: 1440— PASS (confirmed present at end of file)..github/renovate.jsonhasnpmandcargoinenabledManagers, withminimumReleaseAgepackage rules for both — PASS."enabledManagers": ["github-actions", "npm", "cargo"];packageRulesincludematchUpdateTypes: patch/minor/majorentries scoped to["npm", "cargo"]at 1/3/14 days respectively..github/renovate.jsonhas avulnerabilityAlertsblock withminimumReleaseAgeset explicitly — PASS."vulnerabilityAlerts": {"enabled": true, "schedule": [], "minimumReleaseAge": "1 day", "draftPR": false, "labels": [...]}, with an explanatory comment array reiterating why omission would silently drop the cooldown.- Secret scanning, push protection, and Dependabot alerts are enabled — PASS (all three clauses individually verified via
gh apiwithGH_TOKEN=$AUDIT_PAT, since the default token got 403):repos/diffplug/dormouse --jq .security_and_analysis→"secret_scanning":{"status":"enabled"},"secret_scanning_push_protection":{"status":"enabled"};GET /repos/diffplug/dormouse/vulnerability-alerts→HTTP/2.0 204 No Content(204, not 404, meaning Dependabot alerts are on).
Qualitative findings
INFO — Newly added/upgraded runtime dependencies since the last successful audit (2026-09-08T08:52 UTC, run 34206861498; the next scheduled run started in parallel with this one). Six Renovate PRs merged same-day: tailwind-variants v3.3.1 (#594), playwright-core v1.63.0 (#593), @tauri-apps/plugin-updater v2.11.0 (#592), hono v4.13.7 (#591), @types/react-dom v19.2.7 (#590), @tauri-apps/plugin-shell v2.3.6 (#589). Checked each: tailwind-variants, @tauri-apps/plugin-updater, hono, @tauri-apps/plugin-shell are production dependencies and each commit carries a matching website/src/data/dependencies-npm.json update (verified via git show --stat); playwright-core (vscode-ext/package.json devDependencies) and @types/react-dom (four packages, all devDependencies) correctly carry no disclosure-file change since they never reach a user's machine. All six are ordinary version bumps (no dependency additions/removals), all pnpm-managed and already reflected in the clean disclosure regeneration above. No BLOCKER or WARNING.
INFO — Lockfile resolutions outside the registry: exactly one, @diffplug/xterm-addon-webgl-sdf at canopy/package.json, resolved as a GitHub Releases tarball (https://github.com/diffplug/xterm.js/releases/download/sdf-v0.20.0-sdf304.0/...tgz) with an integrity: sha512-... pin in pnpm-lock.yaml. canopy is one of the two workspaces the spec explicitly excludes as a root ("Storybook-only rendering lab no shipped build imports"), and this is exactly the fork dependency AGENTS.md's Architecture section documents by name. Confirmed no such non-registry resolution is reachable from any of the six product roots: all 55 disclosed npm entries have ordinary semver-looking version fields (no http/github substrings), and standalone/src-tauri/Cargo.lock shows every one of its 491 non-root packages sourced from registry+https://github.com/rust-lang/crates.io-index (the only package with no source line is the local dormouse crate itself). No BLOCKER or WARNING.
INFO — Install scripts in production dependencies: pnpm-workspace.yaml's allowBuilds allowlist (@swc/core: true, esbuild: true, node-pty: true, sharp: true, and explicit denials @vscode/vsce-sign: false, keytar: false) is being enforced, not just declared. node_modules/.modules.yaml after a frozen-lockfile install shows "ignoredBuilds": [] and "pendingBuilds": [] — no package with a build/install script is sitting untriaged, which is what would happen if a newly bumped dependency introduced one Renovate hadn't been taught about. Both keytar@7.9.0 and @vscode/vsce-sign@2.0.9 are present in the lockfile (transitive, packaging-tool-only) and correctly build-denied rather than absent. No BLOCKER or WARNING.
INFO — Reachable-but-undisclosed check: no gap found. dor-lib-common and remote-lib-common (workspace edges, not named roots) are exercised by the coverage assertion and its pinned test; pnpm-workspace.yaml was not touched since the last audit, so no new workspace package needs classification.
No BLOCKER or WARNING findings. All FAIL IF checks in scope were determined (none UNVERIFIABLE).
CI and secrets
VERDICT: PASS
FAIL IF results
docs/specs/security.md
- Private vulnerability reporting enabled — PASS.
gh api repos/diffplug/dormouse/private-vulnerability-reporting→{"enabled":true}.
docs/specs/security-ci.md — GitHub Actions Policies
pull_request_targetappears only intend-*.yaml— PASS. Repo-wide grep found exactly one hit:.github/workflows/tend-review.yaml:11. No other.github/workflows/**file uses it.- Non-agent-managed workflow effective write permissions restricted to the named exceptions — PASS.
release.yml: top-levelcontents: read;build-standalone/build-vscodejobs add onlyid-token: write+attestations: write(the named exception);security-auditjob adds onlyactions: write(the named exception);publish-vscodehas no job-level block, inheritingcontents: read.ci.ymlandchromatic.ymleach declare onlycontents: read, no job overrides.
Automated Maintainer (tend)
scripts/workflow-audit.test.mjspins the tend-regen materialization boundary (mode-100644/100755-only, symlink rejection, no unrelated generated files) — PASS.node scripts/workflow-audit.test.mjs: 6/6 tests pass, including "never materializes an unrelated symlink" and "rejects symlink workflow inputs before running the generator."workflow-audit.yaml's lower bound is the previous successful run's server-setcreated_at, never pusher-controlled — PASS. Read.github/workflows/workflow-audit.yaml:SINCE=$(gh api .../workflows/workflow-audit.yaml/runs?status=success...--jq '.workflow_runs[0].created_at // ""'), falling back only to a fixed25 hours agoif no prior run exists. No--sincederived from commit data.- Admin-gating rulesets — PASS, both clauses independently verified via
GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/rulesets/16757376and/16757382:Merge access(16757376):target: branch,conditions.ref_name.include: ["~DEFAULT_BRANCH"],rules: [{type: update}],bypass_actors: [{actor_id: 5, actor_type: RepositoryRole}]— exact match.Tag operations(16757382):target: tag,conditions.ref_name.include: ["~ALL"],rules: [{type: creation}, {type: update}],bypass_actors: [{actor_id: 5, actor_type: RepositoryRole}]— exact match.
dormouse-botholds neithermaintainnoradmin— PASS.GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/collaborators/dormouse-bot/permission→permission: "write",role_name: "write",permissions: {admin: false, maintain: false, push: true, ...}.- Every environment's deployment-branch-policy admits only refs admin-gated by the two rulesets — PASS, checked all four environments individually:
release-attest:v*tag only (Tag operations).security-audit:mainbranch +v*tag (Merge access + Tag operations).tend:mainonly (Merge access).vscode-extension-publish:v*tag only (Tag operations).
All match the spec's "Today:" list exactly.
- Secret inventory placement — PASS, verified with one
gh apicall per surface:- Repo-level (
actions/secrets): onlyCHROMATIC_PROJECT_TOKEN. - Org-level (
actions/organization-secrets):total_count: 0. security-auditenv secrets:AUDIT_PAT,CLAUDE_CODE_OAUTH_TOKEN— exact match.tendenv secrets:TEND_BOT_TOKEN,CLAUDE_CODE_OAUTH_TOKEN— exact match.vscode-extension-publishenv secrets:OVSX_PAT,VSCE_PAT— exact match.release-attestenv:secrets: [],variables: []— empty as required.- No
ANTHROPIC_API_KEYvisible at repo or org level.
- Repo-level (
CHROMATIC_PROJECT_TOKENpresent in.config/tend.yamlsecrets.allowed— PASS. File contents:secrets:\n allowed:\n - CHROMATIC_PROJECT_TOKEN.workflow-audit.yamlnot missing/disabled, successful run within 48h — PASS.gh api .../actions/workflowsshows itactive; last 5 runs allsuccess, most recent2026-09-08T12:06:14Z(~21h before this audit at2026-09-09T09:00Z).- Every
tend-*.yamlpinsmax-sixty/tendat ≥0.1.19 — PASS. All 8 files pinmax-sixty/tend/claude@0.2.0and carry header# Generated by tend 0.2.0. - Unpinned action refs confined to
tend-*.yaml— PASS. Repo-wide grep for unpinned (@vN/tag-style)uses:lines returned matches only insidetend-ci-fix.yaml,tend-mention.yaml,tend-nightly.yaml,tend-notifications.yaml,tend-review-runs.yaml,tend-review.yaml,tend-triage.yaml,tend-weekly.yaml(actions/checkout@v7,astral-sh/setup-uv@v10.0.1). Every other workflow'suses:lines carry a commit-SHA pin with a version comment (spot-checkedrelease.yml,ci.yml,chromatic.yml,security-audit.yaml,workflow-audit.yaml). - Agent-managed workflows' effective
GITHUB_TOKENpermissions stay within the allowed set — PASS. Read every job-level (and workflow-level, where no job override exists)permissions:block insecurity-audit.yaml,workflow-audit.yaml, and all 8tend-*.yamlfiles: observed scopes are exactly{contents: write|read, pull-requests: write|read, actions: read, issues: write, id-token: write}— no scope outside the allowed list on any job. default_workflow_permissionsisreadandcan_approve_pull_request_reviewsisfalse— PASS.GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/actions/permissions/workflow→{"default_workflow_permissions":"read","can_approve_pull_request_reviews":false}.
VS Code Extension Releases
vscode-extension-publishenvironment protections — PASS.GH_TOKEN=$AUDIT_PAT gh api repos/diffplug/dormouse/environmentsshows nonemptyrequired_reviewers(2 named users),prevent_self_review: trueon that protection rule, and top-levelcan_admins_bypass: false.release.yml's VS Code publish job is bound tovscode-extension-publish, andVSCE_PAT/OVSX_PATappear nowhere else — PASS.publish-vscodejob declaresenvironment: {name: vscode-extension-publish}; repo-wide grep forVSCE_PAT|OVSX_PATunder.github/workflows/found both references only inside that job (lines 347, 359 ofrelease.yml).release.ymldoes not use production desktop signing secrets in CI and generates an ephemeral Tauri updater key — PASS.build-standalonerunstauri signer generate --ci --write-keys "$RUNNER_TEMP/tauri-ci-updater.key" --forceand exports it asTAURI_SIGNING_PRIVATE_KEYfor that job only; repo-wide grep forEV_SIGN_PIN/APPLE_SIGN_PASSunder.github/workflows/returned no hits, and the onlyTAURI_SIGNING_PRIVATE_KEYhit in workflows is this ephemeral-key assignment.
Desktop Releases
scripts/sign-and-deploy.shverifies GitHub artifact attestations, verifies SHA-256 manifests, and uses PIV-backed Windows signing — PASS.verify_downloaded_artifact()runsgh attestation verifywith--cert-identity/--cert-oidc-issuer/--source-ref/--source-digest, andcheck_sha256_manifest()runssha256sum -c/shasum -a 256 -c;sign_windows()callsjsign --storetype PIV --storepass env:EV_SIGN_PIN.TAURI_SIGNING_PRIVATE_KEYis env-only andEV_SIGN_PINis passed tojsign --storepassby environment reference, not literally — PASS.sign_updates()invokesTAURI_SIGNING_PRIVATE_KEY="$TAURI_SIGNING_PRIVATE_KEY" ... tauri signer sign "$bundle"(env prefix, never on thetauriargv), and bothjsigncalls use the literal stringenv:EV_SIGN_PINas the--storepassargument (jsign resolves it from the environment itself), never the PIN's value.APPLE_SIGN_PASSdoes travel onnotarytool's argv — this is the spec's documented, already-accepted known gap, not a new finding.
docs/specs/security-audit.md
- Every
docs/specs/security*.mdspec claimed by exactly one domain, no scope names a nonexistent file — PASS.node scripts/spec-lint.mjs→spec-lint: OK (33 specs, 65 files checked); its check 16 is exactly this rule, and it passed. Manually cross-checked the 6 non-rationalesecurity*.mdfiles against the three domain**Scopeblocks: each appears exactly once. - Dedicated
application-securitysubagent, not merged into another domain's context — PASS..github/audit/application-security.mdexists standalone, scoped only tosecurity-local.md/security-remote.md;.github/workflows/security-audit.yaml's--agentsJSON defines it as a separate agent fromci-and-secrets/supply-chain. application-securityon a stronger model than the mechanical domains, in both CI and local — PASS.security-audit.yaml: top-levelclaude_argssets--model sonnet, and the--agentsJSON overridesapplication-securityalone with"model":"opus".scripts/security-audit-local.sh:run_domain()setsmodel_args="--model sonnet"unconditionally then overrides to--model opusonly whendomain = application-security..github/audit/has every prompt file the workflow names, and the local runner uses the same files — PASS. All five files (_preamble.md,orchestrator.md,supply-chain.md,ci-and-secrets.md,application-security.md) exist;security-audit-local.shloops over exactly that filename set at startup and fails closed if any is missing; both CI and local prompts areRead ".github/audit/_preamble.md" and ".github/audit/<domain>.md"pointers, not inlined copies.- Union of qualitative scopes covers every top-level path — PASS.
ci-and-secretsowns.github/,.config/,.claude/,.vscode/,scripts/,website/public/, plus any secret-touching code;supply-chainowns the dependency graph, lockfile, and all ofwebsite/exceptwebsite/public/;application-securityis defined as the recursive subtraction remainder, covering everything else including.impeccable/and root files.ls -Aof the repo root shows nothing outside this union (node_modules/is gitignored and untracked, not a scope gap). .github/audit/and.vscode/both insideworkflow-audit.yaml's diff window — PASS.WINDOW=(.github/workflows/ .config/tend.yaml .github/audit/ .vscode/), andWINDOW_NON_WORKFLOW=("${WINDOW[@]:1}")is derived from it (not hand-duplicated), consumed identically by the commit list,own_changes(), and both classifiers' refusal checks.
Orchestration
orchestrator.mdrequires a non-turn-ending wait via a persisted-deadline Bashuntilloop, re-issued past the 10-minute cap, under a 25-minute deadline — PASS. §2 of.github/audit/orchestrator.mdspecifies exactly this, withDEADLINE_FILE="$RUNNER_TEMP/audit-deadline"persisted across re-issues.security-audit.yaml'stimeout-minutes: 40exceeds the orchestrator's 25-minute wait deadline — PASS. Confirmed in the workflow file with an explanatory comment tying the two together.- Orchestrator cannot report PASS while a subagent left no fragment — PASS. §4 of
orchestrator.md: "If no subagent returned FAIL but any domain returned INCONCLUSIVE, or a fragment is missing, empty, or has no exact verdict line, write no status file at all," landing on MISSING → INCONCLUSIVE, not PASS.security-audit.yaml's reporting step independently re-checks fragment presence and verdict-line exactness regardless of what the orchestrator wrote (defense in depth), and both paths exit non-zero.
Outcomes and reporting
- Only literal
PASS/FAILhonored, three real outcomes — PASS.case "${STATUS:-}" in PASS|FAIL) ;; *) STATUS=MISSING ;; esacinsecurity-audit.yaml. audit-report.mdwritten beforeaudit-status.txt— PASS.orchestrator.md§4 states this explicitly and orders the instructions accordingly.- Redact-secrets step covers every published sink and fails closed — PASS. Read the step: it processes
$TRANSCRIPT,audit-report.md, and$AUDIT_FRAGMENTS(the three per-domain files, sourced from the job-levelAUDIT_FRAGMENTSenv var so the redactor and the reporting-step guard loop can't drift), and on any Node error runsrm -fon that exact same file set before exiting 1.node scripts/security-audit.test.mjs→ 19/19 pass, including "redaction covers every published sink" and "redactor failure removes every published sink." - Reporting step writes one note per condition, not per combination — PASS. Five independent
if [ -n "${X:-}" ]; then echo "..." >> "$NOTES"; fiblocks (DISSENTING,MISSING_FRAGMENTS,UNREADABLE_VERDICTS,INCONCLUSIVE_DOMAINS,STATUS_FILE_VERDICT), each appending independently. - Both fragment guards run unconditionally, not gated on status — PASS. Both
for f in $AUDIT_FRAGMENTSloops run unconditionally before the single escalation block; comments in the file explicitly flag this ("Both loops now run UNCONDITIONALLY"). - Reporting step accepts only exact
VERDICT: PASS, recognizesVERDICT: FAILas dissent (with or without suffix), flagsVERDICT: INCONCLUSIVE, and treats anything else as unreadable;STATUSonly literalPASS/FAIL— PASS, confirmed by direct read of thecasestatements plusnode scripts/security-audit.test.mjs's "reporting" test group (10/10 pass, covering exact-match, prefix, whitespace, and combination cases). - Redaction/reporting regression suite — PASS.
node scripts/security-audit.test.mjs: 19/19 tests pass (0 failing).
Environment and AUDIT_PAT
security-audit.yamldispatch is a full release gate (dispatch + watch +needs:) — PASS.release.yml'ssecurity-auditjob runsgh workflow run "$workflow" ..., thengh run watch "$run_id" ... --exit-status;publish-vscodedeclaresneeds: [build-standalone, build-vscode, security-audit].Verify AUDIT_PAT is provisionedstep present, after checkout/install, before the audit step — PASS. Confirmed at that exact position insecurity-audit.yaml, checking[ -n "$AUDIT_PAT" ]and failing closed (writesFAILstatus, exits 1) otherwise.security-audit.yamlitself active and producing successful runs — PASS.gh api .../actions/workflowsshowsstate: active; last several scheduled runs allconclusion: success(onein_progressat check time, which is this run).
Qualitative findings
No BLOCKER or WARNING findings. Scope swept: .github/ (all workflows, .github/audit/), .config/tend.yaml, .claude/ (settings.json, skills, output-styles, commands), .vscode/ (tasks.json, launch.json), scripts/ (spot-checked every script that touches process.env/secrets.), and website/public/ (the Tauri updater manifest and static assets).
.vscode/tasks.jsonhas one task (build-dormouse-vscode, a plainpnpm build:vscodeshell command) and carries no"runOn": "folderOpen"or any other checkout-triggered automation — matchesdocs/specs/security-ci.md's statement that no such task exists today. INFO: worth re-checking on every future edit to this file, since addingrunOnhere is exactly the persistence class this domain watches for and it is not caught by any lint — only by this qualitative pass..claude/settings.jsonpermission allowlist is narrow and read/build-oriented (agent-browser,npx tsc,magick identify,pnpm test/filtered variants, a preview MCP tool) — nothing that reaches a secret or performs a network write..claude/skills/*and.claude/commands/release-notes.mdare prose/process guidance for the tend bot and release workflow; nothing executes with elevated privilege from these files themselves, and none references a secret value.website/public/standalone-latest.json(the Tauri updater manifest) contains only public update-signature material (Tauri's public-key-verifiable minisign-style signatures, not secrets) and public download URLs — consistent withdocs/specs/security-ci.md-> "Desktop Releases": the manifest is generated locally bysign-and-deploy.sh'ssign_updates()from the (locally-held) private key, and committing the public signature is expected.- Scripts touching
process.env/secrets.outside.github/workflows/(scripts/csp-defaults.mjs,scripts/dogfood-vscode.mjs,scripts/free-dev-port.mjs,scripts/pairing-walkthrough/*.mjs) read only non-secret config/dev-environment variables (ports, CSP policy toggles, dev-harness flags) — no credential material observed. - No org-level secret is shared with this repo (confirmed via API, not just spec text), so the "Org-level secrets" re-evaluation obligation in
docs/specs/security-ci.mdhas nothing pending. - All redaction/reporting/local-runner/workflow-audit regression suites (
scripts/security-audit.test.mjs,scripts/workflow-audit.test.mjs,scripts/clamp-issue-body-selftest.mjs,scripts/spec-lint.mjs) ran clean in this session, giving mechanical (not just textual) confirmation for the checks they pin.
UNVERIFIABLE: none. Every FAIL IF in scope was independently determined via gh api (using AUDIT_PAT where admin scope was required), direct file reads, or running the pinned test scripts.
Application security
VERDICT: FAIL
Domain: application-security. Scope: docs/specs/security-local.md (17 FAIL IF
bullets) and docs/specs/security-remote.md (50 bullets), plus the catch-all
remainder. Run at bb5ff06 on main.
One FAIL IF clause is violated — docs/specs/security-local.md -> "Terminal
output", the bounded-and-control-stripped rule, for CwdState.host. Every other
clause in both specs PASSES. No BLOCKER-rated qualitative finding.
FAIL IF results
Lint gates, run at HEAD: spec-lint OK (33 specs), loopback-lint OK (3
listeners, 1 allowlisted), e2e-lint OK (13 rules, 1366 checks),
e2e-lint-selftest OK (20 load-bearing checks), deploy-lint OK (3 installers,
31 rules, 81 checks), deploy-lint-selftest OK (112 load-bearing checks),
installer-verify-test OK (62 checks), ps1-cmdlet-lint OK (674 calls),
ps1-cmdlet-lint-selftest OK (4 checks). Test suites: remote-lib-common 241
pass, relay 275 pass, lib remote subtree 585 pass (31 files), vscode-ext
171 pass.
docs/specs/security-local.md -> Terminal output (3 bullets)
-
L37
isKnownUnsupportedIterm2Oscstill consumesOSC 52— PASS.
lib/src/lib/terminal-protocol.ts:708-717returns true for52/52;(and
50/50;); reached fromparseOscat:294, which returns[]so the
sequence never re-entersvisibleData(:223-228).52and50are also in
OSC_CONSUMED_IDS(:93), so an unterminated one is buffered, not forwarded. -
L37 every parse site runs
TerminalProtocolParserbeforepty:dataleaves it
— PASS, 4 emitters, all covered. VS Code:
vscode-ext/src/message-router.ts:231-234routes every rawonDatainto
getOwnerPtyStream(id).write(data); the onlypty:datapost is:532, fed
fromcreateProcessedPtyStream.onChunk(:288-291). Standalone:
standalone/sidecar/main.js:46withholds thedataevent, which reaches the
wire only vialib/src/host/remote/sidecar-entry.ts:256-266->
ownerStream(id).parsed.write(chunk)->:167; a throw in the tap drops the
chunk rather than emitting it raw (:41-45).lib/src/lib/platform/fake-adapter.ts:456-470
parses beforedataHandlers.vscode-ext/src/processed-pty-streams.ts
refuses a second parser. The three webview-sidenew TerminalProtocolParser
are the one-shot replay parsers the spec sanctions. -
L38
TITLE_LIMITbounds every retained title — PASS.
lib/src/lib/terminal-protocol.ts:79(256), applied:333,:387,:696,:793. -
L38
BODY_LIMITbounds every retained body — PASS.:80(4096), applied
:321,:334,:388. -
L38
sanitizeTextstrips controls before storage — PASS.:821-828,
[\x00-\x1f\x7f-\x9f]+-> space, thentruncateText(:834-837) counting
code points, so no split surrogate; empty reduces tonull. -
L38
COMMAND_LINE_LIMITbinds after the\xNNunescape, 4x before it —
PASS.:614-617
sanitizeText(decodeOsc633Value(truncateText(rawCommand, COMMAND_LINE_LIMIT * 4)), COMMAND_LINE_LIMIT);
COMMAND_LINE_LIMIT = 2048at:86. -
L38
MAX_CWD_LENGTH/boundedCwdValuebound and strip every CWD source —
PASS forpathanduri.lib/src/lib/terminal-state.ts:722(4096) and
:738-743; applied incwdFromOsc7(:266,:275),cwdFromOsc9_9(:290),
cwdFromDecodedPath(:679, servingosc633/osc1337/process/manual). -
L38 no retained value stops being bounded and control-stripped —
FAIL, one value:CwdState.host. See BLOCKER-adjacent detail below.cwdFromOsc7(lib/src/lib/terminal-state.ts:263-286) bounds and strips
rawUriat:265and re-bounds the path after its percent-decode at:274
— but:275computeshost = extractFileUriHost(rawUri) || parsed.hostname,
andextractFileUriHost(:704-708) regex-slices[^/]*out of the URI and
thensafeDecodeURIComponents it. The percent-decode re-introduces control
characters after the only strip, and nothing re-sanitizes. Reproduced
against the shipped predicates:
file://ok.example?a%1Bb%5D0;PWNED%07yields
host: "ok.example?ab]0;PWNED",isRemote: true. The URL parser
rejects controls inside the host, but the?/#tail is outside the host and
the regex swallows it to end-of-string.Retained and rendered:
cwd.hostis spliced into pane-header labels at
terminal-state.ts:340and:369and into grouping labels at:850, and is
a component ofcwdIdentity(:326). It is also persisted — the whole
CwdStateis written to the notepad archive on Surface closure and read back
with a type check only (lib/src/lib/notepad/archive-model.ts:137-140, no
length bound, no control strip).Blast radius is bounded: React renders these as text nodes, so there is no
markup execution and no escape re-injection into a PTY, and the value does not
cross a boundary —dor lssendsstate.cwd?.path(lib/src/components/Wall.tsx:1085)
and the remote directory sendspane.cwd?.path
(lib/src/remote/burrow/directory.ts:33), neitherhost. So this is header
and archive-record spoofing/corruption, not code execution. It is nonetheless
a violation of the rule as written, and the code is internally inconsistent:
the siblingpathon the same line is re-bounded after its decode. One-line
fix: wrapextractFileUriHost's return inboundedCwdValue. -
L39
OSC 8activation cannot reachopenExternalwithout the dialog —
PASS.lib/src/lib/terminal-lifecycle.ts:210-217calls
event.preventDefault()thenrequestExternalLinkConfirmation; the only
consumer of that store islib/src/components/ExternalLinkModalHost.tsx:24-28.
The other twoopenExternalcallers are app chrome
(lib/src/components/Wall.tsx:1623,lib/src/components/ExternalTextLink.tsx:19). -
L39 the dialog renders no open action for a deceptive verdict — PASS,
all three render branches.lib/src/components/ExternalLinkModal.tsx:109-156:
isDeceptive(:61) is tested first, so an openable-and-deceptive target
takes the deceptive arm (Close+ copy only,:111-126); branch 2
(:128-146) is the soleonConfirmwiring; branch 3 (:147-156) isClose
alone. Initial focus is the copy button when deceptive (:81). -
L39 the host also rejects a deceptive confirmation — PASS.
ExternalLinkModalHost.tsx:25-27requires
status === 'openable' && verdict !== 'deceptive'. -
L39 second pass through
normalizeExternalUriat every adapter — PASS.
vscode-ext/src/message-router.ts:636-638,standalone/src/tauri-adapter.ts:419-421,
standalone/src/browser-sidecar-adapter.ts:229-231.
docs/specs/security-local.md -> Browser panes (2 bullets)
Truncated to fit: the full body is 100009 characters. The untruncated audit-report.md is in this run's audit-transcript artifact (download).
- Ngôn ngữ chính
- TypeScript
- Star
- 5
- Fork
- 1
- Merge trung bình
- 17 giờ 45 phút
- Pull request đã merge (30 ngày)
- 235
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của diffplug/dormouse
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
-
security-audit-failure
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 25/100
Tất cả issue của diffplug/dormouse
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
bug v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
modelcontextprotocol/inspector#2458 · 1 bình luận ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 75/100
railmapgen/rmp-gallery#4068 ·
-
Mend: dependency security vulnerability status: needs triage 🕵️♀️
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
carbon-design-system/ibm-products#9907 ·