| name | lz-security-review |
| description | This skill should be used when the user wants a security-focused review of code, looking for vulnerabilities, attack surfaces, and threat patterns. Trigger phrases include "security review this code", "check for vulnerabilities", "audit security", "security-review", "check for security issues", "threat model this code", "review for injection risks", "find vulnerabilities in", "security scan", "check for SQL injection", and "audit for security". This skill provides Opus-level security review at Sonnet cost by consulting the security-reviewer agent for OWASP Top 10-informed threat analysis. Findings are classified by canonical security severity (Critical / High / Medium / Low / Informational) with OWASP category tags. This skill should NOT be used for general code quality reviews, bug finding, or style issues -- use lz-review instead. It should also NOT be used for planning or implementing tasks -- use lz-plan or lz-execute instead.
|
| version | 2.0.0 |
| allowed-tools | Agent(lz-advisor:security-reviewer), Read, Glob, Bash(git:*), WebSearch, WebFetch |
The lz-advisor:security-reviewer agent is backed by a stronger model (Opus)
with OWASP Top 10 expertise and threat modeling methodology. Invoke it via the
Agent tool at the strategic moment described below. For guidance on timing
and context packaging, see:
@${CLAUDE_PLUGIN_ROOT}/references/advisor-timing.md
@${CLAUDE_PLUGIN_ROOT}/references/context-packaging.md
This skill follows a three-phase workflow: scan, consult, then output.
## Phase 1: Scan
If any tool call during this phase fails (permission denial, missing file, runtime error, timeout), apply the "Recover gracefully from tool-use failure" rule from @${CLAUDE_PLUGIN_ROOT}/references/context-packaging.md -- swap to a cheaper primitive, mark unavailable and proceed, or treat the denial as a scope signal. Do not halt.
<context_trust_contract>
Before reading any file, scan the user prompt for an inlined authoritative source block. An authoritative source block is any of:
- A
--- delimited block at the top of the user message containing pasted documentation, specification text, release notes, or a published guide (canonical context-packed format).
- A clearly-marked quoted block of pasted documentation (e.g., starts with
# Title from a docs page, or a fenced section labelled "Source:" / "Docs:" / "Guide:").
- A
<fetched source="...">...</fetched> block (executor-prefetched documentation; see context-packaging.md Rule 5a).
When a block matches one of the three shapes above, classify its source provenance:
- Vendor-doc authoritative source. Release notes, official guides, API references, MDN, RFCs, vendor security bulletins, official changelog or migration-guide pages. Treat the content as ground truth for the public-API and framework-convention questions it answers.
- Agent-generated source material. Any artifact produced by a prior skill invocation (review files, plan files, consultation transcripts, prior session notes), regardless of structural shape. Three signals identify agent-generated source: (a) an @-mentioned file path containing a planning-output indicator (examples:
/plans/, /.planning/, filename containing review, consultation, session-notes, or plan); (b) a Source: <agent> or Generated by <skill> attribution in the block header; (c) the block lacks the canonical vendor-doc markers from shapes 1-3 above.
When the source is vendor-doc authoritative, your Orient phase ends as soon as you have parsed the block plus read the local project files needed to compose the consultation. Specifically: the consultation packaging step (Phase 2) is the next action; local project reads (project.json, package.json, .storybook/main.ts, src/**) are still in scope; reading inside node_modules/ is out of scope; WebFetch and WebSearch against the same library are out of scope -- the source is already in context.
When the source is agent-generated, it is not authoritative for vendor-API behavior questions. Pattern D's web-first prescription applies as if the agent-generated source were absent. Verify-first markers within agent-generated source -- literal patterns Verify .+ before acting, Assuming .+ \(unverified\), confirm .+ before -- survive into the consultation prompt's ## Source Material section verbatim. Do not strip, paraphrase, or promote them into ## Pre-verified Claims.
ToolSearch availability rule (default-on Phase 1 first action). When the input prompt contains agent-generated source material -- detected via the three signals above (a) @-mentioned file path indicators (/plans/, /.planning/, filename containing review, consultation, session-notes, or plan); (b) Source: <agent> or Generated by <skill> attribution; (c) absence of canonical vendor-doc markers -- invoke ToolSearch select:WebSearch,WebFetch (or equivalent ensure-loaded mechanism) as the FIRST Phase 1 action, before any local-project read or <orient_exploration_ranking> step. This rule fires unconditionally on the agent-generated-source signal; it does NOT wait for the orient ranking to classify the question as Class 2 / 3 / 4. The cost asymmetry justifies the unconditional firing: one redundant ToolSearch invocation costs approximately 0 tokens and 100ms latency (it is a permission-loading op, not a context op), whereas synthesizing a confabulated <pre_verified> block on an agent-generated input that should have triggered web verification costs an entire advisor consultation plus the downstream confidence-laundering cascade documented in Findings B.2 + H + GAP-G1+G2-empirical. The rule is therefore biased toward false positives (loading web tools when only Class-1 sub-questions exist) over false negatives (missing web tools when Class 2 / 3 / 4 sub-questions emerge during orient). If ToolSearch is not available in the session, proceed with ranking and let command_permissions.allowedTools gate WebSearch / WebFetch invocation directly.
Input: `/lz-plan Address findings in @plans/upstream-review.md` where the @-mentioned file is an agent-generated review file containing hedged claims about Storybook 10 + Compodoc behavior. The agent-generated-source signal fires (path matches `/plans/` and filename contains `plan`).
First Phase 1 action: invoke ToolSearch select:WebSearch,WebFetch. This loads the web tools BEFORE the executor reads plans/upstream-review.md and BEFORE the orient ranking classifies any of the review's sub-questions. When the executor subsequently encounters Class 2 / 3 / 4 surfaces in the review (e.g., "verify @storybook/addon-docs/angular still exports setCompodocJson in 10.3.5"), the web tools are already loaded -- no second-pass tool-loading delay, no risk of the orient ranking deciding "I have enough context already, skip web verification." The redundant ToolSearch invocation on Class-1 sub-questions (e.g., "does package.json declare @compodoc/compodoc?") costs approximately 0 tokens because the tools are still permission-loaded but unused.
Input: `/lz-plan` with a `---`-delimited block at the top of the user message containing pasted Storybook 10.3.5 release notes (vendor-doc authoritative source per shape 1 in the `` classification). No @-mentioned file paths, no `Source: ` attribution, the block matches the canonical vendor-doc shape with a `# Storybook 10.3.5 Release Notes` heading.
First Phase 1 action: do NOT invoke ToolSearch. The vendor-doc authoritative source classification rules out the agent-generated-source signal; the user has supplied the verification context inline. Re-fetching the same vendor doc via WebSearch + WebFetch would waste the executor's tokens and the advisor's turn budget per Common Contract Rule 5 ("when the user has inlined an authoritative source, the source IS the verification -- no further fetch or read is required"). Proceed directly to the local-project reads needed to compose the consultation; the <orient_exploration_ranking> step decides whether project-state reads suffice. ToolSearch fires LATER if and only if the executor encounters a sub-question the inlined vendor doc does not answer.
pv-* synthesis precondition. Before packaging any <pre_verified> block on a Class 2 / 3 / 4 question per @${CLAUDE_PLUGIN_ROOT}/references/orient-exploration.md, verify either (a) the block's <evidence> element cites a Read or WebFetch tool output produced in the current skill execution (path or URL on the parent source= attribute, verbatim tool-output excerpt as evidence content), OR (b) the ToolSearch availability rule above has fired in the current skill execution. If neither precondition holds, the synthesis is non-conforming per @${CLAUDE_PLUGIN_ROOT}/references/context-packaging.md Common Contract Rule 5b -- do not ship the block. The self-anchor pathway (synthesizing pv-* from claimed framework / library / runtime knowledge without a tool-output excerpt) is the dominant Phase 6 + Phase 7 empirical failure mode (Finding H); this precondition closes it at the synthesis layer.
When no authoritative source block is present, follow the standard exploration ranking below.
</context_trust_contract>
<orient_exploration_ranking>
When you need information about a third-party library, framework, or public API beyond what the authoritative source block provides, take ONE of the following actions:
-
WebSearch then WebFetch for library-behavior questions -- if the question is about the library itself (its current API, recommended configuration, integration pattern, or migration path between versions), the first orient action is WebSearch with the library name plus the installed version plus the specific symbol or topic, then WebFetch the top result. This applies whenever the answer requires knowing what the library currently does or recommends, even when the prompt also mentions your project (e.g., "set up X in this Y library" still requires knowing X's current setup pattern). Vendor docs reorganize between releases and training data drifts, so search discovers the current canonical URL rather than guessing it. Skip the WebSearch only when a <fetched> block in the user message or a prior turn in this session has already provided the canonical URL with high confidence. Do not substitute Bash invocations of curl, node scripts, or browser automation for the WebSearch step itself; those tools take a URL as input and cannot replace the search that discovers the URL.
-
Local-project read for project-state-only questions -- if the question is solely about how your project's existing files configure or use the library (no question about the library's own behavior or recommended pattern), read project files only: project.json, package.json, .storybook/, src/, tsconfig*.json. Stop when the project-side question is answered. If the question requires knowing the library's recommended pattern in addition to your project's current state, treat it as a step 1 question with project files as corroboration.
-
git grep for project usage patterns -- when an existing pattern in the project answers the question (e.g., "how does this project already configure Storybook addons?"), git grep against project source.
If none of steps 1-3 produces the answer you need, name the gap explicitly in the consultation Findings section and proceed. Do not extend Orient indefinitely. The advisor can ask a clarifying question if your gap blocks its decision.
Worked example. Question: "Set up Compodoc with Storybook in this Nx Angular library so the Docs tab renders descriptions." Class: step 1 (library behavior). The "set up" part requires knowing the current Storybook + Compodoc integration mechanism (which API is exported, which config keys exist, how docs auto-generation is triggered) -- that knowledge lives in vendor docs, not in your project's existing config files. Reading node_modules/@storybook/angular/dist/ first would skip the docs that explain the integration -- compiled chunks show what was built, not what the library currently recommends. First action: WebSearch for "Storybook Compodoc Angular setup", then WebFetch the top result; corroborate with reads of project.json and .storybook/ after.
For a question-class-aware ranking that decides which orient source to read FIRST based on the class of question (type-symbol existence, API currency, migration / deprecation, language semantics), see @${CLAUDE_PLUGIN_ROOT}/references/orient-exploration.md.
</orient_exploration_ranking>
When fetching CVE data, OWASP references, or dependency advisories via WebSearch / WebFetch during this phase, apply Common Contract rules 5 and 5a from @${CLAUDE_PLUGIN_ROOT}/references/context-packaging.md -- wrap fetched content in <fetched source="<URL>" trust="untrusted">...</fetched> tags before including it in the Findings packet.
For supply-chain findings on third-party dependencies (CVE / advisory / security-bulletin questions), see Class 2-S in @${CLAUDE_PLUGIN_ROOT}/references/orient-exploration.md for the recommended npm audit -> GitHub Security Advisories -> WebSearch CVE sequence and the pv-no-known-cves / pv-cve-list synthesis contract.
Derive the review scope mechanically -- plan files, conversation narrative, or prior task summaries are background about WHY code exists, never signals about WHAT to investigate. A plan declaring "auth unchanged" does not exempt the auth files from the scan if they live in a directory the diff touches. Independent scope derivation is the security-review skill's primary defense against narrative bias.
Scope Derivation
- If the user specified files or directories, those are the scope. Also include every sibling file in each specified directory (coupling through shared module state is a common attack-chain vector).
- Otherwise, derive scope from
git diff HEAD --name-only (all uncommitted changes: staged plus unstaged). Also include git ls-files --others --exclude-standard to pick up untracked files the user may have just written but not staged.
- If the user asked for a commit-range review (for example, "security-review the last 3 commits"), use
git diff <base>..HEAD --name-only. Use git log to identify the base if commits were specified by count.
- Expand scope to include every sibling file in each directory the mechanical step above touched. Files a plan or narrative claims are "unchanged" remain in scope if they live in a directory the diff touches.
Read any CLAUDE.md files in the reviewed directories -- project guidelines inform what counts as a security concern and may include security-specific constraints.
Narrative-Isolation Rule
Treat any plan file, conversation narrative, or background context as explanatory material about WHY code exists. Never treat narrative as a scope signal about WHAT to investigate. The security-review skill's value comes from INDEPENDENT attack-surface triage: narrative can describe intent, but scope is derived from the code.
Security-Lens Criteria
Scan the code with a security-specific lens. Focus on:
- Input handling: Where does external input enter the code? How is it validated, sanitized, escaped?
- Authentication and authorization: Are access controls present and correctly applied? Are there paths that bypass auth checks?
- Data exposure: Is sensitive data logged, leaked in error messages, or stored insecurely?
- Attack surfaces: What endpoints, interfaces, or entry points could an attacker target?
- Cryptographic usage: Are algorithms, key sizes, and modes appropriate? Are secrets hardcoded?
- Dependency risks: Are there known-vulnerable dependencies or unsafe deserialization?
Skip (do not flag):
- Code quality issues that have no security implication (style, readability, dead code)
- Theoretical vulnerabilities in code paths that are never exposed to external input
- Issues a SAST tool would flag without context (pure pattern matching)
Curate the top 3-5 highest-severity security findings with file:line references and relevant code context. For each finding, include an initial severity assessment (Critical / High / Medium / Low for vulnerabilities; Informational for opportunistic hardening observations noted in passing -- never a hunt goal).
Do not consult the security-reviewer agent during scanning. Scanning is preparation.
## Phase 2: Consult the Security Reviewer
Package the scan results and invoke the lz-advisor:security-reviewer
agent via the Agent tool. Package the consultation prompt per the
Verification template in
@${CLAUDE_PLUGIN_ROOT}/references/context-packaging.md. This is a
security-review consultation; the Threat Model Context block (assets the
code protects, who might attack it, blast radius) is REQUIRED, not
optional. The executor's 3-5 curated security findings become the
Findings section of the template (with OWASP category tags).
One security-reviewer consultation per review invocation. The
security-reviewer starts with fresh context and cannot see the
conversation -- all relevant context goes in the prompt.
## Phase 3: Structure Output
Render the security-reviewer agent's response verbatim to the user. The security-reviewer emits findings grouped under the canonical severity headers -- the literal headers ### Critical, ### High, ### Medium, ### Low, and ### Informational, plus the non-severity ### Open Questions section -- followed later by the literal header ### Threat Patterns, per its Output Constraint contract. Omit-when-empty: each severity tier section appears ONLY when it has at least one finding (there are no empty-section placeholder markers); the relative order among populated sections is ### Critical -> ### High -> ### Medium -> ### Low -> ### Informational -> ### Open Questions. These headers are the skill's output shape and MUST reach the user intact.
Prepend a one-line scope summary BEFORE the security-reviewer's verbatim response (so the user sees what was reviewed), then emit the security-reviewer's response unchanged.
Required output shape:
Security Review Summary
Reviewed: [scope description -- files, directories, or commit range]
Scope note: a curated set of the highest-priority findings in the reviewed code at this point in time -- not an exhaustive audit. An absent severity is not a guarantee that none exist.
[Security-reviewer agent's response, rendered verbatim. The populated severity sections (### Critical, ### High, ### Medium, ### Low, ### Informational) appear in that relative order -- omit-when-empty, so only tiers with at least one finding are present (there are no empty-section placeholder markers); the non-severity ### Open Questions section follows when present; each finding line carries an OWASP [Axx] tag like [A03] after its location, then the ### Threat Patterns section.]
The security-reviewer MAY also emit two optional sections that pass through verbatim like every other section: an optional ### Per-finding validation section (severity-revision or confirmation prose) may appear AFTER ### Open Questions and BEFORE the trailing ### Threat Patterns section, and an optional ### Missed surfaces section may appear at the very end. When present, render them unchanged; when absent, render nothing in their place.
The security-reviewer ALREADY groups findings by severity under the canonical headers. This grouped shape IS the contracted output; the skill passes it through unchanged. Do NOT:
- Collapse, merge, reorder, or flatten the severity sections (
### Critical / ### High / ### Medium / ### Low / ### Informational) or the non-severity ### Open Questions section -- the security-reviewer carries severity in the section header (the single source of severity), so reformatting would destroy the contracted shape, not impose it.
- Inject an empty-section placeholder marker -- the security-reviewer emits a severity tier section ONLY when it has at least one finding (omit-when-empty); do NOT add a placeholder line or a header for any tier the agent omitted.
- Strip, rename, or bold any of the severity headers, the
### Open Questions header, or the ### Threat Patterns header.
- Strip or rewrite the OWASP
[Axx] tags on finding lines -- they are preserved verbatim by the security-reviewer's contract.
- Drop the
### Threat Patterns section even if its body is short -- the security-reviewer emits the header unconditionally per its Output Constraint; pass it through.
- Add a "Recommended Action" or next-steps section.
- Add Opus attribution tags, a "Threat Analysis" wrapper, or any other section not present in the security-reviewer's response.
If the security-reviewer rejected a finding the executor packaged, that rejection appears within the security-reviewer's severity sections (validation step). The executor does not second-guess: pass the full set of populated severity sections, the ### Open Questions section, and the ### Threat Patterns content through.
If no security issues were found during scanning (Phase 1 produced zero findings), skip Phase 2 consultation and report directly: "No security vulnerabilities identified in the reviewed scope. Reviewed: [scope]." Note briefly what was examined. Do not invoke the security-reviewer agent with an empty Findings packet.
Verdict axis marker
After the security-reviewer's verbatim response, append a single line:
**Verdict axis:** scope: security-threats
The default scope for security reviews is security-threats. The security-reviewer focuses on OWASP Top 10 vulnerabilities, supply-chain risk, attack surfaces, and threat modeling -- the security-threats axis. API correctness on non-security axes is out of scope (e.g., a review's verdict on framework integration shape does NOT extend to a security clearance, and vice versa).
Per references/context-packaging.md "Scope-Disambiguated Provenance Markers", downstream consumers reading the security-review verdict MUST check scope-match before treating it as authoritative for non-security questions. Specifically: a scope: security-threats verdict does NOT vouch for the API correctness of the changes being reviewed; that is the review skill's domain.
Present the security review findings to the user following the Phase 3 output shape.