DevSecOps

SAST si DAST: implementare testare securitate

Nicu Constantin
--13 min lectura
#sast#dast#security-testing#devsecops#semgrep#owasp-zap#cicd

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 results

Integrare 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 critice

Concluzie

Implementarea atat a SAST cat si a DAST ofera acoperire completa de securitate pe tot parcursul ciclului de dezvoltare:

  1. SAST prinde vulnerabilitatile devreme in dezvoltare cu acoperire completa a codului
  2. DAST valideaza securitatea aplicatiilor in executie cu simulari realiste de atac
  3. Regulile custom extind capabilitatile de scanare pentru pattern-uri specifice organizatiei
  4. 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 →

Ai nevoie de ajutor cu conformitatea EU AI Act sau securitatea AI?

Programeaza o consultatie gratuita de 30 de minute. Fara obligatii.

Programeaza un Apel

Weekly AI Security & Automation Digest

Get the latest on AI Security, workflow automation, secure integrations, and custom platform development delivered weekly.

No spam. Unsubscribe anytime.