agentleFS
medy-gribkovmedy-gribkov/arcana/skills/security-review/SKILL.md

security-review

Skill1 starsChanged 8 months agoApache-2.0View on GitHub

Reviews code for security flaws and hardens applications, covering OWASP Top 10 risks, injection (SQL, XSS, command), authentication and authorization, secrets handling and rotation, dependency scanning, input validation, CORS, CSRF, rate limiting and security headers, with examples in JavaScript, Python and Go. Use when reviewing a diff or file for vulnerabilities, hardening login or session code, setting up headers, CORS or rate limits, or auditing how secrets and dependencies are handled.

  • Reads credentials

We split this skill into the rules it gives an agent

8 rules. 0 are common, 7 are distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 167 edited versions we read keep these.

  • 7

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Never store JWTs in localStorage (XSS-accessible).

    line 123
  2. Distinctive:

    Never put sensitive data in JWT payload (it's base64, not encrypted).

    line 124
  3. Very specific:

    Always validate the alg header server-side.

    line 125
  4. Distinctive:

    IDOR prevention: Always check that the authenticated user owns or has permission to access the resource identified by the URL parameter.

    line 162
  5. Distinctive:

    Runtime secrets: Use environment variables or a secrets manager (AWS SSM, Vault, Doppler).

    line 183
  6. Distinctive:

    Add new secret to secrets manager

    line 196
  7. Distinctive:

    Update clients to use new secret

    line 198
  8. Distinctive:

    Remove old secret after grace period (24-48 hours)

    line 199

“Common” is measured: kept by at least 6 in 10 of the 167 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

You are a security engineer specializing in application security, code review, and vulnerability prevention across web, API, and systems code.

Use this skill when

  • Reviewing code for security vulnerabilities
  • Hardening authentication, authorization, or input handling
  • Setting up security headers, CORS, CSRF protection
  • Configuring dependency scanning or secrets management
  • Designing rate limiting or abuse prevention

OWASP Top 10 Quick Reference (2021 edition)

The 2025 edition reorders this list: it adds software supply chain failures and mishandling of exceptional conditions, and folds SSRF into broken access control. The defenses below still apply. Check owasp.org/Top10 for the current numbering before you cite an A-number in a report.

#VulnerabilityPrimary Defense
A01Broken Access ControlDeny by default, server-side checks on every request
A02Cryptographic FailuresTLS everywhere, bcrypt/argon2 for passwords, no secrets in code
A03InjectionParameterized queries, context-aware output encoding
A04Insecure DesignThreat modeling, secure defaults, least privilege
A05Security MisconfigurationMinimal installs, disable defaults, automate config
A06Vulnerable ComponentsSCA scanning (dependabot/snyk), pin versions, update regularly
A07Auth FailuresMFA, credential stuffing protection, secure session management
A08Data Integrity FailuresVerify signatures, use SRI, secure CI/CD
A09Logging FailuresLog security events, don't log secrets, monitor alerts
A10SSRFAllowlist outbound hosts, validate/sanitize URLs

Injection Prevention

SQL Injection

# BAD: string concatenation
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")

# GOOD: parameterized query
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

# GOOD: ORM (SQLAlchemy)
user = session.query(User).filter(User.id == user_id).first()
// BAD: template literal in query
db.query(`SELECT * FROM users WHERE email = '${email}'`);

// GOOD: parameterized
db.query("SELECT * FROM users WHERE email = $1", [email]);
// BAD: fmt.Sprintf in query
db.Query(fmt.Sprintf("SELECT * FROM users WHERE id = %d", id))

// GOOD: parameterized
db.Query("SELECT * FROM users WHERE id = $1", id)

XSS Prevention

// BAD: innerHTML with user data
element.innerHTML = userInput;

// GOOD: textContent (no HTML parsing)
element.textContent = userInput;

// React: NEVER use dangerouslySetInnerHTML with user data
// If you must render HTML, sanitize with DOMPurify:
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />

// CSP header to block inline scripts:
// Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'

Command Injection

# BAD: shell=True with user input
subprocess.run(f"convert {filename} output.png", shell=True)

# GOOD: pass args as list, no shell
subprocess.run(["convert", filename, "output.png"], shell=False)

# GOOD: validate input against allowlist
ALLOWED = re.compile(r'^[a-zA-Z0-9_\-]+\.png$')
if not ALLOWED.match(filename):
    raise ValueError("Invalid filename")

Authentication Patterns

Password Hashing

# Use argon2 (preferred) or bcrypt. NEVER MD5/SHA for passwords.
from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=4)
hash = ph.hash(password)
ph.verify(hash, password)  # raises on mismatch

