Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1e3c952
security(audit): own the repository by subtraction, not by a list
nedtwigg Aug 26, 2026
0badaad
security(release): keep the Tauri updater key off argv
nedtwigg Aug 26, 2026
a855460
security: enable secret scanning and push protection, and let Renovat…
nedtwigg Aug 26, 2026
e66feee
security: fix three claims the audit proved wrong
nedtwigg Aug 26, 2026
e7dc783
security(workflow-audit): make the widened window reach every consumer
nedtwigg Aug 26, 2026
47b1779
security: derive the classifier window, and record that tend shipped …
nedtwigg Aug 26, 2026
38f4e20
chore(tend): regenerate at 0.1.19
nedtwigg Aug 27, 2026
b7393d9
security(tend): record the instruction-file gap as closed, and pin th…
nedtwigg Aug 27, 2026
905b7b5
security(audit): run application-security on Opus, and make a domain'…
nedtwigg Aug 27, 2026
0dfa1e3
security(audit): fold in what #449, #450 and #451 found
nedtwigg Aug 27, 2026
b150870
security(audit): pin both sides of the local model split
nedtwigg Aug 27, 2026
6a208e3
security(standalone): make session snapshots owner-only
nedtwigg Aug 27, 2026
f6530bc
security(remote): bound pending pairings, and make user verification …
nedtwigg Aug 27, 2026
c0af617
security(remote): evict the pairing record, not just its payload
nedtwigg Aug 27, 2026
62848c7
security(audit): give the unreadable-verdict case its own issue prose
nedtwigg Aug 27, 2026
47d01c9
security(audit): let a dissent outrank a missing fragment
nedtwigg Aug 27, 2026
60ebf67
security(audit): report conditions, not combinations
nedtwigg Aug 27, 2026
4353ec7
security(audit): separate recording a condition from deciding the ver…
nedtwigg Aug 27, 2026
e040281
security(audit): drop a FAIL IF sentence describing code that no long…
nedtwigg Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/audit/_preamble.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ for a check you could not determine — a transient network error, or an area yo
ran out of room to reach — and say which it was. It is never a substitute for a
check you could have run.

Write your findings to the file named in your own prompt, with two sections:
Write your findings to the file named in your own prompt. **Its very first
line must be literally `VERDICT: PASS` or `VERDICT: FAIL`** — nothing else on
that line. The reporting step greps for it, so it is the one part of your
report a machine reads: a `FAIL` there cannot be lost in a merge, and it is
what stops an optimistic summary from overriding you. Then two sections:
`### FAIL IF results` (one line per check) and `### Qualitative findings`
(severity-tagged). **Write that file before you return** — your caller reads
the file, not your reply, and a fragment that does not exist fails the whole
Expand Down
32 changes: 25 additions & 7 deletions .github/audit/application-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,28 @@ gap is known (revocation, the audit trail, the two `workflow-audit` window
evasions), do not re-report it as a finding — report only if the situation has
changed or is worse than described.

You also own the **rest of the repository** qualitatively, so that no top-level
path is outside every domain: `lib/`, `server/`, `server-lib-common/`,
`standalone/`, `vscode-ext/`, `dor/`, `dor-lib-common/`, `canopy/`, `deploy/`,
`docs/`, and the root files. Remote control is where the depth goes; the rest
is a sweep for anything that would be a security hole in a terminal that runs
local shells — command construction, path handling, deserialization of
persisted state, IPC that crosses a trust boundary.
You are also the **catch-all** domain, and this is defined by subtraction, not
by a list: you own everything in the repository that `supply-chain.md` and
`ci-and-secrets.md` do not explicitly claim. Run `ls -A` and work out the
remainder rather than trusting any enumeration — an enumeration goes stale the
moment someone adds a directory, which is exactly how `.vscode/` and
`.impeccable/` ended up owned by nobody.

Subtraction is **recursive, not top-level**. Where another domain claims a
subdirectory rather than a whole tree, the rest of that tree is yours — so
check one level down wherever a claim is partial, or the same orphaning
happens inside a directory instead of beside it. `website/` is *not* an
example of this any more: `supply-chain` claims all of it except
`website/public/`, so none of it is yours. That was fixed by stating the claim
as a subtraction rather than as two named subdirectories, which is the shape
to prefer when you find the next one.

Today the remainder is `lib/`, `server/`, `server-lib-common/`, `standalone/`,
`vscode-ext/`, `dor/`, `dor-lib-common/`, `canopy/`, `deploy/`, `docs/`,
`.impeccable/`, and the root files — but treat that as a description of the
current tree, not as your scope. Your scope is the remainder.

