Skill v1.0.0
currentLLM-judged scan90/100version: "1.0.0" name: security-scanning-security-sast description: "'Static Application Security Testing (SAST) for code vulnerability"
SAST Security Plugin
Static Application Security Testing (SAST) for comprehensive code vulnerability detection across multiple languages, frameworks, and security patterns.
Capabilities
- Multi-language SAST: Python, JavaScript/TypeScript, Java, Ruby, PHP, Go, Rust
- Tool integration: Bandit, Semgrep, ESLint Security, SonarQube, CodeQL, PMD, SpotBugs, Brakeman, gosec, cargo-clippy
- Vulnerability patterns: SQL injection, XSS, hardcoded secrets, path traversal, IDOR, CSRF, insecure deserialization
- Framework analysis: Django, Flask, React, Express, Spring Boot, Rails, Laravel
- Custom rule authoring: Semgrep pattern development for organization-specific security policies
Use this skill when
Use 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).
Specialized tools: Use security-secrets.md for advanced credential scanning, security-owasp.md for Top 10 mapping, security-api.md for REST/GraphQL endpoints.
Do not use this skill when
- You only need runtime testing or penetration testing
- You cannot access the source code or build outputs
- The environment forbids third-party scanning tools
Instructions
- Identify the languages, frameworks, and scope to scan.
- Select SAST tools and configure rules for the codebase.
- Run scans in CI or locally with reproducible settings.
- Triage findings, prioritize by severity, and propose fixes.
Safety
- Avoid uploading proprietary code to external services without approval.
- Require review before enabling auto-fix or blocking releases.
SAST Tool Selection
Python: Bandit
# Installation & scanpip install banditbandit -r . -f json -o bandit-report.jsonbandit -r . -ll -ii -f json # High/Critical only
Configuration: .bandit
exclude_dirs: ['/tests/', '/venv/', '/.tox/', '/build/']tests: [B201, B301, B302, B303, B304, B305, B307, B308, B312, B323, B324, B501, B502, B506, B602, B608]skips: [B101]
JavaScript/TypeScript: ESLint Security
npm install --save-dev eslint @eslint/plugin-security eslint-plugin-no-secretseslint . --ext .js,.jsx,.ts,.tsx --format json > eslint-security.json
Configuration: .eslintrc-security.json
{"plugins": ["@eslint/plugin-security", "eslint-plugin-no-secrets"],"extends": ["plugin:security/recommended"],"rules": {"security/detect-object-injection": "error","security/detect-non-literal-fs-filename": "error","security/detect-eval-with-expression": "error","security/detect-pseudo-random-prng": "error","no-secrets/no-secrets": "error"}}
Multi-Language: Semgrep
pip install semgrepsemgrep --config=auto --json --output=semgrep-report.jsonsemgrep --config=p/security-audit --jsonsemgrep --config=p/owasp-top-ten --jsonsemgrep ci --config=auto # CI mode
Custom Rules: .semgrep.yml
rules:- id: sql-injection-format-stringpattern: cursor.execute("... %s ..." % $VAR)message: SQL injection via string formattingseverity: ERRORlanguages: [python]metadata:cwe: "CWE-89"owasp: "A03:2021-Injection"- id: dangerous-innerHTMLpattern: $ELEM.innerHTML = $VARmessage: XSS via innerHTML assignmentseverity: ERRORlanguages: [javascript, typescript]metadata:cwe: "CWE-79"- id: hardcoded-aws-credentialspatterns:- pattern: $KEY = "AKIA..."- metavariable-regex:metavariable: $KEYregex: "(aws_access_key_id|AWS_ACCESS_KEY_ID)"message: Hardcoded AWS credentials detectedseverity: ERRORlanguages: [python, javascript, java]- id: path-traversal-openpatterns:- pattern: open($PATH, ...)- pattern-not: open(os.path.join(SAFE_DIR, ...), ...)- metavariable-pattern:metavariable: $PATHpatterns:- pattern: $REQ.get(...)message: Path traversal via user inputseverity: ERRORlanguages: [python]- id: command-injectionpatterns:- pattern-either:- pattern: os.system($CMD)- pattern: subprocess.call($CMD, shell=True)- metavariable-pattern:metavariable: $CMDpatterns:- pattern-either:- pattern: $X + $Y- pattern: f"...{$VAR}..."message: Command injection via shell=Trueseverity: ERRORlanguages: [python]
Other Language Tools
Java: mvn spotbugs:check Ruby: brakeman -o report.json -f json Go: gosec -fmt=json -out=gosec.json ./... Rust: cargo clippy -- -W clippy::unwrap_used
Vulnerability Patterns
SQL Injection
VULNERABLE: String formatting/concatenation with user input in SQL queries
SECURE:
# Parameterized queriescursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))User.objects.filter(id=user_id) # ORM
Cross-Site Scripting (XSS)
VULNERABLE: Direct HTML manipulation with unsanitized user input (innerHTML, outerHTML, document.write)
SECURE:
// Use textContent for plain textelement.textContent = userInput;// React auto-escapes<div>{userInput}</div>// Sanitize when HTML requiredimport DOMPurify from 'dompurify';element.innerHTML = DOMPurify.sanitize(userInput);
Hardcoded Secrets
VULNERABLE: Hardcoded API keys, passwords, tokens in source code
SECURE:
import osAPI_KEY = os.environ.get('API_KEY')PASSWORD = os.getenv('DB_PASSWORD')
Path Traversal
VULNERABLE: Opening files using unsanitized user input
SECURE:
import osALLOWED_DIR = '/var/www/uploads'file_name = request.args.get('file')file_path = os.path.join(ALLOWED_DIR, file_name)file_path = os.path.realpath(file_path)if not file_path.startswith(os.path.realpath(ALLOWED_DIR)):raise ValueError("Invalid file path")with open(file_path, 'r') as f:content = f.read()
Insecure Deserialization
VULNERABLE: pickle.loads(), yaml.load() with untrusted data
SECURE:
import jsondata = json.loads(user_input) # SECUREimport yamlconfig = yaml.safe_load(user_input) # SECURE
Command Injection
VULNERABLE: os.system() or subprocess with shell=True and user input
SECURE:
subprocess.run(['ping', '-c', '4', user_input]) # Array argsimport shlexsafe_input = shlex.quote(user_input) # Input validation
Insecure Random
VULNERABLE: random module for security-critical operations
SECURE:
import secretstoken = secrets.token_hex(16)session_id = secrets.token_urlsafe(32)
Framework Security
Django
VULNERABLE: @csrf_exempt, DEBUG=True, weak SECRET_KEY, missing security middleware
SECURE:
# settings.pyDEBUG = FalseSECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',]SECURE_SSL_REDIRECT = TrueSESSION_COOKIE_SECURE = TrueCSRF_COOKIE_SECURE = TrueX_FRAME_OPTIONS = 'DENY'
Flask
VULNERABLE: debug=True, weak secret_key, CORS wildcard
SECURE:
import osfrom flask_talisman import Talismanapp.secret_key = os.environ.get('FLASK_SECRET_KEY')Talisman(app, force_https=True)CORS(app, origins=['https://example.com'])
Express.js
VULNERABLE: Missing helmet, CORS wildcard, no rate limiting
SECURE:
const helmet = require('helmet');const rateLimit = require('express-rate-limit');app.use(helmet());app.use(cors({ origin: 'https://example.com' }));app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
Multi-Language Scanner Implementation
import jsonimport subprocessfrom pathlib import Pathfrom typing import Dict, List, Anyfrom dataclasses import dataclassfrom datetime import datetime@dataclassclass SASTFinding:tool: strseverity: strcategory: strtitle: strdescription: strfile_path: strline_number: intcwe: strowasp: strconfidence: strclass MultiLanguageSASTScanner:def __init__(self, project_path: str):self.project_path = Path(project_path)self.findings: List[SASTFinding] = []def detect_languages(self) -> List[str]:"""Auto-detect languages"""languages = []indicators = {'python': ['*.py', 'requirements.txt'],'javascript': ['*.js', 'package.json'],'typescript': ['*.ts', 'tsconfig.json'],'java': ['*.java', 'pom.xml'],'ruby': ['*.rb', 'Gemfile'],'go': ['*.go', 'go.mod'],'rust': ['*.rs', 'Cargo.toml'],}for lang, patterns in indicators.items():for pattern in patterns:if list(self.project_path.glob(f'**/{pattern}')):languages.append(lang)breakreturn languagesdef run_comprehensive_sast(self) -> Dict[str, Any]:"""Execute all applicable SAST tools"""languages = self.detect_languages()scan_results = {'timestamp': datetime.now().isoformat(),'languages': languages,'tools_executed': [],'findings': []}self.run_semgrep_scan()scan_results['tools_executed'].append('semgrep')if 'python' in languages:self.run_bandit_scan()scan_results['tools_executed'].append('bandit')if 'javascript' in languages or 'typescript' in languages:self.run_eslint_security_scan()scan_results['tools_executed'].append('eslint-security')scan_results['findings'] = [vars(f) for f in self.findings]scan_results['summary'] = self.generate_summary()return scan_resultsdef run_semgrep_scan(self):"""Run Semgrep"""for ruleset in ['auto', 'p/security-audit', 'p/owasp-top-ten']:try:result = subprocess.run(['semgrep', '--config', ruleset, '--json', '--quiet',str(self.project_path)], capture_output=True, text=True, timeout=300)if result.stdout:data = json.loads(result.stdout)for f in data.get('results', []):self.findings.append(SASTFinding(tool='semgrep',severity=f.get('extra', {}).get('severity', 'MEDIUM').upper(),category='sast',title=f.get('check_id', ''),description=f.get('extra', {}).get('message', ''),file_path=f.get('path', ''),line_number=f.get('start', {}).get('line', 0),cwe=f.get('extra', {}).get('metadata', {}).get('cwe', ''),owasp=f.get('extra', {}).get('metadata', {}).get('owasp', ''),confidence=f.get('extra', {}).get('metadata', {}).get('confidence', 'MEDIUM')))except Exception as e:print(f"Semgrep {ruleset} failed: {e}")def generate_summary(self) -> Dict[str, Any]:"""Generate statistics"""severity_counts = {'CRITICAL': 0, 'HIGH': 0, 'MEDIUM': 0, 'LOW': 0}for f in self.findings:severity_counts[f.severity] = severity_counts.get(f.severity, 0) + 1return {'total_findings': len(self.findings),'severity_breakdown': severity_counts,'risk_score': self.calculate_risk_score(severity_counts)}def calculate_risk_score(self, severity_counts: Dict[str, int]) -> int:"""Risk score 0-100"""weights = {'CRITICAL': 10, 'HIGH': 7, 'MEDIUM': 4, 'LOW': 1}total = sum(weights[s] * c for s, c in severity_counts.items())return min(100, int((total / 50) * 100))
CI/CD Integration
GitHub Actions
name: SAST Scanon:pull_request:branches: [main]jobs:sast:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3- uses: actions/setup-python@v4with:python-version: '3.11'- name: Install toolsrun: |pip install bandit semgrepnpm install -g eslint @eslint/plugin-security- name: Run scansrun: |bandit -r . -f json -o bandit.json || truesemgrep --config=auto --json --output=semgrep.json || true- name: Upload reportsuses: actions/upload-artifact@v3with:name: sast-reportspath: |bandit.jsonsemgrep.json
GitLab CI
sast:stage: testimage: python:3.11script:- pip install bandit semgrep- bandit -r . -f json -o bandit.json || true- semgrep --config=auto --json --output=semgrep.json || trueartifacts:reports:sast: bandit.json
Best Practices
- Run early and often - Pre-commit hooks and CI/CD
- Combine multiple tools - Different tools catch different vulnerabilities
- Tune false positives - Configure exclusions and thresholds
- Prioritize findings - Focus on CRITICAL/HIGH first
- Framework-aware scanning - Use specific rulesets
- Custom rules - Organization-specific patterns
- Developer training - Secure coding practices
- Incremental remediation - Fix gradually
- Baseline management - Track known issues
- Regular updates - Keep tools current
Related Tools
- security-secrets.md - Advanced credential detection
- security-owasp.md - OWASP Top 10 assessment
- security-api.md - API security testing
- security-scan.md - Comprehensive security scanning
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.