mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-18 20:49:28 +00:00
feat(scan): add initial SAST pipeline
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import tomllib
|
||||
from dataclasses import asdict, dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
DEFAULT_VECTOR_STORE = "json-local"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ModelConfig:
|
||||
ranker: str | None = None
|
||||
hunter: str | None = None
|
||||
verifier: str | None = None
|
||||
patcher: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EmbeddingConfig:
|
||||
model: str | None = None
|
||||
store: str = DEFAULT_VECTOR_STORE
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SandboxConfig:
|
||||
network: bool = False
|
||||
workspace_readonly: bool = True
|
||||
memory_mb: int = 2048
|
||||
timeout_seconds: int = 300
|
||||
pids_limit: int = 512
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DynamicConfig:
|
||||
enabled: bool = False
|
||||
network_scope: tuple[str, ...] = ()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EvidenceConfig:
|
||||
minimum_report_verified: str = "static_corroboration"
|
||||
minimum_exploit: str = "crash_reproduced"
|
||||
minimum_patch: str = "root_cause_explained"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ResolvedConfig:
|
||||
models: ModelConfig = ModelConfig()
|
||||
embeddings: EmbeddingConfig = EmbeddingConfig()
|
||||
sandbox: SandboxConfig = SandboxConfig()
|
||||
dynamic: DynamicConfig = DynamicConfig()
|
||||
evidence: EvidenceConfig = EvidenceConfig()
|
||||
runs_dir: str = ".openultrasast/runs"
|
||||
|
||||
|
||||
def load_config(config_path: Path | None = None) -> ResolvedConfig:
|
||||
data: dict[str, object] = {}
|
||||
if config_path is not None and config_path.exists():
|
||||
with config_path.open("rb") as handle:
|
||||
data = tomllib.load(handle)
|
||||
|
||||
return ResolvedConfig(
|
||||
models=_load_models(data.get("models", {})),
|
||||
embeddings=_load_embeddings(data.get("embeddings", {})),
|
||||
sandbox=_load_sandbox(data.get("sandbox", {})),
|
||||
dynamic=_load_dynamic(data.get("dynamic", {})),
|
||||
evidence=_load_evidence(data.get("evidence", {})),
|
||||
runs_dir=os.environ.get("OPENULTRASAST_RUNS_DIR", ".openultrasast/runs"),
|
||||
)
|
||||
|
||||
|
||||
def write_resolved_config(config: ResolvedConfig, path: Path) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(asdict(config), indent=2, sort_keys=True) + "\n")
|
||||
|
||||
|
||||
def _section(value: object) -> dict[str, object]:
|
||||
return value if isinstance(value, dict) else {}
|
||||
|
||||
|
||||
def _string(value: object) -> str | None:
|
||||
return value if isinstance(value, str) and value else None
|
||||
|
||||
|
||||
def _load_models(value: object) -> ModelConfig:
|
||||
data = _section(value)
|
||||
return ModelConfig(
|
||||
ranker=_string(data.get("ranker")),
|
||||
hunter=_string(data.get("hunter")),
|
||||
verifier=_string(data.get("verifier")),
|
||||
patcher=_string(data.get("patcher")),
|
||||
)
|
||||
|
||||
|
||||
def _load_embeddings(value: object) -> EmbeddingConfig:
|
||||
data = _section(value)
|
||||
return EmbeddingConfig(model=_string(data.get("model")), store=_string(data.get("store")) or DEFAULT_VECTOR_STORE)
|
||||
|
||||
|
||||
def _load_sandbox(value: object) -> SandboxConfig:
|
||||
data = _section(value)
|
||||
return SandboxConfig(
|
||||
network=bool(data.get("network", False)),
|
||||
workspace_readonly=bool(data.get("workspace_readonly", True)),
|
||||
memory_mb=int(data.get("memory_mb", 2048)),
|
||||
timeout_seconds=int(data.get("timeout_seconds", 300)),
|
||||
pids_limit=int(data.get("pids_limit", 512)),
|
||||
)
|
||||
|
||||
|
||||
def _load_dynamic(value: object) -> DynamicConfig:
|
||||
data = _section(value)
|
||||
scope = data.get("network_scope", [])
|
||||
if not isinstance(scope, list):
|
||||
scope = []
|
||||
return DynamicConfig(enabled=bool(data.get("enabled", False)), network_scope=tuple(str(item) for item in scope))
|
||||
|
||||
|
||||
def _load_evidence(value: object) -> EvidenceConfig:
|
||||
data = _section(value)
|
||||
return EvidenceConfig(
|
||||
minimum_report_verified=str(data.get("minimum_report_verified", "static_corroboration")),
|
||||
minimum_exploit=str(data.get("minimum_exploit", "crash_reproduced")),
|
||||
minimum_patch=str(data.get("minimum_patch", "root_cause_explained")),
|
||||
)
|
||||
Reference in New Issue
Block a user