Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions plugins/claude-code/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "foxguard",
"description": "Fast local security scanning in Claude Code: auto-scan edited files, slash commands for full scans, diff scans, post-quantum audits, and TUI triage. Powered by the foxguard Rust scanner — 170+ rules across 10 languages with cross-file taint tracking.",
"version": "0.1.0",
"author": {
"name": "PwnKit Labs",
"url": "https://foxguard.dev"
},
"homepage": "https://foxguard.dev",
"repository": "https://github.com/PwnKit-Labs/foxguard",
"license": "MIT"
}
106 changes: 106 additions & 0 deletions plugins/claude-code/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# foxguard for Claude Code

Live security scanning inside [Claude Code](https://code.claude.com). Every file Claude writes or edits is auto-scanned by [foxguard](https://foxguard.dev) — findings are fed straight back so Claude self-corrects before bad patterns land.

## What you get

- **PostToolUse auto-scan** — every `Write` / `Edit` / `MultiEdit` triggers `foxguard --format json` on the changed file. Medium+ findings are surfaced to Claude on stderr; clean files are silent.
- **SessionStart preamble** — Claude starts each session with foxguard's secure-coding defaults already in context.
- **Slash commands** for on-demand scans:
- `/foxguard:setup` — verify install, set severity threshold
- `/foxguard:scan [path]` — full scan, grouped and triaged by severity
- `/foxguard:diff-scan [base]` — only what this branch introduces vs `main`
- `/foxguard:pq-audit [path]` — post-quantum readiness with CNSA 2.0 deadlines
- `/foxguard:secrets [path]` — hardcoded secrets, tokens, private keys
- `/foxguard:triage [args]` — instructions for the interactive TUI
- **`secure-coding` skill** — model-invoked remediation guidance for command exec, SQL, path traversal, SSRF, secrets, randomness, crypto, and deserialization.

## Install

### 1. Install foxguard

Pick whichever fits:

```sh
# Prebuilt binary — fastest
curl -fsSL https://foxguard.dev/install.sh | sh

# Homebrew
brew install pwnkit-labs/foxguard/foxguard

# npm (global or zero-install)
npm i -g foxguard # or just: npx foxguard

# cargo
cargo install foxguard
```

Verify with `foxguard --version`.

### 2. Install the plugin

Until the plugin is published to a marketplace, load it directly from this repo:

```sh
claude --plugin-dir /path/to/foxguard/plugins/claude-code
```

Or add to your settings to load it permanently. Once installed, run `/foxguard:setup` to confirm it's wired up.

## Configuration

Environment variables:

| Var | Default | Purpose |
| :-- | :-- | :-- |
| `FOXGUARD_HOOK_SEVERITY` | `medium` | Minimum severity for the auto-scan. One of `low|medium|high|critical`. |

Comment on lines +54 to +57
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix markdownlint MD056: table column count.

Your configuration table around lines 54-57 is triggering markdownlint-cli2 MD056 (“Expected: 3; Actual: 6”). Adjust the table to unambiguously define 3 columns in the header/separator, and avoid any accidental extra | characters.

Example normalization
-| Var | Default | Purpose |
-| :-- | :-- | :-- |
+| Var | Default | Purpose |
+| --- | --- | --- |
 | `FOXGUARD_HOOK_SEVERITY` | `medium` | Minimum severity for the auto-scan. One of `low|medium|high|critical`. |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Var | Default | Purpose |
| :-- | :-- | :-- |
| `FOXGUARD_HOOK_SEVERITY` | `medium` | Minimum severity for the auto-scan. One of `low|medium|high|critical`. |
| Var | Default | Purpose |
| --- | --- | --- |
| `FOXGUARD_HOOK_SEVERITY` | `medium` | Minimum severity for the auto-scan. One of `low\|medium\|high\|critical`. |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 56-56: Table column count
Expected: 3; Actual: 6; Too many cells, extra data will be missing

(MD056, table-column-count)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/claude-code/README.md` around lines 54 - 57, The markdown table
defining configuration (the row for FOXGUARD_HOOK_SEVERITY) is malformed and
triggers MD056 because the header/separator doesn't unambiguously define three
columns; open the table near the FOXGUARD_HOOK_SEVERITY row and fix the header
and separator to have exactly three columns (e.g., a single header row with
three cells and a matching separator like `| :-- | :-- | :-- |`), and remove any
accidental extra `|` characters inside cells so the FOXGUARD_HOOK_SEVERITY row
and its header align to three columns.

Set in your shell profile or in Claude Code's environment to tune noise.

## How the auto-scan looks

After Claude edits a file, if foxguard finds an issue, Claude sees something like:

```
foxguard found 1 issue(s) in src/auth.py (severity >= medium):

[CRITICAL] py/no-command-injection at line 42
os.system() called with dynamic argument — risk of command injection
CWE-78
> os.system("ls " + user_input)

Fix these before continuing. Run `/foxguard:scan` for the full repo or `/foxguard:triage` for the interactive TUI.
```
Comment on lines +64 to +73
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix markdownlint MD040: add a fenced-code language.

The example block under “How the auto-scan looks” starts with a bare triple-backtick (line 64 per hint). Specify a language (e.g. text) to satisfy MD040.

Suggested change
-```
+```text
 foxguard found 1 issue(s) in src/auth.py (severity >= medium):
 ...
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 64-64: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/claude-code/README.md` around lines 64 - 73, The fenced code block
under "How the auto-scan looks" in README.md is missing a language specifier
which triggers markdownlint MD040; update the opening triple-backtick to include
a language (for example change ``` to ```text) so the block is recognized as
text, leaving the block contents unchanged; locate the fenced block in the "How
the auto-scan looks" example and modify its opening delimiter accordingly.


Claude is expected to fix the finding (or explain why it's a false positive) before moving on.

## Layout

```
plugins/claude-code/
├── .claude-plugin/plugin.json # manifest
├── hooks/hooks.json # PostToolUse + SessionStart
├── scripts/
│ ├── scan-edited-file.sh # the PostToolUse scanner
│ └── secure-defaults.txt # SessionStart preamble
├── skills/
│ ├── setup/SKILL.md # /foxguard:setup
│ ├── scan/SKILL.md # /foxguard:scan
│ ├── diff-scan/SKILL.md # /foxguard:diff-scan
│ ├── pq-audit/SKILL.md # /foxguard:pq-audit
│ ├── secrets/SKILL.md # /foxguard:secrets
│ ├── triage/SKILL.md # /foxguard:triage
│ └── secure-coding/SKILL.md # model-invoked remediation guidance
└── README.md
```
Comment on lines +79 to +95
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix markdownlint MD040: add a fenced-code language.

The “Layout” directory tree block also uses a bare triple-backtick (line 79 per hint). Add a language identifier like text to clear MD040.

Suggested change
-```
+```text
 plugins/claude-code/
 ├── .claude-plugin/plugin.json     # manifest
 ...
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>

[warning] 79-79: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @plugins/claude-code/README.md around lines 79 - 95, The README's layout
directory tree code block uses a bare triple-backtick which triggers
markdownlint MD040; update the fenced code block around the
"plugins/claude-code/" tree (the Layout directory tree block) to include a
language identifier (e.g., change totext) so the block is fenced with a
language and MD040 is resolved.


</details>

<!-- fingerprinting:phantom:triton:puma:05f3783a-a64c-4a50-b1f4-3b1690357be0 -->

<!-- d98c2f50 -->

<!-- This is an auto-generated comment by CodeRabbit -->


## Notes

- The hook intentionally never blocks Claude on its own machinery: missing binary, parse errors, or unreadable inputs all exit `0`. Only real findings exit `2`.
- The hook calls `foxguard` from `PATH` first, then falls back to `npx --yes foxguard`. If neither is available it stays silent — run `/foxguard:setup` to fix that.
- `--severity medium` is the default cutoff. Drop to `low` for stricter coverage; raise to `high` for noisier projects.
- foxguard's exit codes: `0` clean, `1` findings, `2` error. The hook checks for findings via the JSON, not the exit code, so a piped error doesn't trigger a false alarm.

## License

MIT — same as foxguard.
25 changes: 25 additions & 0 deletions plugins/claude-code/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit|NotebookEdit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/scan-edited-file.sh"
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "cat \"${CLAUDE_PLUGIN_ROOT}/scripts/secure-defaults.txt\""
}
]
}
]
}
}
44 changes: 44 additions & 0 deletions plugins/claude-code/scripts/scan-edited-file.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# PostToolUse hook: scan the file Claude just wrote/edited and feed findings back.
#
# Reads the Claude Code hook input JSON from stdin, extracts tool_input.file_path,
# runs `foxguard --format json` on it, and emits a compact summary on stderr with
# exit 2 when medium+ findings exist so Claude treats it as actionable feedback.
# Stays silent (exit 0) on clean files, missing tools, or unreadable inputs — a
# scanner hook should never block Claude on its own machinery.

