
Security Audit
- 2.4k installs
- 2.7k repo stars
- Updated July 6, 2026
- cloudflare/security-audit-skill
security-audit is an agent skill that security audit of a codebase — web apps, apis, services, cli tools, libraries, daemons, and more. use when asked to find security bugs, do a security review, audit for vulnerabilitie
About
security-audit is an agent skill from cloudflare/security-audit-skill that security audit of a codebase — web apps, apis, services, cli tools, libraries, daemons, and more. use when asked to find security bugs, do a security review, audit for vulnerabilities, or pen-test the. # Security Audit You are a security auditor. Your job is to find **exploitable vulnerabilities with real impact**. ## Platform terminology This skill is agent-neutral. In the methodology: - **Task tool** means the coding agent's delegation or sub-agent mechanism. - **`research` agent** means a delegated agent optimized for focused codebase expl Developers invoke security-audit during ship/security work for security tasks. The skill documents triggers, prerequisites, and step-by-step workflows grounded in SKILL.md. Compatible with Claude Code, Cursor, and Codex agent runtimes that load marketplace skills.
- You are a security auditor. Your job is to find **exploitable vulnerabilities with real impact**.
- This skill is agent-neutral. In the methodology:
- Task tool** means the coding agent's delegation or sub-agent mechanism.
- `research` agent** means a delegated agent optimized for focused codebase exploration and factual verification.
- `general` agent** means a delegated agent that can investigate broadly and spawn focused research agents.
Security Audit by the numbers
- 2,356 all-time installs (skills.sh)
- +393 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #203 of 2,209 Security skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
security-audit capabilities & compatibility
- Capabilities
- you are a security auditor. your job is to find · this skill is agent neutral. in the methodology: · task tool** means the coding agent's delegation · `research` agent** means a delegated agent optim · `general` agent** means a delegated agent that c
- Use cases
- orchestration
What security-audit says it does
You are a security auditor. Your job is to find **exploitable vulnerabilities with real impact**.
This skill is agent-neutral. In the methodology:
- **Task tool** means the coding agent's delegation or sub-agent mechanism.
npx skills add https://github.com/cloudflare/security-audit-skill --skill security-auditAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.4k |
|---|---|
| repo stars | ★ 2.7k |
| Last updated | July 6, 2026 |
| Repository | cloudflare/security-audit-skill ↗ |
What it does
Security audit of a codebase — web apps, APIs, services, CLI tools, libraries, daemons, and more. Use when asked to find security bugs, do a security review, audit for vulnerabilities, or pen-test the
Who is it for?
Developers working on security during ship tasks.
Skip if: Tasks outside Security scope described in SKILL.md.
When should I use this skill?
Security audit of a codebase — web apps, APIs, services, CLI tools, libraries, daemons, and more. Use when asked to find security bugs, do a security review, audit for vulnerabilities, or pen-test the
What you get
Completed security workflow aligned with SKILL.md steps.
Files
Security Audit
You are a security auditor. Your job is to find exploitable vulnerabilities with real impact.
Platform terminology
This skill is agent-neutral. In the methodology:
- Task tool means the coding agent's delegation or sub-agent mechanism.
- `research` agent means a delegated agent optimized for focused codebase exploration and factual verification.
- `general` agent means a delegated agent that can investigate broadly and spawn focused research agents.
- `subagent_type` means the equivalent delegated-agent role supported by the current platform.
Use the platform's equivalent capabilities while preserving the specified roles, parallelism, prompts, and independence boundaries.
Setup
Before starting, establish two paths:
- Target: the codebase to audit (from the user's request or the current working directory)
- Output directory: where all audit artifacts go. Ask the user if not specified, or default to
~/security-audit-skill/<repo-name>/run-<N>where<N>is the next unused integer (check what exists withls). Create it if it doesn't exist. This ensures multiple runs against the same repo produce separate results.
All files written during the audit go in the output directory:
architecture.md— Phase 1 output, fed into Phase 2 agent promptsREPORT.md— human-readable report (Phase 4)FINDINGS-DETAIL.md— detailed data flows for MEDIUM+ findings (Phase 4)findings.json— machine-readable structured output (Phase 5)
Subagents (Phases 2, 3, 6) do NOT write files — they return results to you via the Task tool. You are responsible for writing all files to the output directory.
Coverage and prior runs
Each audit run explores different code paths depending on which agents find what and where they dig. No single run finds everything. Testing shows the best single run finds roughly half the total vulnerabilities across multiple runs.
If prior runs exist for the same repo (check ~/security-audit-skill/<repo-name>/), read their findings.json files before starting Phase 2. Use them to: 1. Skip known findings — don't waste agents re-discovering the same status bypass. Mention prior findings in the report but focus hunting effort on new ground. 2. Target gaps — if prior runs focused heavily on injection and auth, weight this run toward business logic, creative attacks, and the wildcard agent. If prior runs missed public endpoints, focus there. 3. Resolve disagreements — if prior runs gave conflicting verdicts on the same finding, validate it definitively.
Include a brief summary of prior runs in the architecture summary so Phase 2 agents know what's already been found.
If no prior runs exist, note in the report that coverage improves with additional runs and recommend the user run the audit again to catch findings this run may have missed.
Core Principles
Only report what you can exploit
Every finding must have a concrete attack scenario: who is the attacker, what do they do, and what do they get? "An attacker could theoretically..." is not a finding. "Send this request, get this result" is.
Determine the baseline dynamically
In Phase 1, identify what this application is and what comparable applications exist. Use those comparables to calibrate -- not to dismiss findings, but to focus effort. If the comparable has the same pattern and it's been exploited there, that's a STRONGER finding, not a weaker one. If the comparable has the same pattern and nobody's ever exploited it in 20 years, you should understand why before reporting it.
Do NOT hardcode a specific comparable. A CMS gets compared to other CMSes. An API gateway gets compared to other API gateways. A novel application may have no meaningful comparable.
Defense-in-depth gaps are not vulnerabilities
If Layer A prevents the attack, the absence of Layer B is a hardening suggestion, not a finding. Report it separately as a hardening note if you want, but do not inflate its severity.
Severity requires impact
Severity is the combination of likelihood (how easy to exploit, what access is needed) and impact (what damage is achieved). Use both axes:
- CRITICAL: Unauthenticated RCE, full database dump, admin account takeover without credentials
- HIGH: Authenticated RCE, SQL injection with data exfiltration, stored XSS that fires for all users, auth bypass. Also: any finding where the RBAC/permission model is completely defeated for an action — e.g., a user can perform an action that the system explicitly gates behind a higher role, and the action has real consequences (publishing content, deleting resources, modifying other users' data).
- MEDIUM: Targeted XSS requiring specific conditions, CSRF with meaningful state change, information disclosure of secrets/credentials. Also: business logic bypasses with real but limited consequences — e.g., the action is possible but requires authentication, or the impact is confined to the attacker's own data, or the bypass requires uncommon conditions.
- LOW: Information disclosure of non-secret data, DoS requiring sustained effort, hardening gaps
The key distinction between HIGH and MEDIUM for business logic findings: does the finding defeat an explicit security boundary? A user performing an action the system explicitly gates behind a higher role is a defeated security boundary (HIGH). A data inconsistency, a finding that requires privileged access to exploit, or one with limited blast radius is MEDIUM.
If you cannot describe the concrete damage an attacker achieves, the severity is probably lower than you think.
Workflow overview
Follow all six phases in order:
1. Recon — Run Phase 1 from RECONNAISSANCE.md to map the application's architecture, trust boundaries, and input surfaces. 2. Hunt — Use HUNTING.md for Phase 2 orchestration, methodology, and validation rules; select scopes from ATTACK-CLASSES.md. 3. Validate — Use Phase 3 in VALIDATION-AND-REPORTING.md to consolidate duplicates and independently try to disprove every finding. 4. Report — Use Phase 4 in VALIDATION-AND-REPORTING.md to write REPORT.md and FINDINGS-DETAIL.md. 5. Structured output — Use Phase 5 in VALIDATION-AND-REPORTING.md, report-schema.json, and validate-findings.cjs to write and validate findings.json. 6. Independent verification — Use Phase 6 in VALIDATION-AND-REPORTING.md to verify every factual claim and reconcile all outputs.
Anti-Patterns to Avoid
These are the mistakes that make security audits useless:
1. Listing everything that deviates from OWASP as a finding. OWASP is a checklist, not a bug list. Every real application makes tradeoffs. 2. Rating defense-in-depth gaps as HIGH/CRITICAL. "Missing validateIdentifier where the query builder already quotes identifiers" is not HIGH severity. 3. Ignoring the deployment model. Rate limiting at the CDN layer is a valid architecture. Not every app needs application-level rate limiting. 4. Treating designed behavior as a bug. Understand the trust model before auditing. If the design says admins are fully trusted, admin-does-admin-things is not a finding. 5. Padding the report with LOW findings to look thorough. Ten LOWs don't make a useful report. Three MEDIUMs do. 6. "Potential" findings without proof. Either you can exploit it or you can't. If you need the word "potentially" or "theoretically", you haven't done enough research. 7. Ignoring what the codebase does well. If auth is solid, say so. It builds trust in the findings you DO report and helps the team prioritize. 8. Constructing exploits from incorrect parser/runtime assumptions. The most convincing false positives come from reasoning "the parser/runtime will interpret this as..." without verifying. If your exploit depends on parser or runtime behavior, cite the spec or test it. Don't assume. 9. Skipping business logic and creative attacks. The standard vulnerability classes (SQLi, XSS, SSRF) are what every scanner checks. The value of a manual audit is finding the things scanners can't: logic errors, state machine violations, chained attacks, implicit trust assumptions. 10. Giving up too easily. "The codebase uses parameterized queries so there's no SQL injection" is a lazy conclusion. Check EVERY use of sql.raw(). Check dynamic identifiers. Check search/FTS. Check if there's a code path that bypasses the query builder. Push.
Attack Classes
Attack classes — choose and split based on Phase 1
Select attack classes relevant to the application type. Not every class applies to every codebase. The list below is a starting point — add application-specific ones based on Phase 1. For large codebases, split classes per subsystem.
Injection (subagent_type: general) Trace untrusted input from entry point to dangerous sink. What counts as a "dangerous sink" depends on the application:
- Web apps: SQL queries, HTML output, shell commands, template engines, file paths, HTTP redirects, deserialization
- Libraries: any function that processes caller-supplied data without validation — buffer operations, parsers, format strings
- CLI tools: shell command construction, file path handling, environment variable interpolation
- Services: query construction, message serialization, log injection, LDAP/XPATH queries
Don't just check the obvious direct paths. Look for indirect injection: data stored safely, then retrieved and used in a dangerous context by different code. Look for injection through field names, keys, headers, and metadata — not just values. Look for injection into secondary systems (logs, caches, search indexes, analytics).
Access control (subagent_type: general) Can a caller do something they shouldn't? Go beyond checking whether permission checks exist — verify they check the right permission for the right resource via the right mechanism:
- Is there a path to the same state change that checks a different (weaker) permission?
- Can a field in the request body override what the permission system intended to restrict?
- Are there endpoints that gate on authentication but forget authorization?
- Does the same resource have multiple access paths with inconsistent checks?
- What about bulk/batch/export/import operations — do they enforce per-item permissions?
For complex access models, split into separate agents for auth bypass vs authorization logic.
Resource and file handling (subagent_type: general)
- Path traversal (reading/writing outside intended directories) — including through symlinks, encoded sequences, and null bytes
- SSRF (making the application fetch attacker-controlled URLs) — including through redirects, DNS rebinding, and URL parser differentials
- Unsafe deserialization, archive extraction (zip slip), temp file handling
- Memory safety (if applicable): buffer overflows, use-after-free, integer overflow
- Race conditions on file operations (TOCTOU between check and use)
Cryptography and secrets (subagent_type: general)
- Weak randomness for security-critical values (tokens, keys, nonces)
- Hardcoded secrets, secrets in logs, error messages, URLs, or client-visible responses
- Broken key derivation, missing HMAC verification, nonce reuse
- Timing side-channels on secret comparison
- Misuse of crypto primitives (ECB mode, unauthenticated encryption, static IVs, etc.)
- What happens when crypto operations fail? Does the error path fall back to no-crypto?
Business logic (subagent_type: general) This is where the real bugs hide. Standard scanners can't find logic errors. For each major workflow:
- State machine violations: Can you skip steps? Go backwards? Reach an invalid state? What happens if you replay a completed flow? What about partial failure — if step 2 of 3 fails, is step 1 rolled back?
- Race conditions with business impact: Concurrent operations that produce invalid states (double-spend, double-approve, lost updates). Focus on operations that check-then-act non-atomically.
- Numeric/quantity manipulation: Negative values, zero values, overflow, precision loss, type coercion between string and number.
- Access boundary violations: Not "does the permission check exist" but "is it the right check for the business rule?" Can input to one operation bypass a restriction enforced on a different operation for the same effect?
- Implicit trust assumptions: Data from storage, config, other components, or plugins assumed safe because "we validated it on the way in." What if a different code path wrote it?
- Time-based logic: Expiry checks, scheduling, rate windows, clock skew. What happens at exact boundary moments? What about timezone differences between components?
- Default and fallback behavior: What's the security posture when config is missing? When a feature flag is off? When a dependency is unavailable? When the system is mid-migration?
Feature abuse and data leakage (subagent_type: general) Legitimate features used for unintended purposes. Don't look for bugs in the code — look for bugs in the design:
- Export/backup as exfiltration: Can a low-privilege user trigger an export, snapshot, or backup that includes data above their access level? Can they export other users' data? Does the export include deleted/draft/private content? Revision history that was supposed to be pruned?
- Import/restore as injection: Can import overwrite existing data? Can it create records that bypass normal validation? Can it inject content into collections the user doesn't have write access to? Does it respect the same permission model as the UI?
- Search/filter/sort as oracle: Can search queries reveal whether content exists that the user can't directly access? Do filter parameters let users probe statuses, roles, or fields they shouldn't know about? Does sorting by a hidden field reveal its values through result ordering?
- Enumeration through side effects: Do error messages differ between "doesn't exist" and "you don't have access"? Do response times differ? Response sizes? HTTP status codes? Can you enumerate users through password reset, invite, or registration flows?
- Preview/draft/staging leakage: Are preview tokens scoped to one item or do they unlock broader access? Can draft content be discovered through search, RSS feeds, sitemaps, or API listing endpoints? Can cache headers cause a CDN to serve private content publicly?
- Notification/webhook as SSRF: Can a user set a notification URL, webhook URL, or callback URL that the server fetches? Is it validated against internal networks? What about after a redirect?
Chained attacks and trust boundaries (subagent_type: general) Individual safe behaviors that become dangerous in combination. Think about the full system:
- Multi-step chains: Map out what a low-privilege user CAN do, then look for combinations. Info disclosure (learning a resource ID) + IDOR (accessing it directly) + missing rate limit (brute-forcing the ID space). Open redirect + OAuth callback = token theft. Benign XSS in a low-value context + CSRF to escalate it.
- Cross-component trust gaps: Component A validates input and passes it to component B. Does B re-validate or trust A? What if A's validation is subtly different from what B needs (e.g., A allows 255 chars but B truncates at 128, creating a different string)? What about plugin/extension trust — can third-party code manipulate core state, bypass permission hooks, or access storage directly?
- Second-order attacks: Data safe when stored but dangerous when used in a different context. A field name safe in SQL becomes a key in a JSON path expression. A slug safe in a URL becomes part of a file path. Content stored HTML-escaped gets double-escaped or rendered in a context that expects raw text. Config values stored as strings get parsed as URLs, regexes, or templates.
- Scope and capability escalation: Tokens, API keys, or OAuth scopes that grant broader access than their name implies. A
readscope that also allows listing draft content. Session cookies that survive a role downgrade. Plugin capabilities that provide a stepping stone to higher access. MCP or AI tool integrations that inherit the user's full session. - Timing and ordering: Can you use a feature before setup/migration is complete? Act on a resource between soft-delete and hard-delete? Use a token between revocation and cache expiry? Exploit the gap between two non-atomic operations (check-then-act, read-then-write, validate-then-use)?
- Rollback and recovery abuse: What happens when an operation is undone? Undelete, restore from backup, revert a revision, cancel a pending action. Does the rollback restore more than intended? Does it bypass current permissions? Can you restore a resource into a state that's no longer valid?
Wildcard (subagent_type: general) You are not given a category. You are given the codebase and told to break it.
Ignore the standard vulnerability classes — other agents are covering those. Your job is to find the thing nobody thought to look for. Read code that looks boring. Follow functions that seem unrelated to security. Get curious about the weird stuff.
Some starting points, but don't limit yourself to these:
- What's the strangest code in the codebase? Why does it exist? What happens if it's abused?
- Are there any features that feel half-finished, experimental, or bolted on? Those have the weakest security because they got the least review.
- What happens if you use the API in a way the frontend never would? The UI constrains users, but the API doesn't. What API calls are possible but never made by the client?
- Are there any hidden or undocumented endpoints, parameters, headers, or features? Look at route registrations, middleware, and config for things that aren't in the docs.
- What happens when you mix features that weren't designed to work together? Localization + preview + caching. Import + plugins + webhooks. OAuth + impersonation + API keys.
- Is there anything interesting in the git history? Reverted security fixes, commented-out auth checks, secrets that were committed then removed (still in history).
- What would you do if you had a valid account but wanted to cause maximum damage without being detected? Not escalation — sabotage. Corrupting data, poisoning caches, exhausting resources, creating confusing state.
- Are there any operations that are irreversible? What if you trick an admin into performing one?
- What assumptions does the code make about the environment? That the database is local, that the clock is accurate, that DNS is trustworthy, that the filesystem is case-sensitive?
- Look at the test files — what are they NOT testing? What edge cases did the developer think about (tests exist) vs. what they didn't (no tests)?
Follow rabbit holes. If something looks weird, dig. If a function has a comment explaining why it's safe, verify the explanation. If a variable is named temp or hack or legacy, read every line of it.
Obvious things (subagent_type: general) The other agents are hunting for subtle bugs. This agent checks the dumb stuff that's easy to overlook because everyone assumes someone else already checked it:
- Are there any hardcoded passwords, API keys, tokens, or secrets in the source? (grep for
password,secret,apikey,token,Bearer,-----BEGIN, common default passwords) - Are there any TODO/FIXME/HACK/XXX comments that reference security? (
TODO: add auth,FIXME: validate input,HACK: skip permission check) - Is debug mode / dev mode properly gated? Can it be enabled in production via environment variable, query parameter, or header?
- Are there test/example/seed credentials that work in production?
- Is there a
/debug,/admin,/test,/status,/health,/metrics,/env,/.env,/configendpoint that's unprotected? - Are there any
.env,.env.local,credentials.json,*.pem,*.keyfiles checked into the repo? - Does the
.gitignoreactually cover secrets, uploads, and local config? - Are dependencies pinned? Are there known CVEs in the dependency tree? (check lockfiles)
- Are there any
eval(),exec(),child_process,Function(),vm.runInContext,import()with dynamic input? - Are CORS headers set to
*or overly permissive? IsAccess-Control-Allow-Credentialscombined with a wildcard origin? - Are cookies missing
HttpOnly,Secure, orSameSiteattributes? - Are there any open redirects? (parameters named
redirect,return,next,url,goto,continuethat feed into redirects without validation) - Is TLS enforced? Are there any HTTP-only endpoints?
- Are error responses in production returning stack traces, internal paths, or SQL errors?
This agent doesn't need to be creative. It needs to be thorough and literal. Check every item. Report what it finds.
IMPORTANT: For any finding this agent reports, it must verify the full code path, not just surface appearance. If a cookie is missing HttpOnly, check whether the cookie contains security-sensitive data and whether JS needs to read it by design. If an error message contains a field name, check whether the field is ever actually populated with sensitive data. A flag is not a finding — trace the impact before reporting.
Vulnerability Hunting
Phase 2: Hunt for vulnerabilities
Launch multiple `general` agents in parallel via the Task tool. Use general, not research — general agents can spawn their own sub-agents via the Task tool, so when a hunter finds a rabbit hole that needs deeper investigation (e.g., tracing injection into an auth subsystem it doesn't fully understand), it can spin up a focused research sub-agent rather than trying to do everything in one context window.
Each agent gets the architecture summary from Phase 1 injected into its prompt plus the hunting methodology and validation rules. Launch them in a single message so they run concurrently.
How many agents? Use Phase 1 to decide. More focused agents produce better results than broad ones that run out of context. For a small library, 3-4 agents may suffice. For a large application with distinct subsystems, launch 8-12+ — split by attack class AND by subsystem. If Phase 1 revealed an auth system, a plugin system, a media pipeline, and a comment engine, each of those could warrant its own injection agent, its own logic agent, etc.
Every agent prompt MUST include: 1. The architecture summary from Phase 1 (copy it in verbatim) 2. The specific attack class and scope to investigate 3. Relevant file paths from Phase 1 as starting points 4. The hunting methodology (below) 5. The validation rules (below)
Hunting methodology — include in every Phase 2 agent prompt
Tell each agent to think like an attacker, not a code reviewer:
## How to hunt
Don't just check if defenses exist. Try to break them.
READ THE CODE AT DEPTH. Don't stop at the first function. Follow the data through
every layer — from the entry point through validation, transformation, storage, retrieval,
and output. Bugs live in the gaps between layers.
Think about these angles:
1. THE HAPPY PATH IS DEFENDED. ATTACK THE SAD PATH.
Error handlers, fallback branches, catch blocks, default cases, timeout paths,
retry logic, cleanup routines. What happens when things fail? Are errors handled
with the same rigor as success? Does a failed validation leave state half-modified?
2. WHAT HAPPENS AT BOUNDARIES?
Empty input. Maximum-length input. Null vs undefined vs missing. Zero. Negative numbers.
Unicode edge cases. The first item and the last item. One more than the maximum. Exactly
at the rate limit. The moment a token expires.
3. WHAT DO COMPONENTS ASSUME ABOUT EACH OTHER?
Does the database layer assume the API layer validated input? Does the renderer assume
content was sanitized on write? Does the auth middleware assume routes register themselves
correctly? Find where trust is implicit and test whether it's justified.
4. WHAT IF OPERATIONS HAPPEN IN THE WRONG ORDER?
Call step 3 before step 1. Call delete during create. Send the callback before the request.
Hit the confirmation endpoint without starting the flow. Replay a completed flow.
5. WHAT IF TWO THINGS HAPPEN AT ONCE?
Two requests to the same resource. Modify while reading. Delete while iterating.
Publish while someone else is editing. Two users claiming the same unique resource.
6. WHERE DO TWO PARSERS OR VALIDATORS DISAGREE?
Input accepted by the schema but rejected by the database. URL parsed differently by
the router vs the application code. Content-type header says one thing, body is another.
Filename extension vs MIME type vs magic bytes.
7. WHAT SURVIVES A ROUND TRIP?
Data stored then retrieved — is it the same? Does encoding change? Does escaping
double-up? Is a relative path resolved differently on read vs write? Does serialization
lose type information?
8. WHAT DOES THE CONFIGURATION CONTROL?
What happens when config is missing or default? Can an environment variable override a
security control? Does a feature flag disable validation? What's the security posture
during setup/first-run before config is complete?
9. FOLLOW THE MONEY (OR THE PRIVILEGE).
For every operation that changes state, ask: who authorized this? Trace back to the
permission check. Is it checking the right permission? Is it checking against the right
resource? Is there a parallel path to the same state change that checks differently
or not at all?
10. LOOK FOR LEAKED CONTEXT.
Error messages that reveal internal paths. Stack traces in production. Timing differences
that reveal whether a record exists. Response size differences. HTTP headers that
disclose versions. Debug endpoints that survived into production.
YOU CAN SPAWN SUB-AGENTS. If you need to understand a subsystem in depth to evaluate
a potential finding — use the Task tool to launch a research agent. Don't try to hold
everything in your own context. Go deep where it matters.
11. WHAT PARAMETERS OVERRIDE SECURITY-RELEVANT DEFAULTS?
Where a default is safe but a user-supplied parameter can change it. Look for
every input that overrides a security-relevant default and check if the override
is gated by appropriate permissions.
12. WHERE DO UNVERIFIED CLAIMS DRIVE TRUST DECISIONS?
Anywhere self-declared identity, capability, or metadata influences an access
or trust decision without independent verification.
YOUR SCOPE IS YOUR PRIMARY FOCUS, NOT A BOUNDARY.
If while investigating your assigned area you notice something wrong in a different
category — a permission issue while tracing injection, a race condition while reviewing
auth — report it. Don't ignore a bug because it's "not your area." Attackers don't
respect category boundaries.
## Validation rules — apply before reporting ANY finding
1. You MUST construct a concrete attack (exact inputs, requests, or action sequence)
2. The attack MUST achieve meaningful impact (not just "learn field names" or "cause an error")
3. Check if another layer already prevents exploitation — if so, it's a hardening note, not a finding
4. If the baseline comparable has the same pattern, note whether it's been exploited there
5. If your exploit depends on parser/runtime behavior, verify against the relevant spec or implementation — do not reason from intuition.
6. Return ONLY confirmed findings with concrete attacks, or "No exploitable vulnerabilities found" if that's honest.MIT License
Copyright (c) 2025-2026 Cloudflare, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
security-audit
A coding-agent skill that turns your agent into a security auditor. It orchestrates multiple parallel agents through a six-phase pipeline -- recon, hunting, validation, reporting, structured output, and independent verification -- to find exploitable vulnerabilities with real impact.
This is the skill that seeded Cloudflare's vulnerability discovery harness, described in Build your own vulnerability harness. The harness grew into a multi-stage, fleet-wide system; this skill is the single-repo starting point it evolved from.
What it does
The skill runs a structured audit in six phases:
1. Recon -- parallel research agents map the application's architecture, trust boundaries, and input surfaces. Produces architecture.md. 2. Hunt -- parallel general agents attack the codebase from different angles (injection, access control, business logic, cryptography, feature abuse, chained attacks, and a wildcard). Each agent can spawn sub-agents to dig deeper. 3. Validate -- separate agents try to disprove each finding. Adversarial review kills false positives. 4. Report -- produces REPORT.md (human-readable) and FINDINGS-DETAIL.md (detailed traces for MEDIUM+ findings). 5. Structured output -- writes findings.json conforming to report-schema.json, validated by validate-findings.cjs. 6. Independent verification -- fresh agents verify every factual claim in the structured output against the actual source code.
Multiple runs against the same repo are additive. Each run explores different code paths; the skill reads prior findings.json files to skip known issues and target gaps.
Files
| File | Purpose |
|---|---|
SKILL.md | Setup, core principles, platform terminology, workflow overview, and audit anti-patterns |
RECONNAISSANCE.md | Phase 1 reconnaissance prompts and synthesis instructions |
HUNTING.md | Phase 2 orchestration, hunting methodology, and validation rules |
ATTACK-CLASSES.md | Core, wildcard, and obvious-things attack prompts |
VALIDATION-AND-REPORTING.md | Phases 3–6 validation, reporting, and verification |
report-schema.json | JSON schema for findings.json (confirmed and rejected finding structures) |
validate-findings.cjs | Zero-dependency Node.js validator that checks findings.json against the schema |
Installation
Install the skill with the Skills CLI:
npx skills add https://github.com/cloudflare/security-audit-skill \
--skill security-auditUse --global for a user-level installation:
npx skills add https://github.com/cloudflare/security-audit-skill \
--skill security-audit \
--globalRun npx skills --help for agent-selection and non-interactive options.
Usage
Start your coding agent in (or pointed at) the codebase you want to audit, then ask it to do a security audit:
security audit this codebasefind security vulnerabilities in ./srcdo a security review, output to ~/audits/my-projectThe skill activates automatically when the request matches its trigger (security audit, find vulnerabilities, pen-test the code, etc.). It will ask for an output directory if you don't specify one, defaulting to ~/security-audit-skill/<repo-name>/run-<N>.
Requirements
- A coding agent with a model that supports tool use and parallel sub-agents
- Node.js (for
validate-findings.cjsschema validation in Phase 5)
Design principles
- Only report what you can exploit. Every finding needs a concrete attack scenario, not "an attacker could theoretically..."
- Adversarial validation. The agent that checks a finding is never the agent that found it.
- Severity requires impact. Likelihood x impact, not deviation from a checklist.
- Defense-in-depth gaps are not vulnerabilities. If Layer A prevents the attack, the absence of Layer B is a hardening note.
- Multiple runs improve coverage. Testing shows a single run finds roughly half the total vulnerabilities across multiple runs.
Contact
Questions, feedback, or comparing notes on AI-driven security tooling: security-ai-research@cloudflare.com
License
MIT -- see LICENSE.
Phase 1: Understand the Application
Phase 1: Understand the application
Before looking for bugs, understand what you're auditing. This requires depth, not just a directory listing. Launch multiple `research` agents in parallel to map different aspects of the codebase:
Agent 1a: Overview, tech stack, and comparable baseline
Explore the codebase at <path>. Answer:
1. What is this application? What kind of software? (web app, API, CLI tool, library, daemon, desktop app, mobile backend, etc.)
2. Who uses it and how? (end users, developers, operators, other services)
3. What's the tech stack? (languages, frameworks, databases, runtime, deployment model)
4. What comparable mainstream software exists? What security tradeoffs does the comparable accept?
5. What's the high-level directory structure?
Return specific file paths for key entry points.Agent 1b: Trust boundaries and access control
Explore the codebase at <path>. Find and read ALL code related to:
1. Trust boundaries — where does untrusted input enter the system? (HTTP requests, CLI args, file reads, IPC, message queues, environment variables, config files, etc.)
2. Authentication — how do callers prove identity? (sessions, tokens, API keys, mTLS, Unix sockets, etc.) If there's no authentication, note that.
3. Authorization — how are permissions enforced? (middleware, decorators, capability checks, file permissions, etc.) If there's no authorization model, note that.
4. Privilege separation — does the code run as root? Drop privileges? Use sandboxing? Fork workers?
5. Any bypass mechanisms (dev-only modes, test helpers, setup flows, debug flags)
Return the trust model: who are the actors, what can each do by design, and which code enforces it. Include specific file paths and line numbers.Agent 1c: Input surface inventory
Explore the codebase at <path>. Produce a complete inventory of where external input enters the system:
1. Network-facing surfaces (HTTP endpoints, gRPC services, WebSocket handlers, TCP/UDP listeners, etc.) — list each with method/verb and purpose
2. File-based input (file uploads, config file parsing, log ingestion, import/export, etc.)
3. IPC and inter-service input (message queues, shared memory, Unix sockets, environment variables, CLI arguments)
4. User-generated content surfaces (anywhere users provide content that is stored and later rendered, served, or processed)
5. External integrations (OAuth, webhooks, third-party APIs, plugin loading, dynamic code execution)
6. All places where input reaches dangerous sinks (SQL/query builders, HTML/template output, file paths, shell commands, deserialization, eval, dynamic imports)
Return specific file paths. Be exhaustive.Collect all three agents' outputs and synthesize them into <output-dir>/architecture.md:
- 1-2 page structured summary covering application type, tech stack, trust model, input surfaces, and baseline comparable
- Include the key file paths from all agents — these become the starting points for Phase 2
- This document is injected verbatim into every Phase 2 agent prompt
If Phase 1 agents reveal the codebase is larger or more complex than expected (e.g., plugin system, multi-tenant architecture, complex auth chains, multiple deployment targets), launch additional research agents to map those areas before proceeding. The quality of Phase 2 depends entirely on the quality of Phase 1.
{
"$comment": "Single source of truth for findings.json structure (see SKILL.md Phase 5). validate-findings.cjs reads this file directly and interprets it — there is no second copy of these rules to keep in sync.",
"output_schema": {
"oneOf": [
{
"type": "object",
"description": "Confirmed vulnerability — provide the complete, independently verified report.",
"properties": {
"verdict": {
"type": "string",
"const": "confirmed"
},
"title": {
"type": "string",
"description": "A concise, standard title for the vulnerability."
},
"description": {
"type": "string",
"description": "Comprehensive explanation of the vulnerability. Include any reproduction details (proof-of-concept input, configuration, observed output or crash) here."
},
"root_cause": {
"type": "string",
"description": "One sentence using the template: '[function_or_component] in [file] does not [missing action], allowing [consequence]'. MUST include the function/component name and file name where the defect exists."
},
"intended_behavior": {
"type": "string",
"description": "What was the developer trying to build? Explain the intended, non-vulnerable business logic."
},
"trace": {
"type": "array",
"minItems": 2,
"items": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": ["entrypoint", "propagation", "sink"]
},
"file": {
"type": "string",
"description": "Exact file path relative to repository root."
},
"line": {
"type": "integer"
},
"scope": {
"type": "string",
"description": "Bare function or method name. No parentheses, no arguments."
},
"description": {
"type": "string",
"description": "Factual description of the state change or data movement."
}
},
"required": ["kind", "file", "line", "scope", "description"],
"additionalProperties": false
},
"description": "Sequential code trace from entrypoint to sink, verified against actual source code. The first step must be kind 'entrypoint' and the last must be kind 'sink' (enforced by the validator)."
},
"conditions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": ["authentication_level", "authorization_role", "user_interaction", "system_configuration", "network_routing", "environmental_dependency", "data_state", "timing_dependency", "third_party_dependency"]
},
"description": {
"type": "string"
}
},
"required": ["kind", "description"],
"additionalProperties": false
},
"description": "Factual prerequisites for exploitation. Empty array if exploitable by default."
},
"execution": {
"type": "object",
"properties": {
"attacker_perspective": {
"type": "string",
"description": "Who is the attacker and their starting point."
},
"payloads": {
"type": "array",
"items": {
"type": "string"
},
"description": "Specific malicious inputs, HTTP requests, or scripts."
},
"instructions": {
"type": "array",
"items": {
"type": "string"
},
"description": "Linear array of all attacker actions from setup through exploitation."
},
"expected_result": {
"type": "string",
"description": "Observable outcome confirming successful exploitation."
}
},
"required": ["attacker_perspective", "payloads", "instructions", "expected_result"],
"additionalProperties": false
},
"remediation": {
"type": "object",
"properties": {
"strategy": {
"type": "string",
"description": "High-level explanation of the fix."
},
"code_changes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"file_name": {
"type": "string"
},
"fixed_code": {
"type": "string"
}
},
"required": ["file_name", "fixed_code"],
"additionalProperties": false
}
}
},
"required": ["strategy"],
"additionalProperties": false
},
"severity": {
"type": "object",
"properties": {
"likelihood": {
"type": "object",
"properties": {
"score": {
"type": "string",
"enum": ["informational", "low", "medium", "high", "critical"]
},
"reason": {
"type": "string"
}
},
"required": ["score", "reason"],
"additionalProperties": false
},
"impact": {
"type": "object",
"properties": {
"score": {
"type": "string",
"enum": ["informational", "low", "medium", "high", "critical"]
},
"reason": {
"type": "string"
}
},
"required": ["score", "reason"],
"additionalProperties": false
},
"overall_severity": {
"type": "string",
"enum": ["informational", "low", "medium", "high", "critical"]
}
},
"required": ["likelihood", "impact", "overall_severity"],
"additionalProperties": false
},
"confidence": {
"type": "object",
"properties": {
"score": {
"type": "string",
"enum": ["low", "medium", "high"]
},
"reason": {
"type": "string",
"description": "Why you scored the confidence this way. Mention any missing files, complex routing, or ambiguous data flows."
}
},
"required": ["score", "reason"],
"additionalProperties": false
}
},
"required": ["verdict", "title", "description", "root_cause", "intended_behavior", "trace", "conditions", "execution", "remediation", "severity", "confidence"],
"additionalProperties": false
},
{
"type": "object",
"description": "Rejected finding — the described behavior is factually incorrect or the code path does not exist.",
"properties": {
"verdict": {
"type": "string",
"const": "rejected"
},
"reason": {
"type": "string",
"description": "Explain which specific claims in the finding are factually wrong (e.g., code path doesn't exist, mitigation prevents the described flow, trace is incorrect)."
}
},
"required": ["verdict", "reason"],
"additionalProperties": false
}
]
}
}
#!/usr/bin/env node
/**
* Validates findings.json against report-schema.json.
* Usage: node validate-findings.cjs <path-to-findings.json>
*
* The validation rules live in report-schema.json — the single source of truth.
* This script reads that schema at runtime and interprets the subset of JSON
* Schema it uses: type (object|array|string|integer), properties, required,
* additionalProperties:false, enum, const, items, minItems, and oneOf.
*
* Two constraints can't be expressed in that subset (a confirmed trace must
* start at an "entrypoint" and end at a "sink"). They're applied as an explicit,
* clearly-labelled semantic layer after schema validation.
*
* Zero dependencies. Exits 0 on success, 1 on validation failure.
*/
const fs = require("fs");
const path = require("path");
const file = process.argv[2];
if (!file) {
console.error("Usage: node validate-findings.cjs <path-to-findings.json>");
process.exit(1);
}
const schemaPath = path.join(__dirname, "report-schema.json");
let itemSchema;
try {
const doc = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
itemSchema = doc.output_schema;
if (!itemSchema) throw new Error('report-schema.json is missing top-level "output_schema"');
} catch (e) {
console.error(`Failed to load schema from ${schemaPath}:`, e.message);
process.exit(1);
}
let findings;
try {
findings = JSON.parse(fs.readFileSync(file, "utf8"));
} catch (e) {
console.error("Failed to parse JSON:", e.message);
process.exit(1);
}
if (!Array.isArray(findings)) {
console.error("findings.json must be an array");
process.exit(1);
}
// --- Generic JSON Schema interpreter (the subset used by report-schema.json) ---
function typeOf(v) {
if (Array.isArray(v)) return "array";
if (v === null) return "null";
return typeof v; // "object" | "string" | "number" | "boolean"
}
// For oneOf: find a property defined with a `const` so error messages can point
// at the intended branch (e.g. discriminate confirmed vs rejected by "verdict").
function findDiscriminator(schema) {
if (!schema.properties) return null;
for (const [key, sub] of Object.entries(schema.properties)) {
if (sub && Object.prototype.hasOwnProperty.call(sub, "const")) {
return { key, value: sub.const };
}
}
return null;
}
function validate(value, schema, p, errors) {
if (schema.oneOf) {
// Prefer the branch whose const discriminator matches, so the caller sees
// detailed errors for the branch they clearly intended.
for (const branch of schema.oneOf) {
const disc = findDiscriminator(branch);
if (disc && value && typeof value === "object" && value[disc.key] === disc.value) {
validate(value, branch, p, errors);
return;
}
}
// No discriminator matched. If every branch is discriminated by the same
// key, report the bad discriminator value clearly.
const discs = schema.oneOf.map(findDiscriminator).filter(Boolean);
if (discs.length === schema.oneOf.length && value && typeof value === "object") {
const key = discs[0].key;
const allowed = discs.map((d) => JSON.stringify(d.value)).join(", ");
errors.push(`${p}: "${key}" must be one of ${allowed}, got ${JSON.stringify(value[key])}`);
return;
}
const passing = schema.oneOf.filter((b) => collect(value, b, p).length === 0);
if (passing.length !== 1) {
errors.push(`${p}: does not match exactly one of the allowed schemas`);
}
return;
}
if (Object.prototype.hasOwnProperty.call(schema, "const") && value !== schema.const) {
errors.push(`${p}: must equal ${JSON.stringify(schema.const)}, got ${JSON.stringify(value)}`);
}
if (schema.enum && !schema.enum.includes(value)) {
const allowed = schema.enum.map((v) => JSON.stringify(v)).join(", ");
errors.push(`${p}: invalid value ${JSON.stringify(value)} (expected one of ${allowed})`);
}
switch (schema.type) {
case "object": {
if (typeOf(value) !== "object") {
errors.push(`${p}: expected object, got ${typeOf(value)}`);
return;
}
for (const req of schema.required || []) {
if (!(req in value)) errors.push(`${p}: missing required field "${req}"`);
}
for (const key of Object.keys(value)) {
if (schema.properties && key in schema.properties) {
validate(value[key], schema.properties[key], `${p}.${key}`, errors);
} else if (schema.additionalProperties === false) {
errors.push(`${p}: unexpected field "${key}"`);
}
}
break;
}
case "array": {
if (typeOf(value) !== "array") {
errors.push(`${p}: expected array, got ${typeOf(value)}`);
return;
}
if (typeof schema.minItems === "number" && value.length < schema.minItems) {
errors.push(`${p}: must have at least ${schema.minItems} item(s), got ${value.length}`);
}
if (schema.items) {
value.forEach((el, i) => validate(el, schema.items, `${p}[${i}]`, errors));
}
break;
}
case "integer": {
if (typeOf(value) !== "number" || !Number.isInteger(value)) {
errors.push(`${p}: expected integer, got ${typeOf(value)}`);
}
break;
}
case "string": {
if (typeOf(value) !== "string") {
errors.push(`${p}: expected string, got ${typeOf(value)}`);
}
break;
}
default:
break; // no type constraint at this node
}
}
function collect(value, schema, p) {
const errors = [];
validate(value, schema, p, errors);
return errors;
}
// --- Run ----------------------------------------------------------------------
let errorCount = 0;
findings.forEach((f, i) => {
const label = `[${i}] ${(f && (f.title || f.reason)) || "(untitled)"}`;
console.log(`Checking ${label}`);
const errs = collect(f, itemSchema, `[${i}]`);
// Semantic layer — constraints the schema subset can't express:
// a confirmed trace must start at an entrypoint and end at a sink.
if (f && f.verdict === "confirmed" && Array.isArray(f.trace) && f.trace.length > 0) {
if (f.trace[0] && f.trace[0].kind !== "entrypoint") {
errs.push(`[${i}].trace[0].kind must be "entrypoint", got ${JSON.stringify(f.trace[0].kind)}`);
}
const last = f.trace.length - 1;
if (f.trace[last] && f.trace[last].kind !== "sink") {
errs.push(`[${i}].trace[${last}].kind must be "sink", got ${JSON.stringify(f.trace[last].kind)}`);
}
}
for (const msg of errs) console.error(" ERROR:", msg);
errorCount += errs.length;
});
console.log();
if (errorCount === 0) {
console.log(`PASS: ${findings.length} findings valid`);
} else {
console.error(`FAIL: ${errorCount} error(s) across ${findings.length} findings`);
process.exit(1);
}
Validation, Reporting, and Verification
Phase 3: Validate findings
Collect all findings from Phase 2 agents and consolidate duplicates first. Phase 2 deliberately overlaps agent scopes, so the same issue is frequently reported by more than one hunter — merge findings that share a root cause before validating, or you'll validate and report the same bug multiple times. For each remaining finding, launch a separate `research` validation agent that tries to disprove it. The hunting agents are biased toward finding things; the validation agents are biased toward killing false positives. This adversarial step is critical.
For findings from the same attack surface, batch them into one validation agent. Launch validation agents in parallel where they cover independent areas.
Each validation agent prompt should: 1. State the specific finding being validated (title, claimed attack, claimed impact) 2. Ask the agent to read the exact code paths and verify each step of the trace 3. Ask it to apply these tests:
Validation tests: 1. Exploitation test: Read the actual code at each step of the trace. Does the data flow work as claimed? Can you construct the exact input (HTTP request, CLI invocation, API call, crafted file, etc.) that triggers this? 2. Impact test: What does the attacker actually get? If the answer is "they learn field names" or "they cause an error", that's LOW at best. 3. Baseline test: Does the identified comparable have the same pattern? If yes, has it been exploited? If never exploited in years of production use, understand why before reporting. 4. Mitigation test: Is there another layer that prevents exploitation? Check middleware, database constraints, framework defaults. 5. Parser/runtime behavior test: If the exploit depends on how a parser or runtime handles specific input, verify against the actual spec or implementation — do not reason from intuition.
Tell each validation agent:
Your job is to DISPROVE this finding. Read the actual source code at every step. If you cannot disprove it, confirm it with the exact code that makes it exploitable. Return one of:
- "CONFIRMED: [explanation of why it's real, with code evidence]"
- "REJECTED: [explanation of what the finding got wrong, with code evidence]"Kill false positives aggressively, but don't kill real findings. A short report with 3 real findings is worth more than a long report with 30 theoretical ones. An honest "nothing found" is valid — but push hard before reaching that conclusion.
Phase 4: Report
Write the report to the output directory established in Setup.
Output files:
1. REPORT.md -- Main report with:
- One-paragraph executive summary (honest assessment of security posture)
- Identified baseline and how this application compares
- Findings table (severity, title, one-line description)
- Each finding with: file path, concrete attack scenario, impact, recommended fix
- Hardening notes section (defense-in-depth suggestions, NOT findings)
- Positive patterns section (what the codebase does well -- this calibrates trust in the audit)
2. FINDINGS-DETAIL.md -- For each finding rated MEDIUM or above:
- Complete data flow from input to sink with file:line references
- Exact HTTP request(s) to trigger
- What the attacker gets
- How the baseline comparable handles the same scenario
Keep it short. If the report is longer than the codebase deserves, you're padding.
Phase 5: Structured output and schema check
For every finding that survived Phase 3 validation, produce a structured JSON object conforming to the schema defined in report-schema.json (in the same directory as this skill file — read it via the Read tool before writing output). Write the result to <output-dir>/findings.json.
The schema supports two verdict types via oneOf:
- `confirmed` — a validated vulnerability with full trace, execution, and remediation
- `rejected` — a finding that was investigated and determined to be factually incorrect
Before writing `findings.json`:
1. Read report-schema.json from this skill's directory. Follow it exactly — additionalProperties: false is enforced, so extra fields will make the output invalid. 2. For each finding, populate every required field. If you cannot fill trace with real file paths and line numbers verified against the source, the finding is not sufficiently verified — go back and verify it or reject it. 3. Run node <skill-dir>/validate-findings.cjs <output-dir>/findings.json to validate. It checks required fields, enum values, structural constraints, and additionalProperties. This is a structural check only — it confirms the JSON conforms to the schema, not that the findings are correct. Factual verification is Phase 6's job. Fix any failures before proceeding.
Phase 6: Independent verification
The structured output from Phase 5 forces self-validation, but the same agent that wrote the finding also wrote the JSON — it won't catch its own blind spots. This phase uses a fresh agent to independently verify every claim in findings.json.
Launch one `research` agent per confirmed finding via the Task tool, all in parallel. Each agent gets exactly one finding from findings.json and verifies it independently. Give each agent the JSON object for its finding and this prompt:
You are an independent verifier. You did NOT write this finding. Your job is to read the actual source code and verify that every factual claim is correct.
1. Read the file and line number cited in EVERY trace step. Verify:
- The file exists at that path
- The line number matches the described code
- The scope (function name) is correct
- The description accurately reflects what the code does
2. Verify the root_cause statement by reading the cited file and confirming the described defect exists.
3. Verify the execution payloads would actually work:
- Does the endpoint exist at the claimed URL?
- Does the HTTP method match?
- Would the input pass validation as described?
- Would auth/access checks pass as described?
4. Verify conditions are complete — are there prerequisites the finding missed?
5. Check the remediation code_changes — would the fix actually prevent the attack without breaking normal functionality?
Return one of:
- "VERIFIED" — all claims checked out against the source
- "CORRECTED: [field]: [what was wrong] → [what it should be]" — factual error in a specific field
- "REJECTED: [reason]" — the finding is fundamentally wrongApply the agent's corrections:
- VERIFIED findings: no changes needed
- CORRECTED findings: update the specific fields in
findings.json, re-run the schema validation script - REJECTED findings: change their
verdictto"rejected"with the agent's reason, or remove them entirely
After applying corrections, reconcile the prose deliverables: update REPORT.md and FINDINGS-DETAIL.md so they match the final findings.json. Remove or amend any finding the verification gate rejected or corrected — the human-readable report and the machine-readable output must not disagree.
This is the final quality gate. Do not skip it.
Related skills
FAQ
What does security-audit do?
Security audit of a codebase — web apps, APIs, services, CLI tools, libraries, daemons, and more. Use when asked to find security bugs, do a security review, audit for vulnerabilities, or pen-test the
When should I use security-audit?
During ship security work for security.
Is security-audit safe to install?
Review the Security Audits panel on this listing before production use.