| name | security-scan-repository |
| description | Scan an entire repository for security issues and malicious code — leaked secrets, injection and code-execution flaws, supply chain and CI/CD risks, insecure configuration, and malicious instructions hidden in AI rule files. Use when the user wants a security scan or audit of the whole repository as it stands, rather than of a single diff. |
| targets | ["*"] |
scope = $ARGUMENTS
If scope is not provided, scan the whole repository. If scope is a path, restrict the scan to it and say so in the report.
Overview
Audit the current state of the entire repository, not a diff. When the question is instead "what changed since a given tag or commit, and is any of it malicious?", scope the scan to that diff rather than running this skill over everything.
The scan covers both vulnerabilities (unsafe code the maintainer wrote) and malicious code (backdoors, exfiltration, tampered dependencies, poisoned agent instructions).
The scan does not change the repository: do not switch branches, modify files, or send repository contents anywhere, and treat the Follow-up actions below as the only steps that write anything — they run only when the user asks for them. Step 2 is the exception worth naming: it executes tooling the audited repository provides, so read that tooling's definition first.
Steps
-
Map the repository before delegating anything.
git ls-files is the source of truth for what to scan. Do not review node_modules/, dist/, or other ignored build output as source — the two deliberate exceptions are the installed manifests the supply chain reviewer reads for lifecycle scripts, and untracked .env files. Treat any tracked build artifact, minified bundle, or binary as suspicious in itself and route it to the supply chain reviewer.
- Record: languages and file counts, package manager and lockfiles, application entry points, network/filesystem/shell boundaries, CI workflows, container and IaC files, and AI agent instruction files (
.claude/, .cursor/, .github/copilot-instructions.md, AGENTS.md, .rulesync/, skills/).
- Build an explicit file list per domain in step 3. Subagents waste effort rediscovering the layout.
-
Run the automated tooling the repository already provides, in parallel with reading the code.
- Secret scanning:
pnpm secretlint (or the project's equivalent) when configured; gitleaks detect when the binary is available.
- Dependency advisories:
pnpm audit / npm audit / pip-audit / cargo audit, whichever matches the lockfile.
gh api repos/{owner}/{repo}/code-scanning/alerts and .../dependabot/alerts when the repo has them enabled.
- Read a
package.json script (and the config it loads) before invoking it — pnpm <script> runs code the audited repository chose. When the repository is not one the user controls, invoke the tool binary directly instead of the script alias.
- Do not install new global tooling. When a tool is unavailable, say so in the report's coverage section instead of silently skipping it.
- Automated output is input to the review, not the review itself: confirm every reported hit against the actual file before it becomes a finding.
-
Call security-reviewer subagents in parallel, one per domain, passing each its file list from step 1. Split a domain across several subagents when its file list is large — split on module or subsystem boundaries, not on a raw file count, so that one reviewer still sees a whole data-flow path — and keep each subagent under roughly 40 files.
-
Secrets and credentials — hardcoded API keys, tokens, private keys, connection strings; credentials in test fixtures or committed configs; untracked .env files, which git ls-files does not list — extract their key names without materializing the values (cut -d= -f1 .env), and never quote an .env value, not even the prefix the redaction rule below otherwise allows; secrets surviving in git history (git log --all --oneline -S'<fragment>' to locate the commits, then inspect those commits — search a short distinctive fragment, never the full credential, and single-quote it: the fragment is repository content, so double quotes would still expand $(...) and backticks); values written to logs or error messages.
-
Application source — command injection, SQL/NoSQL injection, XSS and template injection, path traversal, SSRF, unsafe deserialization, prototype pollution, eval/Function/dynamic require, weak or misused crypto and randomness, missing authentication or authorization checks, insecure defaults, unvalidated redirects, race conditions on privileged operations.
-
Supply chain and dependencies — typosquatted or recently-hijacked packages, dependencies pulled from non-registry URLs or forks, lockfile entries whose resolved host disagrees with the declared registry, postinstall/preinstall lifecycle scripts — read the installed package.json manifests under node_modules/ for these, since a malicious script with no published advisory shows up in neither the lockfile nor audit — unpinned or mutable action and image references, tracked build artifacts that no source file explains.
-
CI/CD and automation — script injection via ${{ github.event.* }} expanded into run:, dangerous pull_request_target and workflow_run usage, over-broad permissions and token scopes, secrets exposed to fork-triggered jobs or echoed into logs, third-party actions pinned by tag instead of SHA, self-hosted runner exposure, release/publish workflows that can be triggered by outsiders.
-
Consolidate before reporting.
- Merge duplicates that several subagents found, and drop findings the code does not actually support.
- Read the cited code yourself for every high and critical finding. Report it only if the exploit path is real — reachable from an entry point with attacker-controlled input. Downgrade or drop the rest.
- Note whether a finding is exploitable in production, only in tests or local tooling, or theoretical.
- Assign a severity — low, mid, high, or critical — and a sequential number (#1, #2, ...) to each finding.
-
Produce a unified report:
## Repository Security Scan Report: <repo> (<scope>)
### Conclusion
- Overall verdict, and explicitly whether any malicious code was detected
### Coverage
| Domain | Files Reviewed | Tools Run | Not Covered |
|--------|----------------|-----------|-------------|
| ... | ... | ... | ... |
### Findings
| # | Severity | Description | File:Line | Impact | Exploitable |
|---|----------|-------------|-----------|--------|-------------|
| ... | ... | ... | ... | ... | ... |
### Recommendations
- Concrete fix for each finding, highest severity first
### Positive Observations
- Security practices the repository already gets right
Always state what the dependency tree review did and did not cover: node_modules/ is read only for lifecycle scripts in installed manifests, so a payload hidden anywhere else in a dependency is reachable only through the lockfile and advisory checks.
The findings table follows the same rule as the subagents: reference a secret by file:line and name, never by value.
State the limits of the scan honestly: a clean report means nothing suspicious was found in what was reviewed, not that the repository is proven safe.
Follow-up
Offer, without doing it unprompted: file the findings with the create-issue skill, or fix them and open a PR with the commit-push-pr skill. Never paste a discovered secret into an issue, PR, or commit message — reference it by file:line and tell the user to rotate it.