Static Application Security Testing (SAST) for code vulnerability
Add this skill
npx mdskills install sickn33/security-scanning-security-sastComprehensive SAST guide with multi-language tool configs, vulnerability patterns, and secure code examples
1---2name: security-scanning-security-sast3description: Static Application Security Testing (SAST) for code vulnerability4 analysis across multiple languages and frameworks5metadata:6 globs: "**/*.py, **/*.js, **/*.ts, **/*.java, **/*.rb, **/*.go, **/*.rs, **/*.php"7 keywords: sast, static analysis, code security, vulnerability scanning, bandit,8 semgrep, eslint, sonarqube, codeql, security patterns, code review, ast9 analysis10---11# SAST Security Plugin1213Static Application Security Testing (SAST) for comprehensive code vulnerability detection across multiple languages, frameworks, and security patterns.1415## Capabilities1617- **Multi-language SAST**: Python, JavaScript/TypeScript, Java, Ruby, PHP, Go, Rust18- **Tool integration**: Bandit, Semgrep, ESLint Security, SonarQube, CodeQL, PMD, SpotBugs, Brakeman, gosec, cargo-clippy19- **Vulnerability patterns**: SQL injection, XSS, hardcoded secrets, path traversal, IDOR, CSRF, insecure deserialization20- **Framework analysis**: Django, Flask, React, Express, Spring Boot, Rails, Laravel21- **Custom rule authoring**: Semgrep pattern development for organization-specific security policies2223## Use this skill when2425Use for code review security analysis, injection vulnerabilities, hardcoded secrets, framework-specific patterns, custom security policy enforcement, pre-deployment validation, legacy code assessment, and compliance (OWASP, PCI-DSS, SOC2).2627**Specialized tools**: Use `security-secrets.md` for advanced credential scanning, `security-owasp.md` for Top 10 mapping, `security-api.md` for REST/GraphQL endpoints.2829## Do not use this skill when3031- You only need runtime testing or penetration testing32- You cannot access the source code or build outputs33- The environment forbids third-party scanning tools3435## Instructions36371. Identify the languages, frameworks, and scope to scan.382. Select SAST tools and configure rules for the codebase.393. Run scans in CI or locally with reproducible settings.404. Triage findings, prioritize by severity, and propose fixes.4142## Safety4344- Avoid uploading proprietary code to external services without approval.45- Require review before enabling auto-fix or blocking releases.4647## SAST Tool Selection4849### Python: Bandit5051```bash52# Installation & scan53pip install bandit54bandit -r . -f json -o bandit-report.json55bandit -r . -ll -ii -f json # High/Critical only56```5758**Configuration**: `.bandit`59```yaml60exclude_dirs: ['/tests/', '/venv/', '/.tox/', '/build/']61tests: [B201, B301, B302, B303, B304, B305, B307, B308, B312, B323, B324, B501, B502, B506, B602, B608]62skips: [B101]63```6465### JavaScript/TypeScript: ESLint Security6667```bash68npm install --save-dev eslint @eslint/plugin-security eslint-plugin-no-secrets69eslint . --ext .js,.jsx,.ts,.tsx --format json > eslint-security.json70```7172**Configuration**: `.eslintrc-security.json`73```json74{75 "plugins": ["@eslint/plugin-security", "eslint-plugin-no-secrets"],76 "extends": ["plugin:security/recommended"],77 "rules": {78 "security/detect-object-injection": "error",79 "security/detect-non-literal-fs-filename": "error",80 "security/detect-eval-with-expression": "error",81 "security/detect-pseudo-random-prng": "error",82 "no-secrets/no-secrets": "error"83 }84}85```8687### Multi-Language: Semgrep8889```bash90pip install semgrep91semgrep --config=auto --json --output=semgrep-report.json92semgrep --config=p/security-audit --json93semgrep --config=p/owasp-top-ten --json94semgrep ci --config=auto # CI mode95```9697**Custom Rules**: `.semgrep.yml`98```yaml99rules:100 - id: sql-injection-format-string101 pattern: cursor.execute("... %s ..." % $VAR)102 message: SQL injection via string formatting103 severity: ERROR104 languages: [python]105 metadata:106 cwe: "CWE-89"107 owasp: "A03:2021-Injection"108109 - id: dangerous-innerHTML110 pattern: $ELEM.innerHTML = $VAR111 message: XSS via innerHTML assignment112 severity: ERROR113 languages: [javascript, typescript]114 metadata:115 cwe: "CWE-79"116117 - id: hardcoded-aws-credentials118 patterns:119 - pattern: $KEY = "AKIA..."120 - metavariable-regex:121 metavariable: $KEY122 regex: "(aws_access_key_id|AWS_ACCESS_KEY_ID)"123 message: Hardcoded AWS credentials detected124 severity: ERROR125 languages: [python, javascript, java]126127 - id: path-traversal-open128 patterns:129 - pattern: open($PATH, ...)130 - pattern-not: open(os.path.join(SAFE_DIR, ...), ...)131 - metavariable-pattern:132 metavariable: $PATH133 patterns:134 - pattern: $REQ.get(...)135 message: Path traversal via user input136 severity: ERROR137 languages: [python]138139 - id: command-injection140 patterns:141 - pattern-either:142 - pattern: os.system($CMD)143 - pattern: subprocess.call($CMD, shell=True)144 - metavariable-pattern:145 metavariable: $CMD146 patterns:147 - pattern-either:148 - pattern: $X + $Y149 - pattern: f"...{$VAR}..."150 message: Command injection via shell=True151 severity: ERROR152 languages: [python]153```154155### Other Language Tools156157**Java**: `mvn spotbugs:check`158**Ruby**: `brakeman -o report.json -f json`159**Go**: `gosec -fmt=json -out=gosec.json ./...`160**Rust**: `cargo clippy -- -W clippy::unwrap_used`161162## Vulnerability Patterns163164### SQL Injection165166**VULNERABLE**: String formatting/concatenation with user input in SQL queries167168**SECURE**:169```python170# Parameterized queries171cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))172User.objects.filter(id=user_id) # ORM173```174175### Cross-Site Scripting (XSS)176177**VULNERABLE**: Direct HTML manipulation with unsanitized user input (innerHTML, outerHTML, document.write)178179**SECURE**:180```javascript181// Use textContent for plain text182element.textContent = userInput;183184// React auto-escapes185<div>{userInput}</div>186187// Sanitize when HTML required188import DOMPurify from 'dompurify';189element.innerHTML = DOMPurify.sanitize(userInput);190```191192### Hardcoded Secrets193194**VULNERABLE**: Hardcoded API keys, passwords, tokens in source code195196**SECURE**:197```python198import os199API_KEY = os.environ.get('API_KEY')200PASSWORD = os.getenv('DB_PASSWORD')201```202203### Path Traversal204205**VULNERABLE**: Opening files using unsanitized user input206207**SECURE**:208```python209import os210ALLOWED_DIR = '/var/www/uploads'211file_name = request.args.get('file')212file_path = os.path.join(ALLOWED_DIR, file_name)213file_path = os.path.realpath(file_path)214if not file_path.startswith(os.path.realpath(ALLOWED_DIR)):215 raise ValueError("Invalid file path")216with open(file_path, 'r') as f:217 content = f.read()218```219220### Insecure Deserialization221222**VULNERABLE**: pickle.loads(), yaml.load() with untrusted data223224**SECURE**:225```python226import json227data = json.loads(user_input) # SECURE228import yaml229config = yaml.safe_load(user_input) # SECURE230```231232### Command Injection233234**VULNERABLE**: os.system() or subprocess with shell=True and user input235236**SECURE**:237```python238subprocess.run(['ping', '-c', '4', user_input]) # Array args239import shlex240safe_input = shlex.quote(user_input) # Input validation241```242243### Insecure Random244245**VULNERABLE**: random module for security-critical operations246247**SECURE**:248```python249import secrets250token = secrets.token_hex(16)251session_id = secrets.token_urlsafe(32)252```253254## Framework Security255256### Django257258**VULNERABLE**: @csrf_exempt, DEBUG=True, weak SECRET_KEY, missing security middleware259260**SECURE**:261```python262# settings.py263DEBUG = False264SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')265266MIDDLEWARE = [267 'django.middleware.security.SecurityMiddleware',268 'django.middleware.csrf.CsrfViewMiddleware',269 'django.middleware.clickjacking.XFrameOptionsMiddleware',270]271272SECURE_SSL_REDIRECT = True273SESSION_COOKIE_SECURE = True274CSRF_COOKIE_SECURE = True275X_FRAME_OPTIONS = 'DENY'276```277278### Flask279280**VULNERABLE**: debug=True, weak secret_key, CORS wildcard281282**SECURE**:283```python284import os285from flask_talisman import Talisman286287app.secret_key = os.environ.get('FLASK_SECRET_KEY')288Talisman(app, force_https=True)289CORS(app, origins=['https://example.com'])290```291292### Express.js293294**VULNERABLE**: Missing helmet, CORS wildcard, no rate limiting295296**SECURE**:297```javascript298const helmet = require('helmet');299const rateLimit = require('express-rate-limit');300301app.use(helmet());302app.use(cors({ origin: 'https://example.com' }));303app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));304```305306## Multi-Language Scanner Implementation307308```python309import json310import subprocess311from pathlib import Path312from typing import Dict, List, Any313from dataclasses import dataclass314from datetime import datetime315316@dataclass317class SASTFinding:318 tool: str319 severity: str320 category: str321 title: str322 description: str323 file_path: str324 line_number: int325 cwe: str326 owasp: str327 confidence: str328329class MultiLanguageSASTScanner:330 def __init__(self, project_path: str):331 self.project_path = Path(project_path)332 self.findings: List[SASTFinding] = []333334 def detect_languages(self) -> List[str]:335 """Auto-detect languages"""336 languages = []337 indicators = {338 'python': ['*.py', 'requirements.txt'],339 'javascript': ['*.js', 'package.json'],340 'typescript': ['*.ts', 'tsconfig.json'],341 'java': ['*.java', 'pom.xml'],342 'ruby': ['*.rb', 'Gemfile'],343 'go': ['*.go', 'go.mod'],344 'rust': ['*.rs', 'Cargo.toml'],345 }346 for lang, patterns in indicators.items():347 for pattern in patterns:348 if list(self.project_path.glob(f'**/{pattern}')):349 languages.append(lang)350 break351 return languages352353 def run_comprehensive_sast(self) -> Dict[str, Any]:354 """Execute all applicable SAST tools"""355 languages = self.detect_languages()356357 scan_results = {358 'timestamp': datetime.now().isoformat(),359 'languages': languages,360 'tools_executed': [],361 'findings': []362 }363364 self.run_semgrep_scan()365 scan_results['tools_executed'].append('semgrep')366367 if 'python' in languages:368 self.run_bandit_scan()369 scan_results['tools_executed'].append('bandit')370 if 'javascript' in languages or 'typescript' in languages:371 self.run_eslint_security_scan()372 scan_results['tools_executed'].append('eslint-security')373374 scan_results['findings'] = [vars(f) for f in self.findings]375 scan_results['summary'] = self.generate_summary()376 return scan_results377378 def run_semgrep_scan(self):379 """Run Semgrep"""380 for ruleset in ['auto', 'p/security-audit', 'p/owasp-top-ten']:381 try:382 result = subprocess.run([383 'semgrep', '--config', ruleset, '--json', '--quiet',384 str(self.project_path)385 ], capture_output=True, text=True, timeout=300)386387 if result.stdout:388 data = json.loads(result.stdout)389 for f in data.get('results', []):390 self.findings.append(SASTFinding(391 tool='semgrep',392 severity=f.get('extra', {}).get('severity', 'MEDIUM').upper(),393 category='sast',394 title=f.get('check_id', ''),395 description=f.get('extra', {}).get('message', ''),396 file_path=f.get('path', ''),397 line_number=f.get('start', {}).get('line', 0),398 cwe=f.get('extra', {}).get('metadata', {}).get('cwe', ''),399 owasp=f.get('extra', {}).get('metadata', {}).get('owasp', ''),400 confidence=f.get('extra', {}).get('metadata', {}).get('confidence', 'MEDIUM')401 ))402 except Exception as e:403 print(f"Semgrep {ruleset} failed: {e}")404405 def generate_summary(self) -> Dict[str, Any]:406 """Generate statistics"""407 severity_counts = {'CRITICAL': 0, 'HIGH': 0, 'MEDIUM': 0, 'LOW': 0}408 for f in self.findings:409 severity_counts[f.severity] = severity_counts.get(f.severity, 0) + 1410411 return {412 'total_findings': len(self.findings),413 'severity_breakdown': severity_counts,414 'risk_score': self.calculate_risk_score(severity_counts)415 }416417 def calculate_risk_score(self, severity_counts: Dict[str, int]) -> int:418 """Risk score 0-100"""419 weights = {'CRITICAL': 10, 'HIGH': 7, 'MEDIUM': 4, 'LOW': 1}420 total = sum(weights[s] * c for s, c in severity_counts.items())421 return min(100, int((total / 50) * 100))422```423424## CI/CD Integration425426### GitHub Actions427428```yaml429name: SAST Scan430on:431 pull_request:432 branches: [main]433434jobs:435 sast:436 runs-on: ubuntu-latest437 steps:438 - uses: actions/checkout@v3439 - uses: actions/setup-python@v4440 with:441 python-version: '3.11'442443 - name: Install tools444 run: |445 pip install bandit semgrep446 npm install -g eslint @eslint/plugin-security447448 - name: Run scans449 run: |450 bandit -r . -f json -o bandit.json || true451 semgrep --config=auto --json --output=semgrep.json || true452453 - name: Upload reports454 uses: actions/upload-artifact@v3455 with:456 name: sast-reports457 path: |458 bandit.json459 semgrep.json460```461462### GitLab CI463464```yaml465sast:466 stage: test467 image: python:3.11468 script:469 - pip install bandit semgrep470 - bandit -r . -f json -o bandit.json || true471 - semgrep --config=auto --json --output=semgrep.json || true472 artifacts:473 reports:474 sast: bandit.json475```476477## Best Practices4784791. **Run early and often** - Pre-commit hooks and CI/CD4802. **Combine multiple tools** - Different tools catch different vulnerabilities4813. **Tune false positives** - Configure exclusions and thresholds4824. **Prioritize findings** - Focus on CRITICAL/HIGH first4835. **Framework-aware scanning** - Use specific rulesets4846. **Custom rules** - Organization-specific patterns4857. **Developer training** - Secure coding practices4868. **Incremental remediation** - Fix gradually4879. **Baseline management** - Track known issues48810. **Regular updates** - Keep tools current489490## Related Tools491492- **security-secrets.md** - Advanced credential detection493- **security-owasp.md** - OWASP Top 10 assessment494- **security-api.md** - API security testing495- **security-scan.md** - Comprehensive security scanning496
Full transparency — inspect the skill content before installing.