Testare de Securitate SAST si DAST: Ghid Complet de Implementare
Static Application Security Testing (SAST) si Dynamic Application Security Testing (DAST) formeaza fundamentul programelor de securitate a aplicatiilor. Acest ghid acopera implementarea practica a ambelor abordari, inclusiv configurarea uneltelor, dezvoltarea de reguli custom si integrarea CI/CD.
Intelegerea SAST vs DAST
Static Application Security Testing (SAST)
SAST analizeaza codul sursa, bytecode sau codul binar fara a executa aplicatia:
- Avantaje: Detectie timpurie, acoperire completa a codului, identifica cauza radacina
- Dezavantaje: Rata mare de false positive, specific limbajului, nu poate detecta probleme runtime
- Cel mai bun pentru: Gasirea defectelor de injection, secretelor hardcodate, pattern-urilor nesigure
Dynamic Application Security Testing (DAST)
DAST testeaza aplicatii in executie prin simularea atacurilor:
- Avantaje: Testeaza comportamentul real, agnostic fata de limbaj, putine false positive
- Dezavantaje: Acoperire limitata a codului, detectie tarzie, nu poate identifica cauza radacina
- Cel mai bun pentru: Gasirea vulnerabilitatilor runtime, misconfigurarilor, problemelor de autentificare
Implementare SAST cu Semgrep
Configurare de Baza
# .semgrep.yml
rules:
# Detectie SQL Injection
- id: sql-injection-string-concat
patterns:
- pattern-either:
- pattern: $QUERY = "..." + $USER_INPUT + "..."
- pattern: $QUERY = f"...{$USER_INPUT}..."
- pattern: |
$QUERY = "..." % $USER_INPUT
message: "Potential SQL injection prin concatenare de string-uri"
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-89"
owasp: "A03:2021 - Injection"
category: security
confidence: HIGH
# Secrete Hardcodate
- id: hardcoded-api-key
patterns:
- pattern-regex: |
(?i)(api[_-]?key|apikey|secret[_-]?key|auth[_-]?token)\s*[=:]\s*["'][a-zA-Z0-9]{16,}["']
message: "Cheie API sau secret hardcodat detectat"
languages: [python, javascript, typescript, java]
severity: ERROR
metadata:
cwe: "CWE-798"
category: security
# Deserializare Nesigura
- id: insecure-pickle-load
patterns:
- pattern: pickle.load($SOURCE)
- pattern: pickle.loads($SOURCE)
message: "Deserializare nesigura cu pickle - foloseste alternative mai sigure"
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-502"
# Command Injection
- id: command-injection-subprocess
patterns:
- pattern: subprocess.call($CMD, shell=True, ...)
- pattern: subprocess.Popen($CMD, shell=True, ...)
- pattern: os.system($CMD)
message: "Vulnerabilitate potentiala de command injection"
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-78"
# Path Traversal
- id: path-traversal-open
patterns:
- pattern-inside: |
def $FUNC(..., $PATH, ...):
...
- pattern-not-inside: |
$PATH = os.path.basename(...)
...
- pattern: open($PATH, ...)
message: "Potential path traversal - valideaza caile de fisier"
languages: [python]
severity: WARNING
metadata:
cwe: "CWE-22"Dezvoltarea de Reguli Custom
# custom_semgrep_rules.py
"""
Genereaza reguli Semgrep custom in mod programatic.
"""
import yaml
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, asdict
import json
@dataclass
class RuleMetadata:
"""Metadata pentru o regula Semgrep."""
cwe: str
owasp: Optional[str] = None
category: str = "security"
confidence: str = "HIGH"
references: List[str] = None
@dataclass
class SemgrepRule:
"""Reprezinta o regula Semgrep."""
id: str
message: str
severity: str
languages: List[str]
patterns: List[Dict[str, Any]]
metadata: RuleMetadata
fix: Optional[str] = None
fix_regex: Optional[Dict[str, str]] = None
class SemgrepRuleGenerator:
"""Genereaza reguli Semgrep custom pentru scanare de securitate."""
def __init__(self):
self.rules: List[SemgrepRule] = []
def add_injection_rule(
self,
rule_id: str,
sink_patterns: List[str],
languages: List[str],
injection_type: str = "SQL",
cwe: str = "CWE-89"
):
"""Adauga o regula de detectie a injectiilor."""
patterns = []
for sink in sink_patterns:
patterns.append({
"pattern-either": [
{"pattern": f"{sink}($USER_INPUT)"},
{"pattern": f'{sink}("..." + $USER_INPUT + "...")'},
{"pattern": f'{sink}(f"...{{$USER_INPUT}}...")'},
]
})
rule = SemgrepRule(
id=rule_id,
message=f"Vulnerabilitate potentiala de {injection_type} injection",
severity="ERROR",
languages=languages,
patterns=patterns,
metadata=RuleMetadata(
cwe=cwe,
owasp="A03:2021 - Injection",
confidence="HIGH"
)
)
self.rules.append(rule)
def add_sensitive_data_rule(
self,
rule_id: str,
data_patterns: List[str],
context: str = "logging"
):
"""Adauga o regula pentru detectia expunerii datelor sensibile."""
pattern_either = []
for data_pattern in data_patterns:
if context == "logging":
pattern_either.extend([
{"pattern": f"logging.info(..., {data_pattern}, ...)"},
{"pattern": f"logging.debug(..., {data_pattern}, ...)"},
{"pattern": f"print(..., {data_pattern}, ...)"},
{"pattern": f"logger.info(..., {data_pattern}, ...)"},
])
elif context == "response":
pattern_either.extend([
{"pattern": f"return {{{data_pattern}: ...}}"},
{"pattern": f"json.dumps({{..., {data_pattern}: ..., ...}})"},
])
rule = SemgrepRule(
id=rule_id,
message=f"Date sensibile pot fi expuse in {context}",
severity="WARNING",
languages=["python", "javascript", "typescript"],
patterns=[{"pattern-either": pattern_either}],
metadata=RuleMetadata(
cwe="CWE-532" if context == "logging" else "CWE-200",
category="security",
confidence="MEDIUM"
)
)
self.rules.append(rule)
def add_authentication_rule(
self,
rule_id: str,
weak_patterns: List[Dict[str, str]]
):
"""Adauga reguli de securitate a autentificarii."""
patterns = []
for pattern_config in weak_patterns:
patterns.append({
"pattern": pattern_config["pattern"],
"pattern-not": pattern_config.get("pattern-not")
})
rule = SemgrepRule(
id=rule_id,
message="Pattern slab de autentificare detectat",
severity="ERROR",
languages=["python", "javascript", "java"],
patterns=patterns,
metadata=RuleMetadata(
cwe="CWE-287",
owasp="A07:2021 - Identification and Authentication Failures"
)
)
self.rules.append(rule)
def add_crypto_rule(
self,
rule_id: str,
weak_algorithms: List[str],
languages: List[str]
):
"""Adauga reguli de detectie a criptografiei slabe."""
pattern_either = []
for algo in weak_algorithms:
pattern_either.extend([
{"pattern": f'hashlib.{algo}(...)'},
{"pattern": f'Cipher.getInstance("{algo}")'},
{"pattern": f'crypto.create{algo.title()}(...)'},
])
rule = SemgrepRule(
id=rule_id,
message=f"Algoritm criptografic slab detectat: {', '.join(weak_algorithms)}",
severity="WARNING",
languages=languages,
patterns=[{"pattern-either": pattern_either}],
metadata=RuleMetadata(
cwe="CWE-327",
owasp="A02:2021 - Cryptographic Failures"
)
)
self.rules.append(rule)
def export_rules(self, output_path: str):
"""Exporta regulile in fisier YAML."""
rules_dict = {
"rules": []
}
for rule in self.rules:
rule_dict = {
"id": rule.id,
"message": rule.message,
"severity": rule.severity,
"languages": rule.languages,
"patterns": rule.patterns,
"metadata": {
"cwe": rule.metadata.cwe,
"category": rule.metadata.category,
"confidence": rule.metadata.confidence
}
}
if rule.metadata.owasp:
rule_dict["metadata"]["owasp"] = rule.metadata.owasp
if rule.fix:
rule_dict["fix"] = rule.fix
rules_dict["rules"].append(rule_dict)
with open(output_path, 'w') as f:
yaml.dump(rules_dict, f, default_flow_style=False, sort_keys=False)
# Genereaza reguli custom
generator = SemgrepRuleGenerator()
# Reguli SQL Injection
generator.add_injection_rule(
"custom-sql-injection",
["cursor.execute", "db.execute", "connection.execute"],
["python"],
"SQL",
"CWE-89"
)
# Logare date sensibile
generator.add_sensitive_data_rule(
"sensitive-data-logging",
["password", "secret", "token", "api_key", "ssn", "credit_card"],
"logging"
)
# Criptografie slaba
generator.add_crypto_rule(
"weak-crypto-hash",
["md5", "sha1"],
["python", "java", "javascript"]
)
# Export
generator.export_rules("custom-security-rules.yml")Script de Integrare SAST
# sast_scanner.py
"""
Orchestrare scanare SAST cu unelte multiple.
"""
import subprocess
import json
import os
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from datetime import datetime
import xml.etree.ElementTree as ET
@dataclass
class Finding:
"""Reprezinta o descoperire de securitate."""
tool: str
rule_id: str
severity: str
message: str
file_path: str
line_number: int
code_snippet: Optional[str]
cwe: Optional[str]
fix_suggestion: Optional[str]
class SASTScanner:
"""
Orchestreaza scanere SAST multiple pentru scanare completa.
"""
def __init__(self, project_path: str):
self.project_path = project_path
self.findings: List[Finding] = []
def run_semgrep(
self,
config: str = "auto",
additional_rules: Optional[str] = None
) -> List[Finding]:
"""Ruleaza scanarea Semgrep."""
cmd = [
"semgrep", "scan",
"--config", config,
"--json",
"--no-git-ignore",
self.project_path
]
if additional_rules:
cmd.extend(["--config", additional_rules])
result = subprocess.run(cmd, capture_output=True, text=True)
findings = []
try:
data = json.loads(result.stdout)
for match in data.get("results", []):
findings.append(Finding(
tool="semgrep",
rule_id=match.get("check_id", "unknown"),
severity=match.get("extra", {}).get("severity", "INFO"),
message=match.get("extra", {}).get("message", ""),
file_path=match.get("path", ""),
line_number=match.get("start", {}).get("line", 0),
code_snippet=match.get("extra", {}).get("lines", ""),
cwe=match.get("extra", {}).get("metadata", {}).get("cwe"),
fix_suggestion=match.get("extra", {}).get("fix")
))
except json.JSONDecodeError:
pass
self.findings.extend(findings)
return findings
def run_bandit(self, confidence: str = "MEDIUM") -> List[Finding]:
"""Ruleaza Bandit pentru scanare de securitate Python."""
cmd = [
"bandit",
"-r", self.project_path,
"-f", "json",
"-ll", # Doar severitate medie si mai sus
f"-c", confidence
]
result = subprocess.run(cmd, capture_output=True, text=True)
findings = []
try:
data = json.loads(result.stdout)
for issue in data.get("results", []):
severity_map = {"LOW": "INFO", "MEDIUM": "WARNING", "HIGH": "ERROR"}
findings.append(Finding(
tool="bandit",
rule_id=issue.get("test_id", "unknown"),
severity=severity_map.get(issue.get("severity", "LOW"), "INFO"),
message=issue.get("issue_text", ""),
file_path=issue.get("filename", ""),
line_number=issue.get("line_number", 0),
code_snippet=issue.get("code", ""),
cwe=issue.get("cwe", {}).get("id") if issue.get("cwe") else None,
fix_suggestion=None
))
except json.JSONDecodeError:
pass
self.findings.extend(findings)
return findings
def run_eslint_security(self) -> List[Finding]:
"""Ruleaza ESLint cu plugin-uri de securitate pentru JavaScript/TypeScript."""
cmd = [
"npx", "eslint",
"--plugin", "security",
"--format", "json",
self.project_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
findings = []
try:
data = json.loads(result.stdout)
for file_result in data:
for message in file_result.get("messages", []):
if "security" in message.get("ruleId", ""):
severity_map = {1: "WARNING", 2: "ERROR"}
findings.append(Finding(
tool="eslint-security",
rule_id=message.get("ruleId", "unknown"),
severity=severity_map.get(message.get("severity", 1), "INFO"),
message=message.get("message", ""),
file_path=file_result.get("filePath", ""),
line_number=message.get("line", 0),
code_snippet=message.get("source", ""),
cwe=None,
fix_suggestion=message.get("fix")
))
except json.JSONDecodeError:
pass
self.findings.extend(findings)
return findings
def run_all_scanners(self) -> Dict[str, Any]:
"""Ruleaza toate scanerele SAST configurate."""
results = {
"scan_time": datetime.utcnow().isoformat(),
"project_path": self.project_path,
"scanners": {},
"summary": {}
}
# Ruleaza fiecare scaner
scanners = [
("semgrep", self.run_semgrep),
("bandit", self.run_bandit),
("eslint-security", self.run_eslint_security),
]
for name, scanner_fn in scanners:
try:
findings = scanner_fn()
results["scanners"][name] = {
"status": "success",
"findings_count": len(findings)
}
except Exception as e:
results["scanners"][name] = {
"status": "error",
"error": str(e)
}
# Genereaza sumarul
results["summary"] = self.generate_summary()
return results
def generate_summary(self) -> Dict[str, Any]:
"""Genereaza sumarul tuturor descoperirilor."""
summary = {
"total_findings": len(self.findings),
"by_severity": {},
"by_tool": {},
"by_cwe": {},
"critical_files": []
}
# Numara dupa severitate
for finding in self.findings:
severity = finding.severity
summary["by_severity"][severity] = summary["by_severity"].get(severity, 0) + 1
# Numara dupa unealta
for finding in self.findings:
tool = finding.tool
summary["by_tool"][tool] = summary["by_tool"].get(tool, 0) + 1
# Numara dupa CWE
for finding in self.findings:
if finding.cwe:
summary["by_cwe"][finding.cwe] = summary["by_cwe"].get(finding.cwe, 0) + 1
# Gaseste fisierele critice
file_counts = {}
for finding in self.findings:
if finding.severity in ["ERROR", "HIGH"]:
file_counts[finding.file_path] = file_counts.get(finding.file_path, 0) + 1
summary["critical_files"] = sorted(
file_counts.items(),
key=lambda x: x[1],
reverse=True
)[:10]
return summary
def export_sarif(self, output_path: str):
"""Exporta descoperirile in format SARIF pentru integrare GitHub."""
sarif = {
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [{
"tool": {
"driver": {
"name": "SAST Scanner",
"version": "1.0.0",
"rules": []
}
},
"results": []
}]
}
# Adauga reguli unice
rules_seen = set()
for finding in self.findings:
if finding.rule_id not in rules_seen:
rules_seen.add(finding.rule_id)
sarif["runs"][0]["tool"]["driver"]["rules"].append({
"id": finding.rule_id,
"shortDescription": {"text": finding.message[:100]},
"help": {"text": finding.message}
})
# Adauga rezultate
for i, finding in enumerate(self.findings):
sarif["runs"][0]["results"].append({
"ruleId": finding.rule_id,
"level": "error" if finding.severity in ["ERROR", "HIGH"] else "warning",
"message": {"text": finding.message},
"locations": [{
"physicalLocation": {
"artifactLocation": {"uri": finding.file_path},
"region": {"startLine": finding.line_number}
}
}]
})
with open(output_path, 'w') as f:
json.dump(sarif, f, indent=2)Implementare DAST cu OWASP ZAP
Framework de Automatizare ZAP
# zap-automation.yaml
env:
contexts:
- name: "Application Context"
urls:
- "https://app.example.com"
includePaths:
- "https://app.example.com/.*"
excludePaths:
- "https://app.example.com/logout.*"
- "https://app.example.com/static/.*"
authentication:
method: "form"
parameters:
loginUrl: "https://app.example.com/login"
loginRequestData: "username={%username%}&password={%password%}"
verification:
method: "response"
loggedInRegex: "\\QLogout\\E"
loggedOutRegex: "\\QLogin\\E"
users:
- name: "test-user"
credentials:
username: "${ZAP_AUTH_USER}"
password: "${ZAP_AUTH_PASS}"
parameters:
failOnError: true
failOnWarning: false
progressToStdout: true
jobs:
# Crawl aplicatia
- type: spider
parameters:
context: "Application Context"
user: "test-user"
maxDuration: 10
maxDepth: 5
maxChildren: 10
# AJAX Spider pentru aplicatii JavaScript-heavy
- type: spiderAjax
parameters:
context: "Application Context"
user: "test-user"
maxDuration: 10
maxCrawlDepth: 5
numberOfBrowsers: 2
# Scanare pasiva
- type: passiveScan-wait
parameters:
maxDuration: 5
# Scanare activa
- type: activeScan
parameters:
context: "Application Context"
user: "test-user"
policy: "API-Scan"
maxRuleDurationInMins: 5
maxScanDurationInMins: 30
# Genereaza rapoarte
- type: report
parameters:
template: "traditional-html"
reportDir: "/zap/reports"
reportFile: "zap-report.html"
risks:
- high
- medium
- low
- type: report
parameters:
template: "sarif-json"
reportDir: "/zap/reports"
reportFile: "zap-report.sarif"Scripturi Custom ZAP
# zap_custom_scanner.py
"""
Scanare custom ZAP cu API Python.
"""
from zapv2 import ZAPv2
import time
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
import json
@dataclass
class DASTFinding:
"""Descoperire de vulnerabilitate DAST."""
alert_id: str
name: str
risk: str
confidence: str
url: str
parameter: Optional[str]
evidence: Optional[str]
description: str
solution: str
cwe_id: Optional[str]
wasc_id: Optional[str]
class ZAPScanner:
"""
Scaner ZAP custom cu functionalitate imbunatatita.
"""
def __init__(
self,
target_url: str,
api_key: str = "",
proxy_host: str = "localhost",
proxy_port: int = 8080
):
self.target_url = target_url
self.zap = ZAPv2(
apikey=api_key,
proxies={
"http": f"http://{proxy_host}:{proxy_port}",
"https": f"http://{proxy_host}:{proxy_port}"
}
)
self.context_id = None
self.user_id = None
def setup_context(
self,
context_name: str,
include_paths: List[str],
exclude_paths: List[str]
):
"""Configureaza contextul de scanare."""
# Creeaza contextul
self.context_id = self.zap.context.new_context(context_name)
# Adauga patternuri de includere
for path in include_paths:
self.zap.context.include_in_context(context_name, path)
# Adauga patternuri de excludere
for path in exclude_paths:
self.zap.context.exclude_from_context(context_name, path)
return self.context_id
def setup_authentication(
self,
auth_type: str = "form",
login_url: str = None,
login_data: str = None,
logged_in_indicator: str = None
):
"""Configureaza autentificarea pentru scanare autentificata."""
if auth_type == "form":
self.zap.authentication.set_authentication_method(
self.context_id,
"formBasedAuthentication",
f"loginUrl={login_url}&loginRequestData={login_data}"
)
if logged_in_indicator:
self.zap.authentication.set_logged_in_indicator(
self.context_id,
logged_in_indicator
)
def add_user(
self,
username: str,
password: str,
user_name: str = "test-user"
):
"""Adauga un utilizator pentru scanare autentificata."""
self.user_id = self.zap.users.new_user(self.context_id, user_name)
self.zap.users.set_authentication_credentials(
self.context_id,
self.user_id,
f"username={username}&password={password}"
)
self.zap.users.set_user_enabled(self.context_id, self.user_id, True)
return self.user_id
def run_spider(
self,
max_depth: int = 5,
max_duration: int = 0
) -> int:
"""Ruleaza spider-ul pentru a descoperi URL-uri."""
if self.user_id:
scan_id = self.zap.spider.scan_as_user(
self.context_id,
self.user_id,
self.target_url,
maxchildren=10,
recurse=True,
subtreeonly=False
)
else:
scan_id = self.zap.spider.scan(
self.target_url,
maxchildren=10,
recurse=True,
subtreeonly=False
)
# Asteapta finalizarea spider-ului
while int(self.zap.spider.status(scan_id)) < 100:
time.sleep(2)
return len(self.zap.spider.results(scan_id))
def run_ajax_spider(self, max_duration: int = 10):
"""Ruleaza AJAX spider pentru aplicatii JavaScript."""
if self.user_id:
self.zap.ajaxSpider.scan_as_user(
self.context_id,
self.user_id,
self.target_url,
subtreeonly=False
)
else:
self.zap.ajaxSpider.scan(self.target_url)
# Asteapta AJAX spider
start_time = time.time()
while self.zap.ajaxSpider.status == "running":
if time.time() - start_time > max_duration * 60:
self.zap.ajaxSpider.stop()
break
time.sleep(5)
def run_passive_scan(self, max_wait: int = 300):
"""Asteapta finalizarea scanarii pasive."""
start_time = time.time()
while int(self.zap.pscan.records_to_scan) > 0:
if time.time() - start_time > max_wait:
break
time.sleep(2)
def run_active_scan(
self,
scan_policy: Optional[str] = None
) -> str:
"""Ruleaza scanarea activa de securitate."""
if self.user_id:
scan_id = self.zap.ascan.scan_as_user(
self.target_url,
self.context_id,
self.user_id,
recurse=True,
scanpolicyname=scan_policy
)
else:
scan_id = self.zap.ascan.scan(
self.target_url,
recurse=True,
scanpolicyname=scan_policy
)
# Monitorizeaza progresul
while int(self.zap.ascan.status(scan_id)) < 100:
progress = self.zap.ascan.status(scan_id)
print(f"Active scan progress: {progress}%")
time.sleep(10)
return scan_id
def get_alerts(
self,
min_risk: str = "Low"
) -> List[DASTFinding]:
"""Obtine toate alertele din scanare."""
risk_levels = ["Informational", "Low", "Medium", "High"]
min_risk_index = risk_levels.index(min_risk)
alerts = self.zap.core.alerts(baseurl=self.target_url)
findings = []
for alert in alerts:
alert_risk = alert.get("risk", "Informational")
if risk_levels.index(alert_risk) >= min_risk_index:
findings.append(DASTFinding(
alert_id=alert.get("id", ""),
name=alert.get("name", ""),
risk=alert_risk,
confidence=alert.get("confidence", ""),
url=alert.get("url", ""),
parameter=alert.get("param"),
evidence=alert.get("evidence"),
description=alert.get("description", ""),
solution=alert.get("solution", ""),
cwe_id=alert.get("cweid"),
wasc_id=alert.get("wascid")
))
return findings
def run_full_scan(self) -> Dict[str, Any]:
"""Ruleaza scanarea DAST completa."""
results = {
"target": self.target_url,
"start_time": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"phases": {}
}
# Spider
print("Se ruleaza spider-ul...")
urls_found = self.run_spider()
results["phases"]["spider"] = {"urls_discovered": urls_found}
# AJAX Spider
print("Se ruleaza AJAX spider-ul...")
self.run_ajax_spider()
results["phases"]["ajax_spider"] = {"status": "completed"}
# Scanare pasiva
print("Se ruleaza scanarea pasiva...")
self.run_passive_scan()
results["phases"]["passive_scan"] = {"status": "completed"}
# Scanare activa
print("Se ruleaza scanarea activa...")
self.run_active_scan()
results["phases"]["active_scan"] = {"status": "completed"}
# Obtine descoperirile
findings = self.get_alerts()
results["findings"] = [
{
"name": f.name,
"risk": f.risk,
"confidence": f.confidence,
"url": f.url,
"parameter": f.parameter,
"cwe": f.cwe_id
}
for f in findings
]
# Sumar
results["summary"] = {
"total_findings": len(findings),
"high_risk": len([f for f in findings if f.risk == "High"]),
"medium_risk": len([f for f in findings if f.risk == "Medium"]),
"low_risk": len([f for f in findings if f.risk == "Low"])
}
results["end_time"] = time.strftime("%Y-%m-%dT%H:%M:%SZ")
return resultsIntegrare CI/CD
Workflow GitHub Actions
# .github/workflows/security-testing.yml
name: Security Testing
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
sast:
name: Scanare SAST
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/cwe-top-25
generateSarif: true
- name: Upload Semgrep SARIF
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: semgrep.sarif
- name: Run Bandit (Python)
run: |
pip install bandit
bandit -r . -f sarif -o bandit.sarif || true
- name: Upload Bandit SARIF
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: bandit.sarif
if: always()
- name: Run npm audit
run: |
npm audit --json > npm-audit.json || true
- name: Process npm audit results
run: |
python scripts/process-npm-audit.py npm-audit.json
dast:
name: Scanare DAST
runs-on: ubuntu-latest
needs: [sast]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
services:
app:
image: ${{ github.repository }}:${{ github.sha }}
ports:
- 3000:3000
steps:
- name: Checkout
uses: actions/checkout@v4
- name: OWASP ZAP Full Scan
uses: zaproxy/action-full-scan@v0.8.0
with:
target: 'http://localhost:3000'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'
- name: Upload ZAP Report
uses: actions/upload-artifact@v4
with:
name: zap-report
path: report_html.html
- name: Upload ZAP SARIF
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: zap.sarif
security-gate:
name: Security Gate
runs-on: ubuntu-latest
needs: [sast, dast]
if: always()
steps:
- name: Check for critical findings
run: |
echo "Se verifica rezultatele scanarilor de securitate..."
# Adauga logica pentru a esua daca se gasesc vulnerabilitati criticeConcluzie
Implementarea atat a SAST cat si a DAST ofera acoperire completa de securitate pe tot parcursul ciclului de dezvoltare:
- SAST prinde vulnerabilitatile devreme in dezvoltare cu acoperire completa a codului
- DAST valideaza securitatea aplicatiilor in executie cu simulari realiste de atac
- Regulile custom extind capabilitatile de scanare pentru pattern-uri specifice organizatiei
- Integrarea CI/CD automatizeaza testarea de securitate in fiecare build
Combinand aceste abordari cu tuning adecvat si imbunatatire continua, organizatiile pot reduce semnificativ expunerea la vulnerabilitati.
Sistemul tau AI e conform cu EU AI Act? Evaluare gratuita de risc - afla in 2 minute →