agentsclimarketplace

Sast patterns

Skill vibeeval/vibecosystem/skills/sast-patterns

AI software team for Claude Code - 138 agents, 295 skills, 73 hooks. Self-learning, multi-agent swarm, autonomous skill evolution.

Install
npx -y skills add vibeeval/vibecosystem --skill sast-patterns

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

Static Application Security Testing patterns, OWASP Top 10 checklist, language-specific vulnerability patterns, Semgrep rule writing guide, and CI/CD integration. Use when scanning code for security vulnerabilities or writing custom SAST rules.

SKILL.md

18.8 KB, as published. Nobody here has run it

SAST Patterns Skill

Comprehensive vulnerability pattern library for static application security testing. Covers OWASP Top 10, language-specific patterns, Semgrep custom rules, and CI/CD pipeline integration.

When to Activate

  • Running security scans on code
  • Writing custom Semgrep rules
  • Setting up CI/CD security gates
  • Reviewing code for security vulnerabilities
  • After sast-on-edit hook triggers
  • Before production deployment

OWASP Top 10 (2021) Checklist

A01: Broken Access Control

What to Look For:

  • Missing authorization checks on endpoints
  • Direct object reference without ownership validation
  • CORS misconfiguration allowing wildcard origins
  • Missing function-level access control
  • Metadata manipulation (JWT, cookies, hidden fields)

Detection Patterns:

// VULNERABLE: No authorization check
app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id)
  res.json(user)  // Anyone can access any user
})

// SECURE: Authorization verified
app.get('/api/users/:id', authenticate, async (req, res) => {
  if (req.user.id !== req.params.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Forbidden' })
  }
  const user = await db.users.findById(req.params.id)
  res.json(user)
})
# VULNERABLE: No permission check
@app.route('/admin/delete/<user_id>', methods=['DELETE'])
def delete_user(user_id):
    db.session.delete(User.query.get(user_id))
    db.session.commit()

# SECURE: Permission verified
@app.route('/admin/delete/<user_id>', methods=['DELETE'])
@login_required
@admin_required
def delete_user(user_id):
    db.session.delete(User.query.get(user_id))
    db.session.commit()

Semgrep Rules:

rules:
  - id: missing-auth-check
    patterns:
      - pattern: |
          app.$METHOD($PATH, async (req, res) => {
            ...
            $DB.$QUERY(...)
            ...
          })
      - pattern-not: |
          app.$METHOD($PATH, authenticate, ...)
    message: "Endpoint missing authentication middleware"
    severity: ERROR

A02: Cryptographic Failures

What to Look For:

  • Hardcoded secrets in source code
  • Weak hashing (MD5, SHA1) for passwords
  • Missing encryption for sensitive data at rest
  • HTTP instead of HTTPS
  • Weak TLS configuration

Detection Patterns:

// VULNERABLE: Weak password hashing
const hash = crypto.createHash('md5').update(password).digest('hex')

// SECURE: Strong password hashing
const hash = await bcrypt.hash(password, 12)
# VULNERABLE: Hardcoded secret
SECRET_KEY = "my-super-secret-key-123"

# SECURE: Environment variable
SECRET_KEY = os.environ.get('SECRET_KEY')
if not SECRET_KEY:
    raise ValueError("SECRET_KEY environment variable required")

Regex Patterns for Detection:

# API Keys
(?:api[_-]?key|apikey)\s*[:=]\s*['"][A-Za-z0-9_\-]{20,}['"]

# AWS Keys
(?:AKIA|ASIA)[A-Z0-9]{16}

# Generic Secrets
(?:password|passwd|pwd|secret|token)\s*[:=]\s*['"][^'"]{8,}['"]

# Private Keys
-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----

# JWT Tokens
eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+

A03: Injection

What to Look For:

  • SQL injection (string concatenation in queries)
  • Command injection (unsanitized shell execution)
  • NoSQL injection (MongoDB query operators in user input)
  • LDAP injection
  • Expression Language injection

Detection Patterns:

// SQL Injection
const query = `SELECT * FROM users WHERE id = ${userId}`  // VULNERABLE
const query = 'SELECT * FROM users WHERE id = $1'         // SECURE

// Command Injection
exec(`ping ${hostname}`)           // VULNERABLE
execFile('ping', [hostname])        // SECURE

// NoSQL Injection (MongoDB)
db.users.find({ email: req.body.email })        // VULNERABLE if email = {"$gt": ""}
db.users.find({ email: String(req.body.email) }) // SECURE: type coercion
# SQL Injection
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")  # VULNERABLE
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))  # SECURE