JWT Best Practices

// Sign with RS256 (asymmetric) for services, HS256 for simple apps
const token = jwt.sign(
  { sub: user.id, role: user.role },
  privateKey,
  { algorithm: "RS256", expiresIn: "15m" } // short-lived access tokens
);

// ALWAYS validate: algorithm, expiration, issuer, audience
const payload = jwt.verify(token, publicKey, {
  algorithms: ["RS256"],     // prevent algorithm confusion attack
  issuer: "myapp",
  audience: "myapp-api",
});

JWT rules:

  • Access tokens: 15 min max. Refresh tokens: days, stored server-side, rotatable.
  • Never store JWTs in localStorage (XSS-accessible). Use httpOnly, Secure, SameSite=Strict cookies.
  • Never put sensitive data in JWT payload (it's base64, not encrypted).
  • Always validate the alg header server-side. Reject none algorithm.

Session Security

// Express session config
app.use(session({
  secret: process.env.SESSION_SECRET, // 32+ random bytes
  name: "__Host-sid",                 // __Host- prefix enforces Secure + no Domain
  cookie: {
    httpOnly: true,
    secure: true,
    sameSite: "strict",
    maxAge: 3600000,                  // 1 hour
  },
  resave: false,
  saveUninitialized: false,
}));

Authorization

Principle: deny by default, check on every request, server-side only.

# BAD: checking permissions client-side only
# BAD: checking only "is authenticated" without role/ownership check

# GOOD: middleware that checks ownership
async def get_document(request: Request, doc_id: int) -> Document:
    doc = await db.get_document(doc_id)
    if doc is None:
        raise HTTPException(404)  # don't reveal existence
    if doc.owner_id != request.user.id and request.user.role != "admin":
        raise HTTPException(404)  # 404 not 403 to prevent enumeration
    return doc

IDOR prevention: Always check that the authenticated user owns or has permission to access the resource identified by the URL parameter. Never trust client-supplied IDs without ownership verification.

Secrets Management

# NEVER commit secrets. Use .gitignore + pre-commit scanning.
# .gitignore
.env
*.pem
*.key
credentials.json

# Pre-commit: gitleaks
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks

Runtime secrets: Use environment variables or a secrets manager (AWS SSM, Vault, Doppler). Never hardcode. Never log secrets. Rotate regularly.

Secrets Rotation

Rotation schedule:

  • API keys: Every 90 days
  • Database passwords: Every 90 days or on personnel changes
  • JWT signing keys: Every 6-12 months
  • TLS certificates: Automated via Let's Encrypt (90-day expiry)
  • Service account tokens: Every 30-90 days

Rotation process:

  1. Generate new secret (key/password/token)
  2. Add new secret to secrets manager
  3. Deploy code that supports BOTH old and new secrets
  4. Update clients to use new secret
  5. Remove old secret after grace period (24-48 hours)
# Example: rotate a database password with zero downtime.
# Keep two roles with identical grants (myapp_a, myapp_b). Only one is live.
# Changing the live role's password would break running instances at once.

# 1. Generate the new password and set it on the idle role (myapp_b).
#    Pipe the SQL through stdin so the password never shows in the process list.
NEW_PASS=$(openssl rand -base64 32)
printf "ALTER ROLE myapp_b PASSWORD '%s';\n" "$NEW_PASS" | psql "$ADMIN_DATABASE_URL"

# 2. Store it encrypted (SecureString), not as a plain String parameter
aws ssm put-parameter --name /prod/db/password --type SecureString --value "$NEW_PASS" --overwrite
unset NEW_PASS

# 3. Rolling restart so instances pick up the new role and password
kubectl rollout restart deployment/myapp
kubectl rollout status deployment/myapp

# 4. After every instance is on myapp_b and the grace period has passed,
#    disable login for the old role (myapp_a). Next rotation swaps the roles.
# GOOD: secrets from environment with validation
import os

def get_required_env(key: str) -> str:
    value = os.environ.get(key)
    if not value:
        raise RuntimeError(f"Required env var {key} is not set")
    return value

DATABASE_URL = get_required_env("DATABASE_URL")

Security Headers

# Nginx or reverse proxy
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com" always;

CORS Configuration

// BAD: allow everything
app.use(cors({ origin: "*", credentials: true })); // credentials + wildcard = browser rejects

// GOOD: explicit allowlist
app.use(cors({
  origin: ["https://myapp.com", "https://staging.myapp.com"],
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true,
  maxAge: 86400,
}));

CSRF Protection

For cookie-based auth (sessions), use the synchronizer token pattern or SameSite cookies:

// Option 1: SameSite=Strict cookies (sufficient for most apps)
// Option 2: CSRF token (needed if SameSite=Lax or cross-subdomain)

// Modern approach: csrf-csrf (csurf is deprecated)
import { doubleCsrf } from "csrf-csrf";

const { generateToken, doubleCsrfProtection } = doubleCsrf({
  getSecret: () => process.env.CSRF_SECRET,
  cookieName: "__Host-csrf",
  cookieOptions: { httpOnly: true, secure: true, sameSite: "strict" },
  getTokenFromRequest: (req) => req.headers["x-csrf-token"],
});

app.use(doubleCsrfProtection);

// Alternative: Double-submit cookie pattern (manual)
app.post("/api/action", (req, res) => {
  const tokenFromCookie = req.cookies.csrfToken;
  const tokenFromHeader = req.headers["x-csrf-token"];
  if (tokenFromCookie !== tokenFromHeader) return res.status(403).send("CSRF validation failed");
  // Process request
});

Token-based auth (Bearer JWT in Authorization header) is inherently CSRF-safe because browsers don't auto-attach it.

Rate Limiting

// Express rate limiting
import rateLimit from "express-rate-limit";

// General API rate limit
app.use("/api/", rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  limit: 100,
  standardHeaders: true,
  legacyHeaders: false,
  keyGenerator: (req) => req.ip, // or req.user?.id for authenticated
}));

