- 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 & scan
pip install bandit bandit -r . -f json -o bandit-report.json bandit -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-secrets eslint . --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 semgrep semgrep --config = auto --json --output = semgrep-report.json semgrep --config = p/security-audit --json semgrep --config = p/owasp-top-ten --json semgrep ci --config = auto
CI mode
- Custom Rules
- :
- .semgrep.yml
- rules
- :
- -
- id
- :
- sql
- -
- injection
- -
- format
- -
- string
- pattern
- :
- cursor.execute("
- ...
- %s
- ...
- " % $VAR)
- message
- :
- SQL injection via string formatting
- severity
- :
- ERROR
- languages
- :
- [
- python
- ]
- metadata
- :
- cwe
- :
- "CWE-89"
- owasp
- :
- "A03:2021-Injection"
- -
- id
- :
- dangerous
- -
- innerHTML
- pattern
- :
- $ELEM.innerHTML = $VAR
- message
- :
- XSS via innerHTML assignment
- severity
- :
- ERROR
- languages
- :
- [
- javascript
- ,
- typescript
- ]
- metadata
- :
- cwe
- :
- "CWE-79"
- -
- id
- :
- hardcoded
- -
- aws
- -
- credentials
- patterns
- :
- -
- pattern
- :
- $KEY = "AKIA
- ...
- "
- -
- metavariable-regex
- :
- metavariable
- :
- $KEY
- regex
- :
- "(aws_access_key_id|AWS_ACCESS_KEY_ID)"
- message
- :
- Hardcoded AWS credentials detected
- severity
- :
- ERROR
- languages
- :
- [
- python
- ,
- javascript
- ,
- java
- ]
- -
- id
- :
- path
- -
- traversal
- -
- open
- patterns
- :
- -
- pattern
- :
- open($PATH
- ,
- ...
- )
- -
- pattern-not
- :
- open(os.path.join(SAFE_DIR
- ,
- ...
- )
- ,
- ...
- )
- -
- metavariable-pattern
- :
- metavariable
- :
- $PATH
- patterns
- :
- -
- pattern
- :
- $REQ.get(
- ...
- )
- message
- :
- Path traversal via user input
- severity
- :
- ERROR
- languages
- :
- [
- python
- ]
- -
- id
- :
- command
- -
- injection
- patterns
- :
- -
- pattern-either
- :
- -
- pattern
- :
- os.system($CMD)
- -
- pattern
- :
- subprocess.call($CMD
- ,
- shell=True)
- -
- metavariable-pattern
- :
- metavariable
- :
- $CMD
- patterns
- :
- -
- pattern-either
- :
- -
- pattern
- :
- $X + $Y
- -
- pattern
- :
- f"
- ...
- {
- $VAR
- }
- ...
- "
- message
- :
- Command injection via shell=True
- severity
- :
- ERROR
- languages
- :
- [
- 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 queries
cursor . 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 text
- element
- .
- textContent
- =
- userInput
- ;
- // React auto-escapes
- <
- div
- >
- {
- userInput
- }
- <
- /
- div
- >
- // Sanitize when HTML required
- import
- DOMPurify
- from
- 'dompurify'
- ;
- element
- .
- innerHTML
- =
- DOMPurify
- .
- sanitize
- (
- userInput
- )
- ;
- Hardcoded Secrets
- VULNERABLE
-
- Hardcoded API keys, passwords, tokens in source code
- SECURE
- :
- import
- os
- API_KEY
- =
- os
- .
- environ
- .
- get
- (
- 'API_KEY'
- )
- PASSWORD
- =
- os
- .
- getenv
- (
- 'DB_PASSWORD'
- )
- Path Traversal
- VULNERABLE
-
- Opening files using unsanitized user input
- SECURE
- :
- import
- os
- ALLOWED_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 json data = json . loads ( user_input )
SECURE
import yaml config = 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 args
import shlex safe_input = shlex . quote ( user_input )
Input validation
- Insecure Random
- VULNERABLE
-
- random module for security-critical operations
- SECURE
- :
- import
- secrets
- token
- =
- 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.py
DEBUG
- False
- SECRET_KEY
- =
- os
- .
- environ
- .
- get
- (
- 'DJANGO_SECRET_KEY'
- )
- MIDDLEWARE
- =
- [
- 'django.middleware.security.SecurityMiddleware'
- ,
- 'django.middleware.csrf.CsrfViewMiddleware'
- ,
- 'django.middleware.clickjacking.XFrameOptionsMiddleware'
- ,
- ]
- SECURE_SSL_REDIRECT
- =
- True
- SESSION_COOKIE_SECURE
- =
- True
- CSRF_COOKIE_SECURE
- =
- True
- X_FRAME_OPTIONS
- =
- 'DENY'
- Flask
- VULNERABLE
-
- debug=True, weak secret_key, CORS wildcard
- SECURE
- :
- import
- os
- from
- flask_talisman
- import
- Talisman
- app
- .
- 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
json
import
subprocess
from
pathlib
import
Path
from
typing
import
Dict
,
List
,
Any
from
dataclasses
import
dataclass
from
datetime
import
datetime
@dataclass
class
SASTFinding
:
tool
:
str
severity
:
str
category
:
str
title
:
str
description
:
str
file_path
:
str
line_number
:
int
cwe
:
str
owasp
:
str
confidence
:
str
class
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 ) break return languages def 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_results def 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 ) + 1 return { '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 Scan on : pull_request : branches : [ main ] jobs : sast : runs-on : ubuntu - latest steps : - uses : actions/checkout@v3 - uses : actions/setup - python@v4 with : python-version : '3.11' - name : Install tools run : | pip install bandit semgrep npm install -g eslint @eslint/plugin-security - name : Run scans run : | bandit -r . -f json -o bandit.json || true semgrep --config=auto --json --output=semgrep.json || true - name : Upload reports uses : actions/upload - artifact@v3 with : name : sast - reports path : | bandit.json semgrep.json GitLab CI sast : stage : test image : python : 3.11 script : - pip install bandit semgrep - bandit - r . - f json - o bandit.json | | true - semgrep - - config=auto - - json - - output=semgrep.json | | true artifacts : 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