# Command Injection
os.system(f"convert {filename} output.png")                # VULNERABLE
subprocess.run(['convert', filename, 'output.png'], check=True)  # SECURE
// SQL Injection
db.Query("SELECT * FROM users WHERE id = " + userID)     // VULNERABLE
db.Query("SELECT * FROM users WHERE id = $1", userID)     // SECURE

// Command Injection
exec.Command("sh", "-c", userInput)                       // VULNERABLE
exec.Command("ping", "-c", "1", hostname)                 // SECURE
// SQL Injection
stmt.executeQuery("SELECT * FROM users WHERE id = " + id);          // VULNERABLE
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setString(1, id);                                                // SECURE

A04: Insecure Design

What to Look For:

  • Missing rate limiting on critical operations
  • No account lockout after failed attempts
  • Missing CAPTCHA on public forms
  • Business logic flaws
  • Missing transaction atomicity

Detection Patterns:

// VULNERABLE: No rate limiting on login
app.post('/login', async (req, res) => {
  const user = await authenticate(req.body)
  res.json({ token: generateToken(user) })
})

// SECURE: Rate limited login
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  message: 'Too many login attempts'
})
app.post('/login', loginLimiter, async (req, res) => {
  const user = await authenticate(req.body)
  res.json({ token: generateToken(user) })
})
# VULNERABLE: Race condition in balance check
balance = get_balance(user_id)
if balance >= amount:
    withdraw(user_id, amount)  # Another request could drain first

# SECURE: Atomic transaction
with db.transaction():
    balance = db.query("SELECT balance FROM accounts WHERE id = %s FOR UPDATE", user_id)
    if balance >= amount:
        db.execute("UPDATE accounts SET balance = balance - %s WHERE id = %s", amount, user_id)

A05: Security Misconfiguration

What to Look For:

  • Debug mode in production
  • Default credentials
  • Unnecessary features enabled
  • Missing security headers
  • Verbose error messages
  • Directory listing enabled

Detection Patterns:

// VULNERABLE: Debug mode
app.set('env', 'development')  // In production config

// VULNERABLE: Missing security headers
// No helmet or manual headers

// SECURE: Security headers
import helmet from 'helmet'
app.use(helmet())
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", 'data:', 'https:'],
  }
}))
# VULNERABLE: Debug in production
DEBUG = True  # In production settings

# VULNERABLE: Default secret key
SECRET_KEY = 'django-insecure-change-me'

# SECURE
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']

Security Headers Checklist:

Content-Security-Policy: default-src 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0
Strict-Transport-Security: max-age=31536000; includeSubDomains
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

A06: Vulnerable and Outdated Components

Detection Commands:

# Node.js
npm audit
npm audit --json
npm outdated

# Python
pip-audit
safety check
pip list --outdated

# Go
go list -m -u all
govulncheck ./...

# Java
mvn dependency-check:check
gradle dependencyCheckAnalyze

# Ruby
bundle audit check

A07: Identification and Authentication Failures

What to Look For:

  • Weak password policies
  • Missing MFA
  • Session fixation
  • Credential stuffing vulnerability
  • Plaintext password storage/comparison

Detection Patterns:

// VULNERABLE: Plaintext password comparison
if (password === user.password) { /* login */ }

// SECURE: Hashed comparison
const isValid = await bcrypt.compare(password, user.passwordHash)

// VULNERABLE: Weak session management
app.use(session({ secret: 'secret' }))

// SECURE: Strong session
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true,
    httpOnly: true,
    sameSite: 'strict',
    maxAge: 3600000
  }
}))

A08: Software and Data Integrity Failures

What to Look For:

  • Missing Subresource Integrity (SRI) on CDN scripts
  • Insecure deserialization
  • Missing code signing
  • Auto-update without integrity verification
  • CI/CD pipeline without integrity checks

Detection Patterns:

<!-- VULNERABLE: No SRI -->
<script src="https://cdn.example.com/lib.js"></script>

<!-- SECURE: With SRI -->
<script src="https://cdn.example.com/lib.js"
  integrity="sha384-abc123..."
  crossorigin="anonymous"></script>
