BeClaude

secpriv-skill

New
5GitHubGeneralby facebookresearch

Unified LLM-agent skill for code review covering both security (CWE) and privacy (GDPR) through one detector-validator methodology. Ships the SKILL.md, a 128-case Python benchmark across 30 canonical categories with a held-out true-negative subset, and the evaluation harness from the SecPriv paper.

First seen 5/22/2026

Overview

<!-- Copyright (c) Meta Platforms, Inc. and affiliates.

This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree. -->

SecPriv — Unified Security and Privacy Code Review Skill

Purpose

Perform a single-pass code review that surfaces both security weaknesses (CWE-mapped) and privacy violations (GDPR-mapped). The skill executes a five-phase methodology with a detector-validator decomposition: a detector phase enumerates candidate findings across both surfaces, and a validator phase applies seven shared suppression rules and a confidence threshold. The skill prioritizes precision over recall — only report findings with high confidence and a concrete violation path.

When to Use

Invoke when reviewing code (single files, diffs, or pull requests) that may contain security weaknesses, privacy violations, or both. Optimized for Python and JavaScript/TypeScript; rules generalize to Java, Go, and Rust.

Output Format

Return a JSON array of findings. Each entry MUST contain:

json
{
  "surface": "security" | "privacy",
  "category": "<one of 30 canonical categories listed below>",
  "severity": "HIGH" | "MEDIUM" | "LOW",
  "confidence": 0.0,
  "file": "path/to/file.py",
  "line": 42,
  "description": "Concrete description of the violation in this file",
  "remediation": "Concrete fix",
  "cwe": "CWE-89",                    // security only
  "regulatory_basis": "GDPR Art. 5(1)(f)",  // privacy only
  "exploitation_path": "...",         // security only
  "flow_id": "string"                 // optional, links cross-domain findings
}

If no issues are found, return an empty array []. Do not return findings with confidence < 0.8.


Phase 1: Repository Context Research

Before assessing any change, establish three context items.

1.1 Framework detection. Identify what protective frameworks are in use by reading import statements:

  • ORM layers (SQLAlchemy, Django ORM, Prisma, GORM) — auto-parameterize SQL
  • Input sanitizers (DOMPurify, bleach, html.escape) — neutralize XSS
  • Auth middleware (JWT decorators, session middleware, OAuth)
  • Template engines with auto-escape (Jinja2 default, React JSX, Angular)
  • Consent libraries / privacy SDKs

1.2 Trust boundaries.

  • Untrusted: HTTP request fields (params, headers, body, cookies, URL paths)
  • Semi-trusted: database values, cache entries, application config tables, feature flags (these may have admin-API write paths)
  • Trusted: OS environment variables, compile-time constants

Critical rule: Application-level config (feature flags, settings tables, admin-editable) is NOT trusted — only OS env vars and compile-time constants are.

1.3 Personal-data inventory. Identify what categories of personal data appear (PII identifiers, financial, health, location), what consent mechanisms are present, and what regulations apply.


Phase 2: Source / Sink and Transformation Flow Mapping

Trace data from sources to sinks. For each flow, record the source category, the sink category, and the transformation state of the data as it reaches the sink.

Source categories.

  • Untrusted-input source: HTTP fields, file uploads, webhook payloads, query strings
  • Personal-data source (Tang et al. taxonomy): account, contact, national ID, personal ID, online ID, location, health, financial

Sink categories.

  • Security-sensitive sinks: SQL execution, shell execution, eval/exec, template rendering, file open, network request, auth check
  • Personal-data sinks: data manipulation, transportation, creation/deletion, database write, encryption, log

Transformation state at sink.

StateAdequate?
rawNO — likely violation
salted_hash (with random per-record salt)YES for logging/identifiers
unsalted_hash (e.g., SHA-256 of email)NO — re-identification risk via rainbow tables
aggregated with k ≥ 5 groupingYES
encrypted with KMS-managed keyYES (verify key handling)
tokenized via vaultYES
base64, hex, rot13, plain encodingNO — reversible, not anonymization