// Strict limit on auth endpoints
app.use("/api/auth/", rateLimit({
  windowMs: 15 * 60 * 1000,
  limit: 5,                     // 5 login attempts per 15 min
  skipSuccessfulRequests: true, // don't count successful logins
}));

Dependency Scanning

# JavaScript: npm audit + Snyk
npm audit --omit=dev
npx snyk test

# Python: pip-audit
pip-audit

# Go: govulncheck (official)
govulncheck ./...

# CI: GitHub Dependabot + CodeQL
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

Input Validation Checklist

  1. Validate on the server. Client-side validation is UX, not security.
  2. Allowlist over denylist. Define what IS valid, not what isn't.
  3. Validate type, length, range, and format for every input field.
  4. Reject unexpected fields (strict schema parsing with Pydantic/Zod).
  5. Sanitize for output context (HTML encode for HTML, SQL parameterize for SQL).
  6. File uploads: validate MIME type by content (not extension), limit size, store outside webroot, randomize filenames.
  7. URLs: validate scheme (https only), resolve and check against SSRF allowlist.

Download now (ZIP)

  • Other versions192 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 192

    1. affaan-m/ECC★ 246ksame name
    2. github/awesome-copilot★ 41ksame name
    3. langfuse/langfuse★ 37ksame name
    4. nearai/ironclaw★ 13ksame name
    5. joe-bell/cva★ 6.9ksame name
    6. doorkeeper-gem/doorkeeper★ 5.5ksame name
    7. compozy/compozy★ 2.8ksame name
    8. getsentry/sentry-python★ 2.2ksame name
    9. trycompai/comp★ 2ksame name
    10. xu-xiang/everything-claude-code-zh★ 1.9ksame name

    182 more not listed

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • Used alongsideProjects with it also use dependency-cve-sweep, fleet-standard-adoption and 10 more.

    Other files people use alongside this one.

    Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
    • berntpoppSkill

      dependency-cve-sweep

      Use when handling Dependabot PRs, CodeQL/Trivy alerts, or doing a routine dependency/CVE sweep for this repo.

      Also in 12 projects that use this

    • berntpoppSkill

      fleet-standard-adoption

      Use when bringing this repo into compliance with a GeneFoundry fleet standard (tool-naming, response-envelope, container-hardening, versioning, MCP transport, or logging) or closing its tracking issue.

      Also in 12 projects that use this

    • niloykumarbarmanSkill

      agent-sort

      Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

      Also in 12 projects that use this

    • ysyecustSkill

      brand-discovery

      Use when a brand needs to discover or articulate its identity through structured multi-session interviews. Covers purpose, positioning, audience, personality, voice, narrative, and founder-brand tension across 8 modules using laddering, 5 Whys, and projective techniques. Produces a resumable session with disk-persisted state and a master brandbook (90_SYNTHESIS.md).

      Also in 5 projects that use this

    • InfopibeSkill

      everything-claude-code

      Also in 11 projects that use this

    • junimnjwSkill

      ralphinho-rfc-pipeline

      RFC-driven multi-agent DAG execution pattern with quality gates, merge queues, and work unit orchestration.

      Also in 10 projects that use this

    • +2 more from berntpopp
  • In this repository21 other files it gives its agents.

Does it work?

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

Reviews can't be read right now.

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.