vibesec-backend / scanner /models.py
thefounder03's picture
perf: hybrid AST+LLM taint tracer and tier 0 pre-filter
84d5644
Raw
History Blame Contribute Delete
3.69 kB
from __future__ import annotations
from pydantic import BaseModel, Field
from typing import Optional, Literal, List, Dict, Any
from enum import Enum
class Severity(str, Enum):
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
INFO = "info"
class Confidence(str, Enum):
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
class FileRole(str, Enum):
PRODUCTION = "production"
TEST = "test"
FIXTURE = "fixture"
CI = "ci"
CONFIG = "config"
INFRA = "infra"
VENDOR = "vendor"
class ScanDomain(str, Enum):
SECRETS = "secrets"
DEPS = "deps"
IAC = "iac"
SAST = "sast"
CONFIG = "config"
FRONTEND = "frontend"
LLM_VULNS = "llm_vulns"
DAST = "dast"
CHAIN = "chain"
class TaintPath(BaseModel):
source_file: str
source_line: int
source_type: str # req.body | query_param | cookie | header | file
intermediate_calls: List[Dict[str, Any]] = Field(default_factory=list)
sink_file: str
sink_line: int
sink_type: str # sql | exec | eval | html_render | redirect | external_request
sanitizers_found: List[str] = Field(default_factory=list)
class Evidence(BaseModel):
tool_call: str
result_summary: str
file_path: Optional[str] = None
line_number: Optional[int] = None
class Finding(BaseModel):
id: str = Field(default_factory=lambda: __import__("uuid").uuid4().hex)
title: str
description: str
severity: Severity
confidence: Confidence = Confidence.MEDIUM
domain: ScanDomain
check_id: Optional[str] = None
check_category: Optional[str] = None
policy_reference: Optional[str] = None
file_path: Optional[str] = None
line_number: Optional[int] = None
file_role: Optional[FileRole] = None
explanation: str = ""
suggested_fix: str = ""
evidence: List[Evidence] = Field(default_factory=list)
taint_path: Optional[TaintPath] = None
is_false_positive: bool = False
false_positive_reason: Optional[str] = None
reachability_unconfirmed: bool = False
dismissed_at: Optional[str] = None
confirmed_runtime: bool = False # DAST confirmed
chained_from: List[str] = Field(default_factory=list) # IDs of contributing findings
category: Optional[str] = None
validity: Optional[Literal["active", "revoked", "unknown"]] = None
secret_type: Optional[str] = None
raw_secret_value: Optional[str] = None
secret_value_encrypted: Optional[str] = None
exploitable_by: Optional[str] = None # unauthenticated | authenticated | admin
taint_confirmed: bool = False
verdict: Optional[str] = None
validation_reason: Optional[str] = None
requires_llm_gate: bool = Field(default=False)
@property
def file(self) -> Optional[str]:
return self.file_path
@file.setter
def file(self, value: Optional[str]):
self.file_path = value
class ScanResult(BaseModel):
scan_id: str
status: Literal["completed", "failed", "partial"]
score: int
findings: List[Finding]
findings_count: int
chain_findings: List[Finding] = Field(default_factory=list)
scan_metadata: Dict[str, Any] = Field(default_factory=dict)
suppressed_findings: List[Finding] = Field(default_factory=list)
class ScanRequest(BaseModel):
repo_url: Optional[str] = None
local_files: Optional[List[Dict[str, str]]] = None # [{path, content}, ...]
runtime_url: Optional[str] = None
scan_id: Optional[str] = None
project_id: Optional[str] = None
user_id: Optional[str] = None
plan_tier: Literal["free", "pro", "team", "enterprise"] = "free"
enable_dast: bool = False
github_token: Optional[str] = None