# VULNERABLE: Insecure deserialization
import pickle
data = pickle.loads(user_input)

# SECURE: Use JSON
import json
data = json.loads(user_input)

A09: Security Logging and Monitoring Failures

What to Look For:

  • Missing audit logs for auth events
  • Sensitive data in logs
  • No log integrity protection
  • Missing alerting for suspicious activity

Detection Patterns:

// VULNERABLE: No logging
app.post('/login', async (req, res) => {
  const user = await authenticate(req.body)
  if (!user) return res.status(401).json({ error: 'Invalid' })
  res.json({ token: generateToken(user) })
})

// SECURE: Audit logging
app.post('/login', async (req, res) => {
  const user = await authenticate(req.body)
  if (!user) {
    logger.warn('Failed login attempt', {
      email: req.body.email,
      ip: req.ip,
      timestamp: new Date().toISOString()
    })
    return res.status(401).json({ error: 'Invalid credentials' })
  }
  logger.info('Successful login', { userId: user.id, ip: req.ip })
  res.json({ token: generateToken(user) })
})

// VULNERABLE: Sensitive data in logs
console.log('Login:', { email, password, token })

// SECURE: Redacted logs
console.log('Login:', { email, passwordProvided: !!password })

A10: Server-Side Request Forgery (SSRF)

What to Look For:

  • User-controlled URLs in server-side requests
  • Missing URL validation/whitelist
  • Internal service access via SSRF
  • Cloud metadata endpoint access

Detection Patterns:

// VULNERABLE: SSRF
const response = await fetch(req.query.url)

// SECURE: URL whitelist
const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com']
const url = new URL(req.query.url)
if (!ALLOWED_DOMAINS.includes(url.hostname)) {
  throw new Error('Domain not allowed')
}
// Also block internal IPs
const ip = await dns.resolve(url.hostname)
if (isPrivateIP(ip)) {
  throw new Error('Internal addresses not allowed')
}
const response = await fetch(url.toString())
# VULNERABLE: SSRF
response = requests.get(user_url)

# SECURE: URL validation
from urllib.parse import urlparse
parsed = urlparse(user_url)
if parsed.hostname not in ALLOWED_HOSTS:
    raise ValueError("Domain not allowed")
if is_private_ip(socket.gethostbyname(parsed.hostname)):
    raise ValueError("Internal addresses blocked")
response = requests.get(user_url, allow_redirects=False)

Semgrep Custom Rule Writing Guide

Basic Rule Structure

rules:
  - id: rule-unique-id
    pattern: |
      eval($USER_INPUT)
    message: "eval() with dynamic input detected - potential RCE"
    languages: [javascript, typescript]
    severity: ERROR
    metadata:
      cwe:
        - CWE-94
      owasp:
        - A03:2021
      category: security
      confidence: HIGH

Pattern Operators

# pattern: match exactly
- pattern: eval($X)

# pattern-not: exclude matches
- pattern-not: eval("static-string")

# patterns: AND (all must match)
- patterns:
    - pattern: $DB.query($SQL)
    - pattern-not: $DB.query($SQL, $PARAMS)

# pattern-either: OR (any can match)
- pattern-either:
    - pattern: eval($X)
    - pattern: new Function($X)

# pattern-inside: match within a context
- pattern-inside: |
    app.$METHOD($PATH, (req, res) => {
      ...
    })

# pattern-not-inside: exclude context
- pattern-not-inside: |
    app.$METHOD($PATH, authenticate, ...)

# pattern-regex: regex in code
- pattern-regex: "password\s*=\s*['\"][^'\"]{3,}['\"]"

Metavariable Constraints

rules:
  - id: weak-hash-for-passwords
    patterns:
      - pattern: crypto.createHash($ALG).update($INPUT)
      - metavariable-regex:
          metavariable: $ALG
          regex: "('md5'|'sha1')"
      - metavariable-regex:
          metavariable: $INPUT
          regex: ".*password.*"
    message: "Weak hash algorithm used for password: $ALG"
    languages: [javascript, typescript]
    severity: ERROR

Taint Analysis (Pro)

rules:
  - id: sql-injection-taint
    mode: taint
    pattern-sources:
      - pattern: req.body.$PARAM
      - pattern: req.query.$PARAM
      - pattern: req.params.$PARAM
    pattern-sinks:
      - pattern: $DB.query($SQL)
    pattern-sanitizers:
      - pattern: $DB.escape($X)
      - pattern: sanitize($X)
    message: "User input flows to SQL query without sanitization"
    languages: [javascript]
    severity: ERROR