This single transformation classifier serves both surfaces. A salted_hash PII output suppresses both pii_leakage and any stored-XSS candidate that would arise from rendering the same field.


Phase 3: Dual-Surface Vulnerability Assessment

Assess against 30 canonical categories: 20 security and 10 privacy.

3.1 Security Categories (20)

`sql_injection` (CWE-89): user input reaching SQL via f-string, .format(), string concatenation, or SQLAlchemy text() with +/format. Safe: ORM .where() / .filter_by(), parameterized queries with ?/%s placeholders, SQLAlchemy text() with :bind parameters.

`command_injection` (CWE-78): user input in subprocess.run(..., shell=True), os.system, os.popen, or any Popen with shell=True. Safe: subprocess.run([cmd, arg1, ...]) with list args (no shell).

`path_traversal` (CWE-22): user input in file paths without sanitization, including raw concatenation, pathlib.Path(BASE) / user_input without resolve-and-contain, and zip-slip during extractall(). Safe: os.path.realpath() + prefix containment check; pathlib.Path.resolve() then verify the resolved path stays within an expected base directory.

`xss_dom` (CWE-79 DOM-based): client-side JavaScript writes attacker-influenced data into the DOM without escaping. Patterns: element.innerHTML = userInput, document.write(userInput), jQuery $elt.html(userInput), outerHTML assignment. Safe: textContent, setAttribute with safe attrs, framework-managed rendering.

`auth_bypass` (CWE-287 / CWE-863): admin endpoints without auth middleware; missing permission checks; JWT validation bypass (e.g., algorithms=None, verify_signature=False); IDOR (no ownership check on resource access); missing webhook signature verification.

`hardcoded_secret` (CWE-798): literal API keys (sk_live_…, SG.…), DB passwords in connection strings, JWT signing keys, aws_secret_access_key=… literals. Safe: read from os.environ, process.env, secret manager APIs, KMS-managed credentials.

`deserialization` (CWE-502): pickle.loads() on attacker-controlled data; yaml.load() without SafeLoader; joblib.load() of attacker-supplied files; marshal.loads(). Safe: yaml.safe_load(), json.loads(), pickle.loads() only on internally-produced bytes with path validation.

`eval_injection` (CWE-94): eval() / exec() on user input; dynamic __import__(user_input) followed by attribute access. Safe: ast.literal_eval() for parsing literals, an explicit allowlisted dispatch table.

`infinite_loop` (CWE-835 / CWE-674): self-referential redirects, recursion without termination, retry loops without max-attempts cap, recursive directory walks without depth bounds or symlink-cycle detection.

`agent_csrf` (CWE-352 generalized for LLM agents): URL parameter, webhook payload, or deep link auto-triggering an agent write/exec without an interstitial confirmation step. Safe: confirmation dialog with explicit submit token, read-only preview followed by separate execution endpoint.

`second_order_sqli` (CWE-89 retrieval-side): user-controlled value stored in DB/cache/config, later retrieved and concatenated into a raw query, template, or outbound URL without sanitization. Includes stored-SSRF and stored-SSTI variants where the stored value's ultimate sink is a network fetch or template render rather than SQL.

`ssrf` (CWE-918): user-controlled URL passed to a server-side fetch (requests.get, urllib.urlopen, httpx.get) without scheme allowlist, host allowlist, or private-IP-range check. Particularly dangerous: cloud-metadata IPs (169.254.169.254), file:// scheme, internal-only hostnames. Safe: explicit URL allowlist + DNS resolution + private-IP rejection.

`open_redirect` (CWE-601): user-controlled value passed to redirect(), Location header, server-rendered JS location=, or OAuth callback redirect_uri without validation. Safe: url_for(name) with a fixed allowlist of named routes.

`crypto_misuse` (CWE-327 / CWE-329 / CWE-330): broken or risky cryptographic primitive — MD5/SHA-1 for password hashing, hardcoded IV in CBC mode, ECB mode for non-trivial data, random.random() / random.choice() for tokens or session IDs. Safe: bcrypt/argon2/scrypt for passwords, random per-message IV, secrets.token_urlsafe() / secrets.token_bytes() for tokens.

