agentleFS
mDevsLabsmDevsLabs/Skills/skills/security-code-review/SKILL.md

security-code-review

Skill2 starsChanged 4 months agoMITView on GitHub

Reviews source code and diffs for security vulnerabilities — injection, broken access control, secrets, unsafe deserialisation, SSRF, path traversal, weak crypto, dependency risk — and reports each finding with severity, exploit path, and a concrete patch. Use when the user asks for a security review, shares a pull request or diff, asks whether code is safe or exploitable, mentions a CVE, pentest finding, OWASP category, or hardcoded credentials.

5 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

4 rules. 0 are common, 4 are distinctive, and 0 are very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 4

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do not use for: infrastructure/cloud config review, compliance questionnaires, or writing exploits against systems the user does not own.

    line 25
  2. Distinctive:

    If the user cannot answer, assume internet-facing and untrusted input, and say so.

    line 50
  3. Distinctive:

    Treat every hit as a *lead*, not a verdict.

    line 61
  4. Distinctive:

    Do list endpoints filter by the caller's tenant in the query, not in code after fetching?

    line 92

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Security Code Review

Find exploitable bugs, not style violations. Every finding must answer: how would an attacker actually use this?

When to use

Pull-request review, pre-release audit, triaging a scanner report, reviewing auth or crypto code, assessing a dependency, responding to a pentest finding.

Do not use for: infrastructure/cloud config review, compliance questionnaires, or writing exploits against systems the user does not own.

Hard rules

  1. No exploit code for third-party systems. Proof-of-concept is limited to the minimum needed to demonstrate the flaw in the user's own code.
  2. Never invent a CVE number or a version range. Mark unverified claims [VERIFY].
  3. Do not report noise. A finding with no realistic attacker path is a hardening note, not a vulnerability — label it as such.
  4. Redact secrets you discover: show AKIA****EXAMPLE, never the full value.

Workflow

1. Establish the threat model in two minutes

QuestionWhy it changes severity
Internet-facing or internal-only?Reachability gates exploitability
Who are the users — anonymous, authenticated, admin?Determines privilege gain
What data does this touch — PII, money, credentials, health?Determines impact
Is there a WAF, gateway, or auth proxy in front?May reduce (never eliminate) risk
Is the input attacker-controlled, or from a trusted service?Untrusted input is the root of most findings

If the user cannot answer, assume internet-facing and untrusted input, and say so.

2. Run the secret and pattern scanners first

Deterministic detection before manual reading:

python3 scripts/scan_secrets.py ./src --format md
python3 scripts/taint_patterns.py ./src --lang auto

Treat every hit as a lead, not a verdict. Confirm by reading the code.

3. Trace taint: source → sink

For each dangerous sink, walk backwards to the input source. A vulnerability exists only when untrusted data reaches a sink without effective sanitisation.

SinkVulnerabilityEffective control
SQL string concatenationSQLiParameterised queries; allowlist for identifiers
exec, eval, system, child_processRCE / command injectionAvoid shell; pass argv arrays; allowlist
HTML rendering, innerHTML, dangerouslySetInnerHTMLXSSContext-aware escaping; CSP; sanitiser library
File path joinsPath traversalResolve then verify the prefix; reject .. and absolute paths
Outbound HTTP with a user URLSSRFAllowlist hosts; block link-local 169.254.169.254, RFC1918, redirects
pickle, yaml.load, Java/PHP deserialisationRCEUse JSON or safe_load; never deserialise untrusted bytes
LDAP / XPath / NoSQL query buildingInjectionTyped query builders; escape per grammar
Template enginesSSTINever compile a user-supplied template
Redirect targetsOpen redirectRelative paths only, or an allowlist
Log linesLog injection / forgingStrip CR/LF; use structured logging
Archive extractionZip-slipValidate each entry path before writing
Regex from user inputReDoSBound input length; use a linear-time engine

4. Check the access-control model explicitly

