Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use this skill when implementing security measures or conducting security audits. Provides OWASP Top 10 mitigations, authentication patterns, input validation strategies, and compliance guidelines. Ensures applications are secure against common vulnerabilities.
.claude/skills/aiskillstore-security-checklist/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 955% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 337% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 1010% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 239% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 304% | 0% |
This skill provides comprehensive security guidance for building secure applications. Whether performing a security audit, implementing new features, or hardening existing systems, this framework helps identify and mitigate common vulnerabilities.
When to use this skill:
This skill requires the following tools to be installed on your system:
npm auditpip install pip-auditpip-auditbrew install semgreppip install semgrepsemgrep --config=auto .pip install banditbandit -r .brew install trufflesecurity/trufflehog/trufflehoggo install github.com/trufflesecurity/trufflehog/v3@latesttrufflehog filesystem .bash# Verify Node.js & npm node --version npm --version # Verify Python & pip python --version pip --version # Verify pip-audit pip-audit --version # Verify optional tools semgrep --version bandit --version trufflehog --version
Note: The skill will automatically detect which tools are available and use appropriate commands for your project type.
Vulnerability: Users can access resources they shouldn't.
Examples:
python# ❌ Bad: No authorization check @app.route('/api/users/<user_id>') def get_user(user_id): return db.query(f"SELECT * FROM users WHERE id = {user_id}") # ✅ Good: Verify user can access this resource @app.route('/api/users/<user_id>') @login_required def get_user(user_id): current_user = get_current_user() if current_user.id != user_id and not current_user.is_admin: abort(403, "Forbidden") return db.query("SELECT * FROM users WHERE id = ?", [user_id])
Mitigations:
Vulnerability: Sensitive data exposed due to weak or missing encryption.
Examples:
python# ❌ Bad: Storing passwords in plaintext user.password = request.form['password'] # ✅ Good: Hashing passwords with bcrypt from bcrypt import hashpw, gensalt hashed = hashpw(password.encode('utf-8'), gensalt()) user.password_hash = hashed # ❌ Bad: Using weak hashing (MD5, SHA1) import hashlib password_hash = hashlib.md5(password.encode()).hexdigest() # ✅ Good: Using strong hashing (bcrypt, argon2, scrypt) from argon2 import PasswordHasher ph = PasswordHasher() password_hash = ph.hash(password)
Mitigations:
secrets module in Python)Vulnerability: Untrusted data sent to an interpreter as part of a command.
SQL Injection:
python# ❌ Bad: String concatenation (vulnerable to SQL injection) query = f"SELECT * FROM users WHERE email = '{email}'" db.execute(query) # ✅ Good: Parameterized queries query = "SELECT * FROM users WHERE email = ?" db.execute(query, [email])
Command Injection:
python# ❌ Bad: Shell=True with user input import subprocess filename = request.form['filename'] subprocess.run(f"cat {filename}", shell=True) # ✅ Good: Avoid shell, use list arguments subprocess.run(["cat", filename], shell=False)
Mitigations:
eval(), exec(), shell=TrueVulnerability: Design flaws that can't be fixed with implementation.
Examples:
Mitigations:
Vulnerability: Default configs, incomplete setups, verbose errors.
Examples:
python# ❌ Bad: Debug mode in production app.debug = True # ✅ Good: Debug mode only in development app.debug = os.getenv('FLASK_ENV') == 'development' # ❌ Bad: Verbose error messages @app.errorhandler(Exception) def handle_error(e): return str(e), 500 # Exposes stack traces # ✅ Good: Generic error messages @app.errorhandler(Exception) def handle_error(e): logger.error(f"Error: {e}") return {"error": "Internal server error"}, 500
Mitigations:
Vulnerability: Using libraries with known vulnerabilities.
Mitigations:
bash# Check for vulnerabilities npm audit npm audit fix # Python pip-audit safety check
Best Practices:
Vulnerability: Weak authentication, credential stuffing, session hijacking.
Examples:
python# ❌ Bad: Weak password requirements if len(password) < 6: return "Password too short" # ✅ Good: Strong password requirements import re def validate_password(password): if len(password) < 12: return "Password must be at least 12 characters" if not re.search(r"[A-Z]", password): return "Password must contain uppercase letter" if not re.search(r"[a-z]", password): return "Password must contain lowercase letter" if not re.search(r"[0-9]", password): return "Password must contain a number" return None # Valid
Mitigations:
Vulnerability: Code or infrastructure updates without integrity verification.
Examples:
Mitigations:
html<script src="https://cdn.example.com/lib.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" crossorigin="anonymous"></script>
Vulnerability: Insufficient logging prevents detection of breaches.
Examples:
python# ❌ Bad: No logging @app.route('/login', methods=['POST']) def login(): user = authenticate(email, password) return {"token": create_token(user)} # ✅ Good: Log security events import logging @app.route('/login', methods=['POST']) def login(): email = request.form['email'] user = authenticate(email, password) if user: logger.info(f"Successful login: {email}") return {"token": create_token(user)} else: logger.warning(f"Failed login attempt: {email}") return {"error": "Invalid credentials"}, 401
Mitigations:
Vulnerability: Application fetches remote resources without validating URL.
Examples:
python# ❌ Bad: Fetching user-provided URL without validation import requests @app.route('/fetch') def fetch(): url = request.args.get('url') response = requests.get(url) # Can access internal services! return response.text # ✅ Good: Validate URL and use allowlist from urllib.parse import urlparse ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com'] @app.route('/fetch') def fetch(): url = request.args.get('url') parsed = urlparse(url) if parsed.hostname not in ALLOWED_DOMAINS: abort(400, "Invalid domain") response = requests.get(url, timeout=5) return response.text
Mitigations:
python# ✅ Secure password hashing from argon2 import PasswordHasher ph = PasswordHasher() # Hashing password_hash = ph.hash(password) # Verification try: ph.verify(password_hash, password) # Password correct except: # Password incorrect pass
Requirements:
python# ✅ Secure session cookies app.config['SESSION_COOKIE_SECURE'] = True # HTTPS only app.config['SESSION_COOKIE_HTTPONLY'] = True # No JavaScript access app.config['SESSION_COOKIE_SAMESITE'] = 'Strict' # CSRF protection app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=1)
pythonimport jwt from datetime import datetime, timedelta # ✅ Secure JWT generation def create_token(user_id): payload = { 'user_id': user_id, 'exp': datetime.utcnow() + timedelta(hours=1), # Expiration 'iat': datetime.utcnow(), # Issued at } return jwt.encode(payload, SECRET_KEY, algorithm='HS256') # ✅ Secure JWT verification def verify_token(token): try: payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256']) return payload['user_id'] except jwt.ExpiredSignatureError: return None # Token expired except jwt.InvalidTokenError: return None # Invalid token
python# ✅ Allowlist validation def validate_sort_column(column): allowed_columns = ['name', 'email', 'created_at'] if column not in allowed_columns: raise ValueError("Invalid sort column") return column # ✅ Type validation from pydantic import BaseModel, EmailStr, constr class UserCreate(BaseModel): email: EmailStr name: constr(min_length=2, max_length=100) age: int = Field(ge=0, le=150) # Usage try: user = UserCreate(**request.json) except ValidationError as e: return {"errors": e.errors()}, 400
python# ✅ HTML escaping from markupsafe import escape @app.route('/comment', methods=['POST']) def create_comment(): content = escape(request.form['content']) db.execute("INSERT INTO comments (content) VALUES (?)", [content]) return {"status": "ok"}
python# ✅ Set security headers @app.after_request def set_security_headers(response): response.headers['Content-Security-Policy'] = "default-src 'self'" response.headers['X-Content-Type-Options'] = 'nosniff' response.headers['X-Frame-Options'] = 'DENY' response.headers['X-XSS-Protection'] = '1; mode=block' response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' return response
Automated security scanning catches vulnerabilities early. This section teaches agents HOW to run security tools and record evidence.
When to auto-scan:
1. Identify Scan Type (dependencies, code, configuration)
2. Run Appropriate Tool (npm audit, pip-audit, semgrep)
3. Capture Results (exit codes, vulnerability counts)
4. Record Evidence in Context
5. Escalate Critical Findingsbash# Run npm audit and capture results npm audit --json > security-audit.json EXIT_CODE=$? # Check exit code if [ $EXIT_CODE -eq 0 ]; then echo "✅ No vulnerabilities found" else echo "⚠️ Vulnerabilities detected (exit code: $EXIT_CODE)" fi # Parse for critical/high vulnerabilities CRITICAL=$(npm audit --json | jq '.metadata.vulnerabilities.critical') HIGH=$(npm audit --json | jq '.metadata.vulnerabilities.high') if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ]; then echo "🚨 CRITICAL: $CRITICAL critical, $HIGH high severity vulnerabilities" fi
Record evidence:
javascriptcontext.quality_evidence = context.quality_evidence || { last_updated: new Date().toISOString() }; context.quality_evidence.security_scan = { executed: true, tool: 'npm audit', critical: 2, high: 5, moderate: 10, low: 3, timestamp: new Date().toISOString() }; context.writeContext();
bash# Using pip-audit (official tool) pip-audit --format=json > security-audit.json EXIT_CODE=$? # Alternative: using safety safety check --json > security-audit.json # Check for critical vulnerabilities CRITICAL_COUNT=$(cat security-audit.json | jq '[.vulnerabilities[] | select(.severity == "critical")] | length')
Evidence recording:
python# Record in context context['quality_evidence'] = context.get('quality_evidence', {}) context['quality_evidence']['security_scan'] = { 'executed': True, 'tool': 'pip-audit', 'critical': critical_count, 'high': high_count, 'moderate': moderate_count, 'low': low_count, 'timestamp': datetime.now().isoformat() }
bash# Run Semgrep with security rules semgrep --config=auto --json > semgrep-results.json EXIT_CODE=$? # Count findings by severity CRITICAL=$(cat semgrep-results.json | jq '[.results[] | select(.extra.severity == "ERROR")] | length') HIGH=$(cat semgrep-results.json | jq '[.results[] | select(.extra.severity == "WARNING")] | length')
Common security patterns detected:
bash# Run Bandit for Python security issues bandit -r . -f json -o bandit-report.json EXIT_CODE=$? # Count high/medium severity issues HIGH=$(cat bandit-report.json | jq '[.results[] | select(.issue_severity == "HIGH")] | length') MEDIUM=$(cat bandit-report.json | jq '[.results[] | select(.issue_severity == "MEDIUM")] | length')
bash# Scan for secrets in git history trufflehog git file://. --json > secrets-scan.json # Check if any secrets found SECRET_COUNT=$(cat secrets-scan.json | jq '. | length') if [ "$SECRET_COUNT" -gt 0 ]; then echo "🚨 CRITICAL: $SECRET_COUNT secrets detected!" # Extract types cat secrets-scan.json | jq -r '.[] | .DetectorType' | sort | uniq fi
Common secrets detected:
bash# Scan Docker images with Trivy trivy image myapp:latest --format json > trivy-scan.json # Count vulnerabilities CRITICAL=$(cat trivy-scan.json | jq '[.Results[].Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length') HIGH=$(cat trivy-scan.json | jq '[.Results[].Vulnerabilities[]? | select(.Severity == "HIGH")] | length')
After running security scans, record evidence in shared context:
typescriptimport { ContextManager } from '../lib/context/context-manager.js'; const context = new ContextManager(); // Record security scan evidence context.recordSecurityScanEvidence({ executed: true, tool: 'npm audit + semgrep', critical: 2, high: 5, moderate: 10, low: 3, timestamp: new Date().toISOString(), scan_details: { dependency_scan: { tool: 'npm audit', critical: 2, high: 3, vulnerabilities: [ { id: 'GHSA-xxxx', severity: 'critical', package: 'lodash@4.17.19' } ] }, code_scan: { tool: 'semgrep', critical: 0, high: 2, patterns: ['sql-injection', 'xss'] } } });
MANDATORY: Escalate if critical/high vulnerabilities found
javascript// After scanning const securityEvidence = context.getQualityEvidence()?.security_scan; if (!securityEvidence) { console.log('⚠️ WARNING: No security scan performed'); return; } // Check for critical/high vulnerabilities if (securityEvidence.critical > 0 || securityEvidence.high > 5) { console.log('🚨 SECURITY ALERT: Critical vulnerabilities detected'); // BLOCK deployment const blockingReasons = []; if (securityEvidence.critical > 0) { blockingReasons.push(`${securityEvidence.critical} CRITICAL vulnerabilities`); } if (securityEvidence.high > 5) { blockingReasons.push(`${securityEvidence.high} HIGH vulnerabilities (>5 threshold)`); } // Escalate to user console.log('BLOCKED: ' + blockingReasons.join(', ')); console.log('Action Required: Fix critical/high vulnerabilities before proceeding'); return { approved: false, blockingReasons }; } console.log('✅ Security scan passed');
Escalation Thresholds:
Use this checklist when performing security reviews:
markdown## Security Scan Checklist - [ ] **Dependency Scan**: npm audit / pip-audit executed - [ ] **Exit Code Captured**: 0 = clean, non-zero = vulnerabilities - [ ] **Severity Counts**: Critical, High, Moderate, Low recorded - [ ] **Evidence Recorded**: Added to context.quality_evidence.security_scan - [ ] **Critical Threshold Check**: BLOCK if critical > 0 or high > 5 - [ ] **Scan Results Saved**: JSON output saved for review - [ ] **False Positives Noted**: Known safe issues documented - [ ] **Fix Recommendations**: Upgrade paths or mitigations documented
When Code Quality Reviewer agent performs review:
markdown1. Run linter/type checker (already implemented) 2. **AUTO-TRIGGER**: Run security scan - npm audit (for JS/TS projects) - pip-audit (for Python projects) 3. Capture and record evidence 4. Check critical thresholds 5. BLOCK approval if critical vulnerabilities found 6. Include security scan summary in review output
Example output:
## Code Quality Review
### Lint & Type Check: ✅ PASS
- ESLint: 0 errors, 2 warnings
- TypeScript: 0 errors
### Security Scan: ⚠️ WARNING
- Tool: npm audit
- Critical: 0
- High: 3
- Moderate: 8
- Low: 2
**Recommendation**: 3 high severity vulnerabilities detected. Run `npm audit fix` to address:
- lodash@4.17.19 (Prototype Pollution - High)
- minimist@1.2.5 (Prototype Pollution - High)
- axios@0.21.1 (SSRF - High)
### Overall Status: BLOCKED
Security vulnerabilities must be resolved before approval.JavaScript/TypeScript:
bash# npm audit (built-in, no install needed) npm audit # Semgrep pip install semgrep # TruffleHog docker run --rm trufflesecurity/trufflehog:latest
Python:
bash# pip-audit (official tool) pip install pip-audit # safety pip install safety # Bandit pip install bandit
General:
bash# Trivy (containers, dependencies, code) brew install aquasecurity/trivy/trivy # Gitleaks (secrets) brew install gitleaks
When securing an application:
Skill Version: 1.0.0 Last Updated: 2025-10-31 Maintained by: AI Agent Hub Team
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-11 | pass→pass | 15,097 | 12,926 | -14% | 1 | 1 | 0% | 1,774 | 8,091 | +356% | 0 | 0 | — |
case-01 | fail→pass | 11,341 | 33,757 | +198% | 1 | 1 | 0% | 1,172 | 12,369 | +955% | 0 | 0 | — |
case-02 | fail→pass | 12,192 | 10,245 | -16% | 1 | 1 | 0% | 1,967 | 8,599 | +337% | 0 | 0 | — |
case-03 | fail→pass | 8,996 | 9,129 | +1% | 1 | 1 | 0% | 659 | 7,316 | +1010% | 0 | 0 | — |
case-04 | pass→pass | 4,415 | 9,889 | +124% | 1 | 1 | 0% | 684 | 7,200 | +953% | 0 | 0 | — |
case-05 | pass→pass | 12,480 | 16,435 | +32% | 1 | 1 | 0% | 2,236 | 8,569 | +283% | 0 | 0 | — |
case-06 | fail→pass | 19,269 | 17,508 | -9% | 1 | 1 | 0% | 2,721 | 9,233 | +239% | 0 | 0 | — |
case-07 | pass→pass | 19,171 | 17,399 | -9% | 1 | 1 | 0% | 2,468 | 8,768 | +255% | 0 | 0 | — |
case-08 | pass→pass | 18,542 | 13,217 | -29% | 1 | 1 | 0% | 2,471 | 9,261 | +275% | 0 | 0 | — |
case-09 | fail→pass | 15,642 | 11,086 | -29% | 1 | 1 | 0% | 1,915 | 7,742 | +304% | 0 | 0 | — |
case-10 | fail→pass | 24,184 | 21,521 | -11% | 1 | 1 | 0% | 3,918 | 10,654 | +172% | 0 | 0 | — |
case-12 | pass→fail | 13,586 | 22,585 | +66% | 1 | 1 | 0% | 2,367 | 9,410 | +298% | 0 | 0 | — |
case-13 | fail→pass | 16,360 | 17,590 | +8% | 1 | 1 | 0% | 2,901 | 8,841 | +205% | 0 | 0 | — |
case-14 | fail→pass | 18,014 | 9,058 | -50% | 1 | 1 | 0% | 2,611 | 8,452 | +224% | 0 | 0 | — |
case-15 | pass→pass | 12,668 | 8,993 | -29% | 1 | 1 | 0% | 1,328 | 7,360 | +454% | 0 | 0 | — |
case-16 | pass→pass | 14,863 | 14,396 | -3% | 1 | 1 | 0% | 1,724 | 8,512 | +394% | 0 | 0 | — |
case-17 | pass→pass | 18,290 | 18,851 | +3% | 1 | 1 | 0% | 2,371 | 9,274 | +291% | 0 | 0 | — |
case-18 | pass→pass | 18,097 | 25,557 | +41% | 1 | 1 | 0% | 3,354 | 11,069 | +230% | 0 | 0 | — |
case-19 | pass→pass | 12,131 | 3,249 | -73% | 1 | 1 | 0% | 1,218 | 7,227 | +493% | 0 | 0 | — |
case-20 | pass→pass | 3,650 | 7,861 | +115% | 1 | 1 | 0% | 592 | 7,153 | +1108% | 0 | 0 | — |
case-21 | pass→pass | 16,331 | 23,996 | +47% | 1 | 1 | 0% | 3,140 | 10,676 | +240% | 0 | 0 | — |
case-22 | pass→pass | 21,794 | 20,337 | -7% | 1 | 1 | 0% | 2,250 | 8,865 | +294% | 0 | 0 | — |
case-23 | pass→pass | 27,708 | 31,014 | +12% | 1 | 1 | 0% | 3,661 | 11,728 | +220% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +30 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.