`xxe` (CWE-611): XML parser configured with default entity resolution on attacker-controlled input — lxml.etree.fromstring() without resolve_entities=False, explicit xinclude() on user XML. Safe: etree.XMLParser(resolve_entities=False, no_network=True, load_dtd=False).

`ssti` (CWE-1336): server-side template injection — Template(user_input).render(...) for Jinja2 / Mako / Twig with attacker-controlled template body, or str.format(obj) with user-controlled format spec walking attribute chains ("{0.api_key}").

`csrf` (CWE-352): state-changing endpoint (POST/PUT/DELETE) without CSRF token check; session cookie set with SameSite=None and no token; mutating action exposed on a GET endpoint. Safe: framework CSRF middleware (@csrf_protect, Flask-WTF, Django CSRF), SameSite=Strict or Lax cookies.

`insecure_logging` (CWE-532): secrets, tokens, full PII payloads, or full request headers (which include Authorization) written to application logs. Particularly dangerous in exception handlers that dump request.headers or full payload bodies. Safe: log identifiers as salted hashes, redact Authorization and other sensitive headers before logging.

3.2 Privacy Categories (10)

`pii_leakage` (GDPR Art. 5(1)(f)): personal data in logs, error responses, metric labels, stack traces. Includes indirect identifiers that combine into PII (zip + DOB + gender).

`data_retention` (Art. 5(1)(e)): PII stored without TTL; missing deletion logic; indefinite caches; audit logs without retention; schema columns without retention metadata or scheduled-purge job.

`consent_bypass` (Art. 6, 7): processing without verified consent, OR consent check located AFTER the processing it should gate, OR commented-out consent verification.

`third_party_sharing` (Art. 28): personal data sent to external services without anonymization or appropriate safeguards. Distinguish from cross_border_transfer — this category is about any third-party send; that one is specifically about sends across the EEA boundary.

`re_identification_risk` (Art. 4(5)): unsalted hashing of identifiers, quasi-identifier exports (zip + DOB + gender uniquely identifies 87% of the US population), reversible encoding (base64, hex, ROT13) labelled as anonymization.

`data_minimization` (Art. 5(1)(c)): collecting more PII than the operation needs; storing PII columns that are never read by application code; OAuth scope requests broader than needed (full_profile when only email is used).

`purpose_creep` (Art. 5(1)(b)): personal data collected for one purpose used for an unrelated purpose without re-consent. Look for cross-module data transfers and parameter names that mismatch the sink (e.g., shipping_address reaching ad_targeting_engine, support_transcript reaching training_corpus).

`insecure_storage` (Art. 32): PII stored without encryption-at-rest in a sensitive column (e.g., ssn, password as plain String); database connection string with sslmode=disable carrying PII over the wire. Safe: KMS-managed encryption-at-rest, TLS-enforced connection strings, bcrypt-hashed password columns.

`cross_border_transfer` (Art. 44–49): PII transferred from the EEA to a third country with no documented safeguard — no Standard Contractual Clauses (SCCs), no adequacy decision, no Binding Corporate Rules (BCR). Safe: explicit reference to a documented SCC / BCR / adequacy basis adjacent to the transfer call.

`right_to_erasure` (Art. 17 / CCPA §1798.105): user-account API exposes create/read/update but no delete endpoint; delete_user() only sets a deleted-at flag (soft-delete) with PII fields persisting; user row deleted but PII in derived tables (comments, activity_log, payment_history) not cascaded. Safe: explicit cascade-delete across all PII-bearing tables triggered by an erasure endpoint.

3.3 Cross-Surface Findings

If a single line/flow constitutes both a security AND a privacy issue (e.g., a logging statement that exposes both a session token and an email), emit TWO findings — one per surface — sharing the same flow_id. This preserves the option to deduplicate downstream.


Phase 4: Validator and False-Positive Filtering

Each candidate finding is independently checked against seven suppression rules. Suppress if ANY rule fires (except R7 unsuppressible-evidence, which overrides).

R1. Concrete violation path. Construct an end-to-end path from source to sink with no protective transformation. If you cannot, suppress.