Remote control is where the depth goes; the rest is a sweep for anything that
would be a security hole in a terminal that runs local shells — command
construction, path handling, deserialization of persisted state, IPC that
crosses a trust boundary.
14 changes: 11 additions & 3 deletions .github/audit/ci-and-secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ write scopes.
## Qualitative pass

You own `.github/` (including `.github/audit/`, which holds this audit's own
prompts), `.config/`, `.claude/`, `scripts/`, and `website/public/` — the Tauri
updater manifest shipped apps fetch lives there, so it is a release artifact
rather than marketing. You also own any code anywhere that touches a secret.
prompts), `.config/`, `.claude/`, `.vscode/`, `scripts/`, and
`website/public/` — the Tauri updater manifest shipped apps fetch lives there,
so it is a release artifact rather than marketing. You also own any code
anywhere that touches a secret.

`.vscode/` is here rather than with the product code because it is
configuration that can execute: a `tasks.json` entry with
`"runOn": "folderOpen"` runs on checkout when a maintainer opens the folder,
which is the same shape of persistence `workflow-audit.yaml` watches workflows
for. There is no such task today; the point is that adding one should be a
finding, not a quiet config change.
18 changes: 13 additions & 5 deletions .github/audit/orchestrator.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,21 @@ adversarially — and one context holding all three degrades the third.
## 1. Spawn all three

Spawn them with the Task tool **in a single message** so they run
concurrently. Give each subagent, verbatim:
concurrently, using these three `subagent_type` values:

- the shared preamble in `.github/audit/_preamble.md`, then
- its own file: `.github/audit/supply-chain.md`,
`.github/audit/ci-and-secrets.md`, `.github/audit/application-security.md`.
- `supply-chain`
- `ci-and-secrets`
- `application-security`

Read all four files before you spawn anything.
Each is already defined with the prompt it needs — pointing at
`.github/audit/_preamble.md` plus its own domain file — and with the model it
should run on. `application-security` is deliberately on a stronger model than
the other two; do not override it, and do not paste prompt text into the Task
call. A one-line instruction such as "begin your audit" is enough, because the
agent definition carries the rest.

Do not read the domain files yourself. They are long, you are not auditing,
and holding all three in your context is the thing this split exists to avoid.

## 2. Wait without ending your turn

Expand Down
8 changes: 7 additions & 1 deletion .github/audit/supply-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ enumeration is the shortcut that goes stale.

## Qualitative pass

You own the dependency graph, the lockfile, and `website/src/`. Look at:
You own the dependency graph, the lockfile, and **all of `website/` except
`website/public/`**, which is `ci-and-secrets`' because the Tauri updater
manifest lives there. So `website/src/`, `website/scripts/`, and the build
config (`package.json`, `vite.config.ts`, `react-router.config.ts`,
`tsconfig.json`) are all yours. `generate-deps.js` is in that set: audit the
whole generator, not just the `productDependencyFilters` array the
root-completeness bullet names.

- newly added or upgraded runtime dependencies since the last audit
- anything in the lockfile that resolves outside the registry
Expand Down
12 changes: 12 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
"dependencyDashboard": true,
"timezone": "America/Los_Angeles",
"schedule": ["* * * * 1"],
"vulnerabilityAlerts": {
"description": [
"Security fixes must not wait for the Monday window. Everything else here is batched to one day a week, which is right for routine bumps and wrong for a known-vulnerable dependency, so this overrides the schedule only.",
"`minimumReleaseAge` is set here EXPLICITLY, and must stay that way. Renovate\u0027s own default for this block is `minimumReleaseAge: null`, force-applied before lookup — so omitting it does not inherit the cooldown from packageRules, it DROPS the cooldown entirely. Stating it is the only way to keep it.",
"Keeping it is the deliberate choice: the cooldown guards the opposite threat — a compromised release that gets yanked within a day — and a reviewer reading a Renovate diff cannot detect a supply-chain compromise the way the ecosystem\u0027s own yank process can. Nothing here auto-merges, and the Dependabot alert already makes the vulnerability visible the moment it is published, so what the cooldown costs is a day before the remediation PR appears, not a day before anyone knows.",
"See SECURITY.md, Dependency Supply Chain."
],
"enabled": true,
"schedule": [],
"minimumReleaseAge": "1 day",
"labels": ["security", "dependencies"]
},
"packageRules": [
{
"matchManagers": ["npm", "cargo"],
Expand Down
Loading