security-audit
wlsdks/ontology-atlas/.agents/skills/security-audit/SKILL.md
Periodic security sweep of Atlas in four areas (MCP and CLI, the Tauri desktop shell, the web renderer and connectors, the supply chain and CI) that follows untrusted input to what it can reach, proves each reach with a planted input, and turns every confirmed finding into a fix slice with a regression test. Not for a single diff, which takes the reviewer's security lens.
Skill139 starsChanged 4 days ago
---
name: security-audit
description: Periodic security sweep of Atlas in four areas (MCP and CLI, the Tauri desktop shell, the web renderer and connectors, the supply chain and CI) that follows untrusted input to what it can reach, proves each reach with a planted input, and turns every confirmed finding into a fix slice with a regression test. Not for a single diff, which takes the reviewer's security lens.
---
# Security audit
`SECURITY.md` is the public threat model. `.claude/rules/local-first.md`,
`.claude/rules/forbidden.md` ("Data and security") and
`.claude/rules/surfaces.md` (the bridge roster and the one agent-config
exception) are the boundaries. The audit finds where untrusted input crosses
them. It never reads credentials or scans anything outside this repository.
## 0. Run the floor
pnpm test:security
pnpm desktop:check
pnpm audit --prod --audit-level=high
pnpm --dir mcp audit --prod --audit-level=high
cargo audit --file src-tauri/Cargo.lock # when installed; the release workflow also runs it
A red line is the first finding. A green floor proves only what those gates
cover.
## 1. One reviewer per area
Give each area to its own read-only reviewer agent, briefed with
`.agents/agents/reviewer.md`, in parallel. Each gets
its area below, this skill, `SECURITY.md`, and the report shape in §3, and
builds its inventory with commands, never from memory.
- **MCP and CLI.** Input: tool arguments an agent sends after reading injected
text, vault and repository file content, `absorb_document` input. Reach: a
path outside the granted root (`..`, a symlink, an absolute path), a process
or shell string, a write without approval or an `expected_mtime` guard, the
network. Inventory: every path-typed tool argument, and every `spawn`,
`exec`, `fetch`, `realpath` and `resolve` call under `mcp/src` and `cli/src`.
- **Tauri desktop.** Input: anything the WebView can send to a command, deep
links, the update feed. Reach: a command acting past the vault, a capability
grant, the CSP, a secret leaving Rust, an unsigned update. Inventory: every
`#[tauri::command]` in `src-tauri/src`, the capability files, and
`src-tauri/tauri.conf.json`.
- **Web renderer and connectors.** Input: vault Markdown, agent and ACP
output, connector config, wiki pages. Reach: injected HTML, a `javascript:`
or `file:` URL in a link or `window.open`, the external-link door, an env or
header value leaving connector discovery. Inventory: every
`dangerouslySetInnerHTML`, `react-markdown`, `window.open` and computed
`href` under `src` and `app`.
- **Supply chain and CI.** Input: a fork's pull request, a new dependency or
action, an install script, a downloaded runtime. Reach: a secret in a
pull-request workflow, `pull_request_target`, an untrusted `${{ }}` in a
`run:` block, an unpinned action or runtime, the updater signing key.
Inventory: `.github/workflows`, the lockfiles' diff since the last audit,
and `pnpm.onlyBuiltDependencies` in each `package.json`.
## 2. Prove before reporting
A finding is a reach shown with a planted hostile input — a vault file with a
`javascript:` link, a `../` or symlinked path argument, a pull request title
holding `$(…)` — in a scratch copy or as a failing test, never by reading
alone. A suspicion without that proof goes in a separate list with the
cheapest proof that would settle it.
## 3. Report
Verdict first: the count of confirmed findings and the most severe one in one
line. Then one row per finding, most severe first:
| # | Area | Input → reach | Guard today | Proof (command and output) | Severity | Fix slice |
|---|---|---|---|---|---|---|
Severity is reach times precondition: content-borne input that reaches code
execution, a secret, or a write outside the vault is critical; anything that
needs the person's own deliberate action is low.
## 4. Close the loop
- Each confirmed finding becomes a slice: a planned slice, an implementing
agent, then `/review-and-land` with the `security` lens. The fix ships with its
planted input as a regression test, proven red on the unfixed code
(`/gate-probe`), and that test joins `pnpm test:security`.
- A class of finding that could recur (a new raw-HTML sink, an unguarded path
argument) becomes a gate only after `/gate-probe`'s inventory.
- A risk accepted rather than fixed is a decision record
(`pnpm record:new -- --kind=decision`) with its falsifier; a change to the
public threat model updates `SECURITY.md` in the same pull request.
- After the fixes land, rerun §0 and every planted input from this audit, and
report each as blocked or still reached.
- A gate that should have caught a finding is a `/harness-retro` lesson.
## Never
- Describe an unfixed, exploitable finding in a public issue, pull request, or
commit before the fixed build ships. Ask the owner about a private GitHub
security advisory; a fix pull request names the defect class, not the recipe.
- Read credential files, or scan outside this repository, to prove a point.
- Trust a gate because it is green.
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