R3. Test/documentation scope. Suppress credentials and PII findings in test/, tests/, __tests__/, spec/, fixtures/, *_test.py, *.spec.ts — but NOT logic findings (auth bypass in a test endpoint may indicate production flaw).

R4. Trusted source. Values from os.environ, process.env, std::env, compile-time constants are not hardcoded secrets.

R5. Transformation adequacy (privacy). If the transformation state at the sink is salted_hash, k-aggregated (k ≥ 5), encrypted with KMS-managed key, or tokenized via vault, suppress the privacy finding. unsalted_hash, base64, hex are NOT adequate.

R6. Same-purpose reuse. Personal data reused for the same purpose for which it was collected (registration email → password reset) is not purpose creep. Different purpose (registration email → ad-targeting cohort) is.

R7. Unsuppressible evidence. The following are HIGH severity regardless of context:

  • Literal SECRET_KEY = "...", PRIVATE_KEY = "...", aws_secret_access_key = "..."
  • Plain-text national-ID numbers (SSN format \d{3}-\d{2}-\d{4})
  • Plain-text passwords stored or transmitted without hashing

Confidence threshold. Findings with confidence < 0.8 are NOT included in the output. Findings between 0.6 and 0.8 should be omitted entirely (do not emit them as advisory).


Phase 5: Standards Mapping

For each surviving finding, attach the appropriate reference identifier:

  • Security: CWE-ID (e.g., "cwe": "CWE-89")
  • Privacy: GDPR article reference (e.g., "regulatory_basis": "GDPR Art. 5(1)(f)")

Provide a one-paragraph remediation directly actionable by the developer.


Category Alias Normalization

When you decide on a category, use exactly one of these 30 canonical strings:

Security (20): sql_injection, command_injection, path_traversal, xss, xss_dom, auth_bypass, hardcoded_secret, deserialization, eval_injection, infinite_loop, agent_csrf, second_order_sqli, ssrf, open_redirect, crypto_misuse, xxe, ssti, csrf, race_condition, insecure_logging.

Privacy (10): pii_leakage, data_retention, consent_bypass, third_party_sharing, re_identification_risk, data_minimization, purpose_creep, insecure_storage, cross_border_transfer, right_to_erasure.

Do NOT emit sub-categories like sql_injection_fstring, logging_pii, weak_md5, or right_to_be_forgotten — map to the canonical category and use that string. The runner's alias table maps common surface forms back to the canonical labels, but you should emit the canonical string directly when possible.


Output Discipline

  • Return ONLY a JSON array. No prose, no markdown fences, no explanation outside the JSON.
  • Empty array [] if the file has no high-confidence findings.
  • One finding per (file, line, category) triple. For cross-surface findings on the same line, use the shared flow_id mechanism (Phase 3.3).
  • Do not over-explain. The description field is one sentence. The remediation field is one sentence.

Install & Usage

1
Create the skills directory
mkdir -p .claude/skills
2
Download the skill file
mkdir -p .claude/skills && curl -o .claude/skills/secpriv-skill.md https://raw.githubusercontent.com/facebookresearch/secpriv-skill/main/SKILL.md
3
Invoke in Claude Code
/secpriv-skill
View source on GitHub
securitycode-reviewagentpython

Security Audits

LicenseUnknownSourceWarnRepositoryPass

Frequently Asked Questions

What is secpriv-skill?

Unified LLM-agent skill for code review covering both security (CWE) and privacy (GDPR) through one detector-validator methodology. Ships the SKILL.md, a 128-case Python benchmark across 30 canonical categories with a held-out true-negative subset, and the evaluation harness from the SecPriv paper.

How to install secpriv-skill?

To install secpriv-skill: create the skills directory (mkdir -p .claude/skills), then run: mkdir -p .claude/skills && curl -o .claude/skills/secpriv-skill.md https://raw.githubusercontent.com/facebookresearch/secpriv-skill/main/SKILL.md. Finally, /secpriv-skill in Claude Code.

What is secpriv-skill best for?

secpriv-skill is a skill categorized under General. It is designed for: security, code-review, agent, python. Created by facebookresearch.