Project .semgrep.yml Example

rules:
  - id: no-hardcoded-secrets
    pattern-regex: |
      (?:api[_-]?key|secret|password|token)\s*[:=]\s*['"][A-Za-z0-9+/=_\-]{16,}['"]
    paths:
      exclude:
        - "*.test.*"
        - "*.spec.*"
        - "__tests__/*"
        - "*.example"
    message: "Potential hardcoded secret detected"
    languages: [generic]
    severity: ERROR

  - id: no-console-log-sensitive
    patterns:
      - pattern: console.log(..., $DATA, ...)
      - metavariable-regex:
          metavariable: $DATA
          regex: ".*(?:password|secret|token|key|credential).*"
    message: "Sensitive data in console.log"
    languages: [javascript, typescript]
    severity: WARNING

  - id: require-input-validation
    patterns:
      - pattern: |
          app.post($PATH, async (req, res) => {
            ...
            $DB.$METHOD(req.body)
            ...
          })
      - pattern-not: |
          app.post($PATH, async (req, res) => {
            ...
            $SCHEMA.parse(...)
            ...
          })
    message: "POST endpoint without input validation"
    languages: [javascript, typescript]
    severity: WARNING

CI/CD Integration

GitHub Actions

name: SAST Security Scan

on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main]

jobs:
  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep Scan
        uses: semgrep/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/secrets
            p/typescript
          generateSarif: "1"
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif
        if: always()

      - name: Dependency Audit
        run: npm audit --audit-level=high

      - name: Check for Secrets
        uses: trufflesecurity/trufflehog@main
        with:
          extra_args: --only-verified

Pre-commit Hook

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/semgrep/semgrep
    rev: 'v1.60.0'
    hooks:
      - id: semgrep
        args: ['--config', 'auto', '--error', '--severity', 'ERROR']

GitLab CI

sast:
  stage: test
  image: semgrep/semgrep
  script:
    - semgrep --config auto --config "p/secrets" --json --output gl-sast-report.json .
  artifacts:
    reports:
      sast: gl-sast-report.json
  rules:
    - if: $CI_MERGE_REQUEST_ID

CLI Commands Quick Reference

# Full scan with auto rules
semgrep --config auto .

# OWASP + Secrets
semgrep --config "p/owasp-top-ten" --config "p/secrets" .

# Only errors (for CI gates)
semgrep --config auto --error --severity ERROR .

# JSON output for processing
semgrep --config auto --json --output report.json .

# Scan specific files
semgrep --config auto src/api/ src/auth/

# Diff-aware (only changed code)
semgrep --config auto --baseline-commit origin/main

# Custom rules
semgrep --config .semgrep.yml .

# Exclude test files
semgrep --config auto --exclude="*test*" --exclude="*spec*" .

# With metrics disabled (privacy)
semgrep --config auto --metrics=off .

Severity Classification Guide

SeverityCriteriaActionSLA
CRITICALExploitable RCE, SQLi, auth bypass, data breachFix IMMEDIATELY, block deploy< 1 hour
HIGHXSS, SSRF, IDOR, missing auth, weak cryptoFix before production< 24 hours
MEDIUMMissing validation, verbose errors, weak headersFix in current sprint< 1 week
LOWDebug mode, outdated lib (no known CVE), info leakFix in backlog< 1 month

Quick Decision Tree

Is user input involved?
  YES -> Does it reach a dangerous sink (DB, exec, DOM)?
    YES -> Is it sanitized/validated?
      NO  -> CRITICAL (injection)
      YES -> Check sanitizer adequacy -> MEDIUM if weak
    NO  -> MEDIUM (missing validation)
  NO  -> Is it a configuration issue?
    YES -> Affects security posture?
      YES -> HIGH (misconfiguration)
      NO  -> LOW (best practice)
    NO  -> Is it a dependency issue?
      YES -> Known CVE?
        YES -> Match CVE severity
        NO  -> LOW (outdated)
      NO  -> Informational

Resources


Remember: SAST catches patterns, not intent. Always verify findings with manual review. A finding is only a vulnerability if it can be exploited in the application's specific context.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.