Broken access control is the most common critical finding and no scanner sees it.

  • Is authorisation checked on the object, not only on the route? (IDOR)
  • Can a user change userId, tenantId, role or orderId in the request and reach another tenant's data?
  • Are mass-assignment fields filtered on create/update?
  • Are admin routes protected server-side, not just hidden in the UI?
  • Do list endpoints filter by the caller's tenant in the query, not in code after fetching?
  • Are JWTs verified with a pinned algorithm (reject alg: none, reject HS/RS confusion), an audience check and an expiry check?

5. Review crypto and secrets

Anti-patternCorrect
MD5/SHA1 for passwords, or SHA256 unsaltedargon2id, scrypt, or bcrypt
Math.random() / random for tokensCSPRNG: secrets, crypto.randomBytes
AES-ECB, or CBC without a MACAEAD: AES-GCM or ChaCha20-Poly1305
Reused or hardcoded IV/nonceRandom per message; never reuse with GCM
== on secrets/HMACsConstant-time compare
Custom cryptoA vetted library
Secrets in code, env files in git, CI logsSecret manager; rotate anything committed
TLS verification disabledNever in production; flag as critical

6. Assess dependencies

Direct and transitive. Flag: known-vulnerable versions [VERIFY against an advisory DB], unmaintained packages, typosquat-looking names, install scripts, and lockfile absence. Recommend pinning + automated updates.

7. Report

One table, sorted by severity. For each finding: ID, title, severity, file:line, attacker path in one sentence, patch, and effort.

Severity uses impact × exploitability:

SeverityMeaning
CriticalUnauthenticated RCE, auth bypass, mass data exposure
HighAuthenticated privilege escalation, injection with real data access, secret leak
MediumRequires unusual preconditions, limited data, or chained with another bug
LowDefence-in-depth gap, information disclosure with no direct value
NoteHardening suggestion, no attacker path today

Worked example

@app.route("/api/invoices/<invoice_id>")
@login_required
def get_invoice(invoice_id):
    row = db.execute(
        f"SELECT * FROM invoices WHERE id = '{invoice_id}'").fetchone()
    return jsonify(dict(row))

F-01 — SQL injection via invoice_id · Critical · api/invoices.py:14 Any authenticated user can send /api/invoices/x' UNION SELECT password_hash,1,1 FROM users-- and read the users table. Authentication is not a mitigation: any account suffices.

F-02 — IDOR: no ownership check · High · same line Even patched against SQLi, the query filters only on id. User A can read user B's invoice by guessing or enumerating the id.

Patch

@app.route("/api/invoices/<invoice_id>")
@login_required
def get_invoice(invoice_id):
    row = db.execute(
        "SELECT id, number, total_cents, currency, status, created_at "
        "FROM invoices WHERE id = ? AND tenant_id = ?",
        (invoice_id, current_user.tenant_id),
    ).fetchone()
    if row is None:
        abort(404)                      # 404, not 403 — do not confirm existence
    return jsonify(dict(row))

Also fixed: SELECT * leaked internal columns to the API response.

Quality checklist

  • Every finding names a concrete attacker and a concrete gain
  • Severity justified by impact × exploitability, not by CWE class alone
  • Each finding has a patch, not just a description
  • Access control reviewed at the object level, not only the route
  • Secrets redacted in the report; rotation advised for anything committed
  • No invented CVEs or version ranges; unverified claims marked [VERIFY]
  • False-positive-looking scanner hits explicitly dismissed with a reason
  • Hardening notes separated from real vulnerabilities

Failure modes

SituationResponse
Only a snippet is availableState which controls might exist outside the snippet; do not assert exploitability
Framework provides implicit protection (ORM, auto-escaping)Verify it is actually engaged; many have documented escape hatches
User asks for a working exploit against someone else's systemDecline; offer defensive verification steps instead
Scanner reported 400 issuesTriage by reachability first; report the top exploitable set, summarise the rest

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.