set -uo pipefail

input=$(cat)

file_path=$(printf '%s' "$input" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)

[ -z "$file_path" ] && exit 0
[ ! -f "$file_path" ] && exit 0

# Resolve foxguard binary: prefer PATH, fall back to npx.
if command -v foxguard >/dev/null 2>&1; then
fg=(foxguard)
elif command -v npx >/dev/null 2>&1; then
fg=(npx --yes foxguard)
else
exit 0
fi

min_severity="${FOXGUARD_HOOK_SEVERITY:-medium}"

findings=$("${fg[@]}" --format json --severity "$min_severity" "$file_path" 2>/dev/null) || true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent option-style filename bypass in scanner invocation

On Line 30, "$file_path" is passed as a positional argument without --. If the edited filename starts with -, the scanner may parse it as a flag instead of a path.

Suggested fix
-findings=$("${fg[@]}" --format json --severity "$min_severity" "$file_path" 2>/dev/null) || true
+findings=$("${fg[@]}" --format json --severity "$min_severity" -- "$file_path" 2>/dev/null) || true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
findings=$("${fg[@]}" --format json --severity "$min_severity" "$file_path" 2>/dev/null) || true
findings=$("${fg[@]}" --format json --severity "$min_severity" -- "$file_path" 2>/dev/null) || true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/claude-code/scripts/scan-edited-file.sh` at line 30, The scanner is
invoked as "${fg[@]}" --format json --severity "$min_severity" "$file_path"
which can misinterpret filenames beginning with '-' as options; update the
invocation that constructs findings to insert a standalone "--" before the
positional path (i.e., call "${fg[@]}" --format json --severity "$min_severity"
-- "$file_path") so the scanner treats file_path as a filename; ensure you
update the line that assigns findings and keep existing quoting and the fallback
"|| true".


[ -z "$findings" ] && exit 0
[ "$findings" = "[]" ] && exit 0

count=$(printf '%s' "$findings" | jq 'length' 2>/dev/null)
[ -z "$count" ] || [ "$count" = "0" ] && exit 0

{
printf 'foxguard found %s issue(s) in %s (severity >= %s):\n\n' "$count" "$file_path" "$min_severity"
printf '%s' "$findings" | jq -r '.[] | " [\(.severity | ascii_upcase)] \(.rule_id) at line \(.line)\n \(.description)\n \(.cwe // "")\n > \(.snippet)\n"'
printf '\nFix these before continuing. Run `/foxguard:scan` for the full repo or `/foxguard:triage` for the interactive TUI.\n'
} >&2

exit 2
10 changes: 10 additions & 0 deletions plugins/claude-code/scripts/secure-defaults.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
foxguard is active. Apply these secure-coding defaults when writing or editing code:

- Never concatenate user input into shell, SQL, or path strings. Use parameterized APIs (`subprocess` arg lists, prepared statements, `pathlib.Path` joins).
- Never hardcode secrets, tokens, or keys. Read from env vars or a secrets manager. Treat any string that looks like a credential as a finding.
- Validate and canonicalize paths before file ops; reject paths that escape the intended root after resolution.
- For HTTP fetchers, validate the destination host against an allowlist or block private IP ranges to avoid SSRF.
- Prefer post-quantum-ready primitives where the project already supports them; flag legacy RSA / ECDSA / ECDH usage in security-sensitive paths.
- Use `crypto.randomBytes` / `secrets` / `OsRng` for security-sensitive randomness — never `Math.random`, `rand()`, or `random.random()`.

After every Write/Edit, foxguard scans the touched file. If findings come back, fix them before continuing rather than working around them. Use `/foxguard:scan` for a full repo scan, `/foxguard:diff-scan` for changed-only review, `/foxguard:pq-audit` for post-quantum readiness, and `/foxguard:triage` for the interactive TUI.
12 changes: 12 additions & 0 deletions plugins/claude-code/skills/diff-scan/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
description: Scan only changes vs a base branch — surfaces what this branch introduced
disable-model-invocation: true
---

Run a diff scan to show only findings introduced on the current branch.

1. Determine the base branch. If the user passed one as `$ARGUMENTS`, use it. Otherwise default to `main`, falling back to `master` if `main` doesn't exist (`git rev-parse --verify main`).
2. Run via Bash: `foxguard diff "$BASE" . --format json --severity medium`.
3. Parse the JSON output the same way as `/foxguard:scan` — group by file and severity, show rule id, line, description, and snippet.
4. Specifically frame the report as "what this branch adds." If the count is zero, say so plainly — this branch introduces no new findings vs `$BASE`.
5. For critical/high findings, propose fixes. Make clear that pre-existing findings on `$BASE` are not shown — those need a full `/foxguard:scan`.
15 changes: 15 additions & 0 deletions plugins/claude-code/skills/pq-audit/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
description: Post-quantum cryptography audit — find legacy RSA/ECDSA/ECDH usage with CNSA 2.0 deadlines
disable-model-invocation: true
---

Run a post-quantum readiness audit.

1. Take an optional path from `$ARGUMENTS`, default to `.`.
2. Run via Bash: `foxguard pqc "$PATH_OR_DOT" --format json --severity medium`.
3. Parse the JSON. Each finding may include a `cnsa2Deadline` field — surface that prominently. CNSA 2.0 mandates deprecation of classical asymmetric crypto in security-sensitive paths by specific dates; findings nearer those deadlines are higher priority.
4. Report:
- Total PQ-vulnerable call sites
- Breakdown by primitive (RSA, ECDSA, ECDH, etc.) — derive from `rule_id` or `description`
- For each finding: `file:line`, the legacy primitive, the recommended PQ replacement (e.g., ML-KEM, ML-DSA, hybrid suites)
5. Note that classical primitives may still be acceptable in non-security paths (test fixtures, signed-binary verification of trusted releases). Ask the user about context before recommending wholesale rewrites.
15 changes: 15 additions & 0 deletions plugins/claude-code/skills/scan/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
description: Run a full foxguard security scan on the repository or a specific path
disable-model-invocation: true
---

Run a full foxguard scan and walk the user through any findings.

1. If the user passed a path as `$ARGUMENTS`, scan that. Otherwise scan `.`.
2. Run via Bash: `foxguard --format json --severity medium "$PATH_OR_DOT"`. Capture stdout — exit 1 just means findings were detected, that's expected.
3. Parse the JSON array. Group findings by `file`, then by `severity` (critical → high → medium). For each finding show: `[SEVERITY] rule_id` at `file:line` with the description and the `snippet`.
4. Summarize at the top: total count, breakdown by severity, top 3 files by finding count.
5. For each critical/high finding, propose a concrete fix the user can accept. Don't apply edits until the user confirms.
6. If the JSON output is large (>50 findings), suggest `/foxguard:triage` for the interactive TUI instead of scrolling text.

If the scan errors (exit 2), report the error verbatim and suggest `/foxguard:setup`.
16 changes: 16 additions & 0 deletions plugins/claude-code/skills/secrets/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Scan for hardcoded secrets, API tokens, and private keys
disable-model-invocation: true
---

Run a secrets-focused scan.

1. Take an optional path from `$ARGUMENTS`, default to `.`.
2. Run via Bash: `foxguard secrets "$PATH_OR_DOT" --format json`.
3. Parse the JSON. Secret values are redacted by foxguard — do not try to reconstruct them.
4. Report each finding: `file:line`, the `rule_id` (e.g., `secret/aws-access-key`, `secret/github-pat`), and the risk class (cloud key, VCS token, payment, generic high-entropy, private key, etc.).
5. For each finding propose the right remediation:
- Cloud / SaaS tokens: rotate immediately at the issuer, then move to env vars or a secrets manager.
- Private keys: regenerate, never just delete from history without rotation.
- High-entropy strings that are NOT secrets: add to a baseline via `foxguard secrets --write-baseline .foxguard-secrets-baseline.json` — explain this is a suppression, not a fix.
6. Remind the user that removing a leaked secret from the working tree does NOT remove it from git history. If the file was ever committed, the secret must be considered compromised.
52 changes: 52 additions & 0 deletions plugins/claude-code/skills/secure-coding/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
description: Apply foxguard's secure-coding patterns when writing or fixing security-sensitive code. Use proactively when handling user input, shelling out, building SQL, doing file I/O, fetching URLs, generating randomness, or touching crypto.
---

Use this guidance when writing or fixing code that touches any of these surfaces. Match the language patterns to what foxguard's rules look for so the auto-scan stays clean.

## Command execution

- Never pass concatenated strings to a shell. Use argument lists.
- Python: `subprocess.run(["git", "log", branch], check=True)` — never `subprocess.run(f"git log {branch}", shell=True)`.
- Node: `execFile("git", ["log", branch])` — never `exec(`git log ${branch}`)`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix malformed inline code span in Node example

On Line 11, nested backticks break Markdown rendering for the exec example.

Suggested fix
-  - Node: `execFile("git", ["log", branch])` — never `exec(`git log ${branch}`)`.
+  - Node: `execFile("git", ["log", branch])` — never `exec("git log " + branch)` (or template-string shell commands).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Node: `execFile("git", ["log", branch])` — never `exec(`git log ${branch}`)`.
- Node: `execFile("git", ["log", branch])` — never `exec("git log " + branch)` (or template-string shell commands).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/claude-code/skills/secure-coding/SKILL.md` at line 11, The inline
code span on Line 11 is malformed because nested backticks break Markdown
rendering around the Node example; update the text to avoid nested backticks by
using a single backtick for the surrounding inline code and escaping or
reformatting the inner example (e.g., use execFile("git", ["log", branch"])
instead of backticked exec(`git log ${branch}`) or present the inner command in
a code span block), targeting the phrase that references execFile and exec with
the git log ${branch} example so the Markdown renders correctly.

- Go: `exec.Command("git", "log", branch)` — never `exec.Command("sh", "-c", "git log "+branch)`.
- If a shell is genuinely required, validate the input against a strict allowlist regex first.

## SQL

- Use parameterized queries everywhere. `cursor.execute("SELECT * FROM u WHERE id = ?", (uid,))` — not f-strings or `%`.
- ORMs are fine; raw `query.format(...)` / `+` concat is not.

## Path traversal

- Resolve and bound paths: `Path(root).resolve() / Path(name).name` — strip directory components from untrusted names.
- Reject inputs that contain `..`, absolute paths, or null bytes before joining.

## SSRF

- For outbound HTTP from user-supplied URLs: parse the URL, resolve the host, and reject private/loopback/link-local ranges (`10.0.0.0/8`, `127.0.0.0/8`, `169.254.0.0/16`, `172.16.0.0/12`, `192.168.0.0/16`, `::1`, `fc00::/7`, `fe80::/10`) and metadata endpoints (`169.254.169.254`).
- Prefer an allowlist over a denylist when the destinations are known.

## Secrets

- Read from env / secrets manager, never from string literals. `os.getenv("API_TOKEN")` — not `API_TOKEN = "sk-..."`.
- Even in tests, prefer fixtures from env or a vault. High-entropy strings in code will be flagged.
- Never log secrets. Redact before logging.

## Randomness

- Security-sensitive: `secrets.token_urlsafe`, `crypto.randomBytes`, `OsRng`, `crypto/rand`. Never `random.random()`, `Math.random()`, `rand()`, `math/rand`.
- Non-security (jitter, sampling): the fast PRNGs are fine.

## Crypto

- New code: AES-GCM or ChaCha20-Poly1305 for symmetric; Ed25519 for signatures; X25519 for key agreement (until PQ rollout). Never DES, 3DES, RC4, MD5, SHA-1.
- Asymmetric crypto in long-lived security paths: prefer hybrid suites (X25519+ML-KEM) where the toolchain supports it. Use `/foxguard:pq-audit` to find legacy usage.
- Password hashing: argon2id or bcrypt. Never plain SHA-2 or unsalted hashes.

## Deserialization

- Never `pickle.load`, `yaml.load` (use `safe_load`), `Marshal.load`, or `unserialize` on untrusted input.
- For JSON, deserialize into a typed schema (pydantic, zod, serde) — don't trust the shape.

When you finish a fix, the PostToolUse hook will re-scan the file and confirm it's clean. If a finding persists, read the rule's description carefully — the remediation is usually one of the patterns above.
19 changes: 19 additions & 0 deletions plugins/claude-code/skills/setup/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
description: Verify foxguard is installed and ready for the Claude Code plugin
disable-model-invocation: true
---

Walk the user through getting foxguard installed and the plugin working:

1. Run `foxguard --version` via the Bash tool. If it succeeds, report the version and tell the user the plugin is ready — every Write/Edit will now be auto-scanned.
2. If `foxguard` is not on PATH, offer the install options in this order:
- **Prebuilt binary (fastest)**: `curl -fsSL https://foxguard.dev/install.sh | sh`
- **Homebrew** (macOS): `brew install pwnkit-labs/foxguard/foxguard`
- **npm**: `npm i -g foxguard` or zero-install via `npx foxguard`
- **cargo**: `cargo install foxguard`
Ask which the user prefers; do NOT install without confirmation.
3. Recommend the user run `foxguard init` inside their repo to add a pre-commit hook so foxguard also catches issues outside Claude Code sessions.
4. Mention the env vars they can tune:
- `FOXGUARD_HOOK_SEVERITY` — minimum severity for auto-scan (default `medium`; values: `low|medium|high|critical`)

Finish with a one-line confirmation of which version is active and which severity threshold the auto-scan is using.
16 changes: 16 additions & 0 deletions plugins/claude-code/skills/triage/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Open the foxguard interactive TUI for triaging findings
disable-model-invocation: true
---

Launch the foxguard TUI for interactive triage.

1. Tell the user the TUI is an interactive terminal app and Claude cannot drive it — they need to interact with it directly in their terminal.
2. Suggest they run one of these in their own terminal (NOT via your Bash tool, which can't render a TUI):
- `foxguard tui` — full repo
- `foxguard tui --changed` — staged/unstaged files only
- `foxguard tui --diff main` — diff vs main
- `foxguard tui --secrets` — secrets mode
- `foxguard tui --explain` — show dataflow traces in the detail pane
3. Pass through any path or flags from `$ARGUMENTS`.
4. After they finish triaging, offer to follow up with `/foxguard:scan` to re-verify the codebase is clean.