trace-sec 0.0.0-stage → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +210 -2
- package/bin/trace.js +112 -0
- package/package.json +44 -4
- package/pyproject.toml +39 -0
- package/src/trace_engine/__init__.py +8 -0
- package/src/trace_engine/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/__pycache__/cli.cpython-311.pyc +0 -0
- package/src/trace_engine/__pycache__/doctor.cpython-311.pyc +0 -0
- package/src/trace_engine/__pycache__/interactive.cpython-311.pyc +0 -0
- package/src/trace_engine/__pycache__/verify.cpython-311.pyc +0 -0
- package/src/trace_engine/ai/__init__.py +7 -0
- package/src/trace_engine/ai/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/ai/__pycache__/base.cpython-311.pyc +0 -0
- package/src/trace_engine/ai/__pycache__/ollama.cpython-311.pyc +0 -0
- package/src/trace_engine/ai/__pycache__/planner.cpython-311.pyc +0 -0
- package/src/trace_engine/ai/base.py +23 -0
- package/src/trace_engine/ai/ollama.py +50 -0
- package/src/trace_engine/ai/planner.py +40 -0
- package/src/trace_engine/apm/__init__.py +18 -0
- package/src/trace_engine/apm/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/apm/__pycache__/builder.cpython-311.pyc +0 -0
- package/src/trace_engine/apm/__pycache__/edges.cpython-311.pyc +0 -0
- package/src/trace_engine/apm/__pycache__/model.cpython-311.pyc +0 -0
- package/src/trace_engine/apm/__pycache__/nodes.cpython-311.pyc +0 -0
- package/src/trace_engine/apm/__pycache__/serialization.cpython-311.pyc +0 -0
- package/src/trace_engine/apm/builder.py +208 -0
- package/src/trace_engine/apm/edges.py +25 -0
- package/src/trace_engine/apm/model.py +107 -0
- package/src/trace_engine/apm/nodes.py +27 -0
- package/src/trace_engine/apm/serialization.py +105 -0
- package/src/trace_engine/benchmark/__init__.py +5 -0
- package/src/trace_engine/benchmark/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/benchmark/__pycache__/owasp.cpython-311.pyc +0 -0
- package/src/trace_engine/benchmark/owasp.py +183 -0
- package/src/trace_engine/cli.py +1184 -0
- package/src/trace_engine/config/__init__.py +35 -0
- package/src/trace_engine/config/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/config/__pycache__/defaults.cpython-311.pyc +0 -0
- package/src/trace_engine/config/__pycache__/loader.cpython-311.pyc +0 -0
- package/src/trace_engine/config/__pycache__/settings.cpython-311.pyc +0 -0
- package/src/trace_engine/config/defaults.py +48 -0
- package/src/trace_engine/config/loader.py +64 -0
- package/src/trace_engine/config/settings.py +72 -0
- package/src/trace_engine/doctor.py +250 -0
- package/src/trace_engine/findings/__init__.py +15 -0
- package/src/trace_engine/findings/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/findings/__pycache__/correlate.cpython-311.pyc +0 -0
- package/src/trace_engine/findings/__pycache__/model.cpython-311.pyc +0 -0
- package/src/trace_engine/findings/__pycache__/recommendations.cpython-311.pyc +0 -0
- package/src/trace_engine/findings/__pycache__/store.cpython-311.pyc +0 -0
- package/src/trace_engine/findings/correlate.py +103 -0
- package/src/trace_engine/findings/model.py +40 -0
- package/src/trace_engine/findings/recommendations.py +35 -0
- package/src/trace_engine/findings/store.py +39 -0
- package/src/trace_engine/framework/__init__.py +58 -0
- package/src/trace_engine/framework/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/base.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/csharp.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/dart.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/django.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/express.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/fastapi.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/flask.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/go.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/nextjs.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/php.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/react_router.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/ruby.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/rust.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/__pycache__/springboot.cpython-311.pyc +0 -0
- package/src/trace_engine/framework/base.py +49 -0
- package/src/trace_engine/framework/csharp.py +111 -0
- package/src/trace_engine/framework/dart.py +152 -0
- package/src/trace_engine/framework/django.py +188 -0
- package/src/trace_engine/framework/express.py +82 -0
- package/src/trace_engine/framework/fastapi.py +135 -0
- package/src/trace_engine/framework/flask.py +108 -0
- package/src/trace_engine/framework/go.py +94 -0
- package/src/trace_engine/framework/nextjs.py +200 -0
- package/src/trace_engine/framework/php.py +98 -0
- package/src/trace_engine/framework/react_router.py +331 -0
- package/src/trace_engine/framework/ruby.py +69 -0
- package/src/trace_engine/framework/rust.py +101 -0
- package/src/trace_engine/framework/springboot.py +145 -0
- package/src/trace_engine/harness/__init__.py +19 -0
- package/src/trace_engine/harness/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/harness/__pycache__/benchmark.cpython-311.pyc +0 -0
- package/src/trace_engine/harness/__pycache__/context.cpython-311.pyc +0 -0
- package/src/trace_engine/harness/__pycache__/engine.cpython-311.pyc +0 -0
- package/src/trace_engine/harness/__pycache__/patcher.cpython-311.pyc +0 -0
- package/src/trace_engine/harness/__pycache__/remediators.cpython-311.pyc +0 -0
- package/src/trace_engine/harness/benchmark.py +55 -0
- package/src/trace_engine/harness/context.py +49 -0
- package/src/trace_engine/harness/engine.py +187 -0
- package/src/trace_engine/harness/patcher.py +143 -0
- package/src/trace_engine/harness/remediators.py +307 -0
- package/src/trace_engine/ingest/__init__.py +15 -0
- package/src/trace_engine/ingest/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/ingest/__pycache__/files.cpython-311.pyc +0 -0
- package/src/trace_engine/ingest/__pycache__/hashing.cpython-311.pyc +0 -0
- package/src/trace_engine/ingest/__pycache__/ignore.cpython-311.pyc +0 -0
- package/src/trace_engine/ingest/__pycache__/repository.cpython-311.pyc +0 -0
- package/src/trace_engine/ingest/files.py +60 -0
- package/src/trace_engine/ingest/hashing.py +21 -0
- package/src/trace_engine/ingest/ignore.py +108 -0
- package/src/trace_engine/ingest/repository.py +50 -0
- package/src/trace_engine/intelligence/__init__.py +15 -0
- package/src/trace_engine/intelligence/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/__pycache__/evaluation.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/__pycache__/orchestrator.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/__pycache__/tracebench.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/evaluation.py +828 -0
- package/src/trace_engine/intelligence/laya/__init__.py +19 -0
- package/src/trace_engine/intelligence/laya/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/laya/__pycache__/prompts.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/laya/__pycache__/router.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/laya/__pycache__/schemas.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/laya/__pycache__/telemetry.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/laya/__pycache__/thresholds.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/laya/prompts.py +67 -0
- package/src/trace_engine/intelligence/laya/router.py +352 -0
- package/src/trace_engine/intelligence/laya/schemas.py +56 -0
- package/src/trace_engine/intelligence/laya/telemetry.py +48 -0
- package/src/trace_engine/intelligence/laya/thresholds.py +12 -0
- package/src/trace_engine/intelligence/orchestrator.py +130 -0
- package/src/trace_engine/intelligence/securebert/__init__.py +6 -0
- package/src/trace_engine/intelligence/securebert/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/securebert/__pycache__/cache.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/securebert/__pycache__/classifier.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/securebert/cache.py +37 -0
- package/src/trace_engine/intelligence/securebert/classifier.py +240 -0
- package/src/trace_engine/intelligence/tracebench.py +61 -0
- package/src/trace_engine/intelligence/training/__init__.py +21 -0
- package/src/trace_engine/intelligence/training/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/dataset.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/dataset_importers.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/laya_trainer.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/lora_system2.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/morefixes_pipeline.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/slicer.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/train_all.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/__pycache__/trainer.cpython-311.pyc +0 -0
- package/src/trace_engine/intelligence/training/dataset.py +320 -0
- package/src/trace_engine/intelligence/training/dataset_importers.py +349 -0
- package/src/trace_engine/intelligence/training/laya_trainer.py +678 -0
- package/src/trace_engine/intelligence/training/lora_system2.py +162 -0
- package/src/trace_engine/intelligence/training/morefixes_pipeline.py +463 -0
- package/src/trace_engine/intelligence/training/slicer.py +129 -0
- package/src/trace_engine/intelligence/training/train_all.py +1009 -0
- package/src/trace_engine/intelligence/training/trainer.py +321 -0
- package/src/trace_engine/interactive.py +623 -0
- package/src/trace_engine/mcp/__init__.py +6 -0
- package/src/trace_engine/mcp/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/mcp/__pycache__/config.cpython-311.pyc +0 -0
- package/src/trace_engine/mcp/__pycache__/server.cpython-311.pyc +0 -0
- package/src/trace_engine/mcp/config.py +42 -0
- package/src/trace_engine/mcp/server.py +398 -0
- package/src/trace_engine/output/__init__.py +28 -0
- package/src/trace_engine/output/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/output/__pycache__/html.cpython-311.pyc +0 -0
- package/src/trace_engine/output/__pycache__/markdown.cpython-311.pyc +0 -0
- package/src/trace_engine/output/__pycache__/sarif.cpython-311.pyc +0 -0
- package/src/trace_engine/output/__pycache__/terminal.cpython-311.pyc +0 -0
- package/src/trace_engine/output/html.py +142 -0
- package/src/trace_engine/output/markdown.py +65 -0
- package/src/trace_engine/output/sarif.py +180 -0
- package/src/trace_engine/output/terminal.py +537 -0
- package/src/trace_engine/parsing/__init__.py +26 -0
- package/src/trace_engine/parsing/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/parsing/__pycache__/calls.cpython-311.pyc +0 -0
- package/src/trace_engine/parsing/__pycache__/language.cpython-311.pyc +0 -0
- package/src/trace_engine/parsing/__pycache__/locations.cpython-311.pyc +0 -0
- package/src/trace_engine/parsing/__pycache__/parser.cpython-311.pyc +0 -0
- package/src/trace_engine/parsing/__pycache__/symbols.cpython-311.pyc +0 -0
- package/src/trace_engine/parsing/calls.py +14 -0
- package/src/trace_engine/parsing/language.py +24 -0
- package/src/trace_engine/parsing/locations.py +17 -0
- package/src/trace_engine/parsing/parser.py +465 -0
- package/src/trace_engine/parsing/symbols.py +45 -0
- package/src/trace_engine/plugin/__init__.py +157 -0
- package/src/trace_engine/plugin/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/plugin/__pycache__/evaluator.cpython-311.pyc +0 -0
- package/src/trace_engine/plugin/__pycache__/swebench_adapter.cpython-311.pyc +0 -0
- package/src/trace_engine/plugin/__pycache__/task.cpython-311.pyc +0 -0
- package/src/trace_engine/plugin/bundle/hooks.json +24 -0
- package/src/trace_engine/plugin/bundle/mcp_config.json +11 -0
- package/src/trace_engine/plugin/bundle/plugin.json +20 -0
- package/src/trace_engine/plugin/bundle/rules/security_remediation.md +40 -0
- package/src/trace_engine/plugin/bundle/skills/trace-security-harness/SKILL.md +118 -0
- package/src/trace_engine/plugin/evaluator.py +105 -0
- package/src/trace_engine/plugin/swebench_adapter.py +96 -0
- package/src/trace_engine/plugin/task.py +48 -0
- package/src/trace_engine/policy/__init__.py +5 -0
- package/src/trace_engine/policy/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/policy/__pycache__/scope.cpython-311.pyc +0 -0
- package/src/trace_engine/policy/scope.py +80 -0
- package/src/trace_engine/runtime/__init__.py +7 -0
- package/src/trace_engine/runtime/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/runtime/__pycache__/client.cpython-311.pyc +0 -0
- package/src/trace_engine/runtime/__pycache__/observations.cpython-311.pyc +0 -0
- package/src/trace_engine/runtime/__pycache__/target.cpython-311.pyc +0 -0
- package/src/trace_engine/runtime/client.py +81 -0
- package/src/trace_engine/runtime/observations.py +18 -0
- package/src/trace_engine/runtime/target.py +23 -0
- package/src/trace_engine/security/__init__.py +16 -0
- package/src/trace_engine/security/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/security/__pycache__/fusion.cpython-311.pyc +0 -0
- package/src/trace_engine/security/__pycache__/hypotheses.cpython-311.pyc +0 -0
- package/src/trace_engine/security/__pycache__/signals.cpython-311.pyc +0 -0
- package/src/trace_engine/security/__pycache__/timing.cpython-311.pyc +0 -0
- package/src/trace_engine/security/fusion.py +126 -0
- package/src/trace_engine/security/hypotheses.py +251 -0
- package/src/trace_engine/security/signals.py +28 -0
- package/src/trace_engine/security/timing.py +132 -0
- package/src/trace_engine/testpacks/__init__.py +18 -0
- package/src/trace_engine/testpacks/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/authentication.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/base.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/bfla.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/bola.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/cors.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/deserialization.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/injection.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/mass_assignment.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/path_traversal.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/registry.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/ssrf.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/__pycache__/ssti.cpython-311.pyc +0 -0
- package/src/trace_engine/testpacks/authentication.py +60 -0
- package/src/trace_engine/testpacks/base.py +63 -0
- package/src/trace_engine/testpacks/bfla.py +70 -0
- package/src/trace_engine/testpacks/bola.py +94 -0
- package/src/trace_engine/testpacks/cors.py +85 -0
- package/src/trace_engine/testpacks/deserialization.py +86 -0
- package/src/trace_engine/testpacks/injection.py +179 -0
- package/src/trace_engine/testpacks/mass_assignment.py +70 -0
- package/src/trace_engine/testpacks/path_traversal.py +117 -0
- package/src/trace_engine/testpacks/registry.py +44 -0
- package/src/trace_engine/testpacks/ssrf.py +85 -0
- package/src/trace_engine/testpacks/ssti.py +96 -0
- package/src/trace_engine/tools/__init__.py +6 -0
- package/src/trace_engine/tools/__pycache__/__init__.cpython-311.pyc +0 -0
- package/src/trace_engine/tools/__pycache__/adapters.cpython-311.pyc +0 -0
- package/src/trace_engine/tools/__pycache__/base.cpython-311.pyc +0 -0
- package/src/trace_engine/tools/__pycache__/registry.cpython-311.pyc +0 -0
- package/src/trace_engine/tools/adapters.py +111 -0
- package/src/trace_engine/tools/base.py +68 -0
- package/src/trace_engine/tools/registry.py +36 -0
- package/src/trace_engine/verify.py +156 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""TRACE configuration module."""
|
|
2
|
+
|
|
3
|
+
from trace_engine.config.settings import (
|
|
4
|
+
TraceConfig,
|
|
5
|
+
ProjectSettings,
|
|
6
|
+
AISettings,
|
|
7
|
+
TargetSettings,
|
|
8
|
+
RuntimeSettings,
|
|
9
|
+
AnalysisSettings,
|
|
10
|
+
TestsSettings,
|
|
11
|
+
ToolsSettings,
|
|
12
|
+
)
|
|
13
|
+
from trace_engine.config.loader import (
|
|
14
|
+
get_trace_dir,
|
|
15
|
+
get_config_path,
|
|
16
|
+
init_trace_dir,
|
|
17
|
+
load_config,
|
|
18
|
+
save_config,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"TraceConfig",
|
|
23
|
+
"ProjectSettings",
|
|
24
|
+
"AISettings",
|
|
25
|
+
"TargetSettings",
|
|
26
|
+
"RuntimeSettings",
|
|
27
|
+
"AnalysisSettings",
|
|
28
|
+
"TestsSettings",
|
|
29
|
+
"ToolsSettings",
|
|
30
|
+
"get_trace_dir",
|
|
31
|
+
"get_config_path",
|
|
32
|
+
"init_trace_dir",
|
|
33
|
+
"load_config",
|
|
34
|
+
"save_config",
|
|
35
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Default configuration values for TRACE."""
|
|
2
|
+
|
|
3
|
+
DEFAULT_CONFIG_TOML = """# TRACE Configuration File
|
|
4
|
+
# Threat Reconnaissance & Attack-path Correlation Engine
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "default-project"
|
|
8
|
+
version = "1.0.0"
|
|
9
|
+
mode = "local"
|
|
10
|
+
|
|
11
|
+
[ai]
|
|
12
|
+
provider = "ollama"
|
|
13
|
+
model = "qwen2.5-coder:latest"
|
|
14
|
+
temperature = 0.1
|
|
15
|
+
endpoint = "http://127.0.0.1:11434"
|
|
16
|
+
|
|
17
|
+
[target]
|
|
18
|
+
url = "http://127.0.0.1:8000"
|
|
19
|
+
scope_mode = "local"
|
|
20
|
+
allowed_hosts = ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
|
|
21
|
+
allowed_ports = [80, 443, 3000, 5000, 8000, 8080, 18080]
|
|
22
|
+
|
|
23
|
+
[runtime]
|
|
24
|
+
max_requests = 250
|
|
25
|
+
concurrency = 2
|
|
26
|
+
rate_limit_per_second = 5
|
|
27
|
+
timeout_seconds = 8
|
|
28
|
+
max_redirects = 3
|
|
29
|
+
destructive_tests = false
|
|
30
|
+
|
|
31
|
+
[analysis]
|
|
32
|
+
max_graph_hops = 3
|
|
33
|
+
max_source_context_lines = 120
|
|
34
|
+
enable_tree_sitter = true
|
|
35
|
+
|
|
36
|
+
[tests]
|
|
37
|
+
enabled = [
|
|
38
|
+
"bola",
|
|
39
|
+
"bfla",
|
|
40
|
+
"authentication",
|
|
41
|
+
"ssrf",
|
|
42
|
+
"injection",
|
|
43
|
+
"mass_assignment"
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tools]
|
|
47
|
+
enabled = ["schemathesis", "nuclei", "zap", "trufflehog"]
|
|
48
|
+
"""
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Configuration loader for TRACE."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
import tomllib
|
|
7
|
+
import tomli_w
|
|
8
|
+
|
|
9
|
+
from trace_engine.config.settings import TraceConfig
|
|
10
|
+
from trace_engine.config.defaults import DEFAULT_CONFIG_TOML
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_trace_dir(repo_path: Path) -> Path:
|
|
14
|
+
"""Get the .trace directory path within the given repository."""
|
|
15
|
+
return repo_path.resolve() / ".trace"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_config_path(repo_path: Path) -> Path:
|
|
19
|
+
"""Get the path to config.toml in the .trace directory."""
|
|
20
|
+
return get_trace_dir(repo_path) / "config.toml"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def init_trace_dir(repo_path: Path, project_name: Optional[str] = None) -> Path:
|
|
24
|
+
"""Initialize .trace directory structure in repository."""
|
|
25
|
+
trace_dir = get_trace_dir(repo_path)
|
|
26
|
+
trace_dir.mkdir(parents=True, exist_ok=True)
|
|
27
|
+
|
|
28
|
+
(trace_dir / "cache").mkdir(exist_ok=True)
|
|
29
|
+
(trace_dir / "runs").mkdir(exist_ok=True)
|
|
30
|
+
(trace_dir / "reports").mkdir(exist_ok=True)
|
|
31
|
+
|
|
32
|
+
config_file = trace_dir / "config.toml"
|
|
33
|
+
if not config_file.exists():
|
|
34
|
+
content = DEFAULT_CONFIG_TOML
|
|
35
|
+
if project_name:
|
|
36
|
+
content = content.replace('name = "default-project"', f'name = "{project_name}"')
|
|
37
|
+
config_file.write_text(content, encoding="utf-8")
|
|
38
|
+
|
|
39
|
+
return trace_dir
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def load_config(repo_path: Path) -> TraceConfig:
|
|
43
|
+
"""Load configuration from .trace/config.toml or return default configuration."""
|
|
44
|
+
config_file = get_config_path(repo_path)
|
|
45
|
+
if not config_file.exists():
|
|
46
|
+
return TraceConfig()
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
with open(config_file, "rb") as f:
|
|
50
|
+
data = tomllib.load(f)
|
|
51
|
+
return TraceConfig.model_validate(data)
|
|
52
|
+
except Exception:
|
|
53
|
+
return TraceConfig()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def save_config(repo_path: Path, config: TraceConfig) -> None:
|
|
57
|
+
"""Save configuration to .trace/config.toml."""
|
|
58
|
+
trace_dir = get_trace_dir(repo_path)
|
|
59
|
+
trace_dir.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
config_file = trace_dir / "config.toml"
|
|
61
|
+
|
|
62
|
+
data = config.model_dump()
|
|
63
|
+
with open(config_file, "wb") as f:
|
|
64
|
+
tomli_w.dump(data, f)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Configuration settings for TRACE using Pydantic."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ProjectSettings(BaseModel):
|
|
8
|
+
name: str = "default-project"
|
|
9
|
+
version: str = "1.0.0"
|
|
10
|
+
mode: str = "local"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AISettings(BaseModel):
|
|
14
|
+
provider: str = "ollama"
|
|
15
|
+
model: str = "qwen2.5-coder:latest"
|
|
16
|
+
temperature: float = 0.1
|
|
17
|
+
endpoint: str = "http://127.0.0.1:11434"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TargetSettings(BaseModel):
|
|
21
|
+
url: str = "http://127.0.0.1:8000"
|
|
22
|
+
scope_mode: str = "local"
|
|
23
|
+
allowed_hosts: List[str] = Field(
|
|
24
|
+
default_factory=lambda: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
|
|
25
|
+
)
|
|
26
|
+
allowed_ports: List[int] = Field(
|
|
27
|
+
default_factory=lambda: [80, 443, 3000, 5000, 8000, 8080, 18080]
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RuntimeSettings(BaseModel):
|
|
32
|
+
max_requests: int = 250
|
|
33
|
+
concurrency: int = 2
|
|
34
|
+
rate_limit_per_second: int = 5
|
|
35
|
+
timeout_seconds: float = 8.0
|
|
36
|
+
max_redirects: int = 3
|
|
37
|
+
destructive_tests: bool = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AnalysisSettings(BaseModel):
|
|
41
|
+
max_graph_hops: int = 3
|
|
42
|
+
max_source_context_lines: int = 120
|
|
43
|
+
enable_tree_sitter: bool = True
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TestsSettings(BaseModel):
|
|
47
|
+
enabled: List[str] = Field(
|
|
48
|
+
default_factory=lambda: [
|
|
49
|
+
"bola",
|
|
50
|
+
"bfla",
|
|
51
|
+
"authentication",
|
|
52
|
+
"ssrf",
|
|
53
|
+
"injection",
|
|
54
|
+
"mass_assignment",
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ToolsSettings(BaseModel):
|
|
60
|
+
enabled: List[str] = Field(
|
|
61
|
+
default_factory=lambda: ["schemathesis", "nuclei", "zap", "trufflehog"]
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TraceConfig(BaseModel):
|
|
66
|
+
project: ProjectSettings = Field(default_factory=ProjectSettings)
|
|
67
|
+
ai: AISettings = Field(default_factory=AISettings)
|
|
68
|
+
target: TargetSettings = Field(default_factory=TargetSettings)
|
|
69
|
+
runtime: RuntimeSettings = Field(default_factory=RuntimeSettings)
|
|
70
|
+
analysis: AnalysisSettings = Field(default_factory=AnalysisSettings)
|
|
71
|
+
tests: TestsSettings = Field(default_factory=TestsSettings)
|
|
72
|
+
tools: ToolsSettings = Field(default_factory=ToolsSettings)
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""Environment diagnostics and system readiness check (trace doctor)."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import sqlite3
|
|
5
|
+
import shutil
|
|
6
|
+
from typing import List, Dict, Any
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from trace_engine.tools.registry import default_tool_registry
|
|
10
|
+
from trace_engine.ai.ollama import OllamaProvider
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CheckItem(BaseModel):
|
|
14
|
+
name: str
|
|
15
|
+
status: bool # True for pass, False for fail/missing
|
|
16
|
+
category: str
|
|
17
|
+
details: str
|
|
18
|
+
critical: bool = False
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DoctorReport(BaseModel):
|
|
22
|
+
items: List[CheckItem]
|
|
23
|
+
all_critical_passed: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_doctor() -> DoctorReport:
|
|
27
|
+
"""Runs system diagnostics across runtime, AI, tools, and dependencies."""
|
|
28
|
+
items: List[CheckItem] = []
|
|
29
|
+
|
|
30
|
+
# 1. Python version
|
|
31
|
+
py_ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
32
|
+
py_ok = sys.version_info >= (3, 11)
|
|
33
|
+
items.append(
|
|
34
|
+
CheckItem(
|
|
35
|
+
name=f"Python {py_ver}",
|
|
36
|
+
status=py_ok,
|
|
37
|
+
category="Core Runtime",
|
|
38
|
+
details="Requires Python >= 3.11",
|
|
39
|
+
critical=True,
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# 2. SQLite
|
|
44
|
+
try:
|
|
45
|
+
conn = sqlite3.connect(":memory:")
|
|
46
|
+
conn.close()
|
|
47
|
+
items.append(
|
|
48
|
+
CheckItem(
|
|
49
|
+
name="SQLite 3",
|
|
50
|
+
status=True,
|
|
51
|
+
category="Core Runtime",
|
|
52
|
+
details=f"SQLite version {sqlite3.sqlite_version}",
|
|
53
|
+
critical=True,
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
items.append(
|
|
58
|
+
CheckItem(
|
|
59
|
+
name="SQLite 3",
|
|
60
|
+
status=False,
|
|
61
|
+
category="Core Runtime",
|
|
62
|
+
details=str(e),
|
|
63
|
+
critical=True,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# 3. Tree-sitter
|
|
68
|
+
try:
|
|
69
|
+
import tree_sitter_language_pack
|
|
70
|
+
items.append(
|
|
71
|
+
CheckItem(
|
|
72
|
+
name="Tree-sitter AST",
|
|
73
|
+
status=True,
|
|
74
|
+
category="Code Analysis",
|
|
75
|
+
details="Multi-language AST parsing enabled",
|
|
76
|
+
critical=True,
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
items.append(
|
|
81
|
+
CheckItem(
|
|
82
|
+
name="Tree-sitter AST",
|
|
83
|
+
status=False,
|
|
84
|
+
category="Code Analysis",
|
|
85
|
+
details="Fallback AST mode active",
|
|
86
|
+
critical=False,
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# 4. NetworkX
|
|
91
|
+
try:
|
|
92
|
+
import networkx as nx
|
|
93
|
+
items.append(
|
|
94
|
+
CheckItem(
|
|
95
|
+
name="NetworkX APM",
|
|
96
|
+
status=True,
|
|
97
|
+
category="Attack-Path Model",
|
|
98
|
+
details=f"Graph engine v{nx.__version__}",
|
|
99
|
+
critical=True,
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
items.append(
|
|
104
|
+
CheckItem(
|
|
105
|
+
name="NetworkX APM",
|
|
106
|
+
status=False,
|
|
107
|
+
category="Attack-Path Model",
|
|
108
|
+
details=str(e),
|
|
109
|
+
critical=True,
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# 5. HTTPX
|
|
114
|
+
try:
|
|
115
|
+
import httpx
|
|
116
|
+
items.append(
|
|
117
|
+
CheckItem(
|
|
118
|
+
name="HTTPX Client",
|
|
119
|
+
status=True,
|
|
120
|
+
category="Runtime Testing",
|
|
121
|
+
details=f"HTTP engine v{httpx.__version__}",
|
|
122
|
+
critical=True,
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
items.append(
|
|
127
|
+
CheckItem(
|
|
128
|
+
name="HTTPX Client",
|
|
129
|
+
status=False,
|
|
130
|
+
category="Runtime Testing",
|
|
131
|
+
details=str(e),
|
|
132
|
+
critical=True,
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# 6. Local AI (Ollama)
|
|
137
|
+
ollama = OllamaProvider()
|
|
138
|
+
ollama_ok = ollama.is_available()
|
|
139
|
+
items.append(
|
|
140
|
+
CheckItem(
|
|
141
|
+
name="Ollama LLM",
|
|
142
|
+
status=ollama_ok,
|
|
143
|
+
category="AI Inference",
|
|
144
|
+
details="Connected at http://127.0.0.1:11434" if ollama_ok else "Not reachable (using deterministic planner)",
|
|
145
|
+
critical=False,
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# 7. PyTorch & Transformers
|
|
150
|
+
try:
|
|
151
|
+
import torch
|
|
152
|
+
import transformers
|
|
153
|
+
items.append(
|
|
154
|
+
CheckItem(
|
|
155
|
+
name="PyTorch",
|
|
156
|
+
status=True,
|
|
157
|
+
category="AI Inference",
|
|
158
|
+
details=f"PyTorch v{torch.__version__}, Transformers v{transformers.__version__}",
|
|
159
|
+
critical=False,
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
items.append(
|
|
164
|
+
CheckItem(
|
|
165
|
+
name="PyTorch",
|
|
166
|
+
status=False,
|
|
167
|
+
category="AI Inference",
|
|
168
|
+
details=str(e),
|
|
169
|
+
critical=False,
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# 8. Laya System 1 Engine
|
|
174
|
+
try:
|
|
175
|
+
import warnings
|
|
176
|
+
with warnings.catch_warnings():
|
|
177
|
+
warnings.simplefilter("ignore")
|
|
178
|
+
from trace_engine.intelligence.laya.router import LayaDecisionEngine
|
|
179
|
+
laya = LayaDecisionEngine()
|
|
180
|
+
laya_avail = laya.is_available()
|
|
181
|
+
items.append(
|
|
182
|
+
CheckItem(
|
|
183
|
+
name="Laya System 1",
|
|
184
|
+
status=laya_avail,
|
|
185
|
+
category="AI Inference",
|
|
186
|
+
details="Online (convaiinnovations/laya on CPU)" if laya_avail else "Standby (calibrated fallback active)",
|
|
187
|
+
critical=False,
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
except Exception as e:
|
|
191
|
+
items.append(
|
|
192
|
+
CheckItem(
|
|
193
|
+
name="Laya System 1",
|
|
194
|
+
status=False,
|
|
195
|
+
category="AI Inference",
|
|
196
|
+
details=str(e),
|
|
197
|
+
critical=False,
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# 9. SecureBERT 2.0 Classifier
|
|
202
|
+
try:
|
|
203
|
+
from trace_engine.intelligence.securebert.classifier import SecureBERTClassifier
|
|
204
|
+
bert = SecureBERTClassifier()
|
|
205
|
+
items.append(
|
|
206
|
+
CheckItem(
|
|
207
|
+
name="SecureBERT 2.0",
|
|
208
|
+
status=True,
|
|
209
|
+
category="AI Inference",
|
|
210
|
+
details=f"Model: {bert.model_name}",
|
|
211
|
+
critical=False,
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
except Exception as e:
|
|
215
|
+
items.append(
|
|
216
|
+
CheckItem(
|
|
217
|
+
name="SecureBERT 2.0 Semantic Classifier",
|
|
218
|
+
status=False,
|
|
219
|
+
category="AI Inference",
|
|
220
|
+
details=str(e),
|
|
221
|
+
critical=False,
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# 7. External Tools
|
|
226
|
+
for tool_info in default_tool_registry.get_all_info():
|
|
227
|
+
items.append(
|
|
228
|
+
CheckItem(
|
|
229
|
+
name=tool_info.name,
|
|
230
|
+
status=tool_info.installed,
|
|
231
|
+
category="Security Tool Adapters",
|
|
232
|
+
details=tool_info.version or f"Executable '{tool_info.command}' not found in PATH",
|
|
233
|
+
critical=False,
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# 8. Docker
|
|
238
|
+
docker_installed = shutil.which("docker") is not None
|
|
239
|
+
items.append(
|
|
240
|
+
CheckItem(
|
|
241
|
+
name="Docker Desktop / CLI",
|
|
242
|
+
status=docker_installed,
|
|
243
|
+
category="Lab Isolation",
|
|
244
|
+
details="Available for isolated container labs" if docker_installed else "Not found (local process labs enabled)",
|
|
245
|
+
critical=False,
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
all_crit = all(item.status for item in items if item.critical)
|
|
250
|
+
return DoctorReport(items=items, all_critical_passed=all_crit)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Findings and evidence correlation module."""
|
|
2
|
+
|
|
3
|
+
from trace_engine.findings.model import Finding, Severity, FindingConfidence
|
|
4
|
+
from trace_engine.findings.correlate import EvidenceCorrelator
|
|
5
|
+
from trace_engine.findings.store import FindingStore
|
|
6
|
+
from trace_engine.findings.recommendations import get_remediation_for_category
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Finding",
|
|
10
|
+
"Severity",
|
|
11
|
+
"FindingConfidence",
|
|
12
|
+
"EvidenceCorrelator",
|
|
13
|
+
"FindingStore",
|
|
14
|
+
"get_remediation_for_category",
|
|
15
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Evidence correlator connecting static attack paths to runtime test results."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
from trace_engine.security.hypotheses import SecurityHypothesis
|
|
5
|
+
from trace_engine.testpacks.base import TestExecutionResult
|
|
6
|
+
from trace_engine.findings.model import Finding, Severity, FindingConfidence
|
|
7
|
+
from trace_engine.findings.recommendations import get_remediation_for_category
|
|
8
|
+
from trace_engine.apm.model import AttackPathModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
SEVERITY_BY_CATEGORY = {
|
|
12
|
+
"BOLA": Severity.HIGH,
|
|
13
|
+
"BFLA": Severity.HIGH,
|
|
14
|
+
"AUTHENTICATION": Severity.CRITICAL,
|
|
15
|
+
"SSRF": Severity.HIGH,
|
|
16
|
+
"INJECTION": Severity.CRITICAL,
|
|
17
|
+
"MASS_ASSIGNMENT": Severity.MEDIUM,
|
|
18
|
+
"PATH_TRAVERSAL": Severity.HIGH,
|
|
19
|
+
"SSTI": Severity.CRITICAL,
|
|
20
|
+
"CORS": Severity.MEDIUM,
|
|
21
|
+
"DESERIALIZATION": Severity.CRITICAL,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EvidenceCorrelator:
|
|
26
|
+
"""Correlates static code evidence with runtime test executions using Neuro-Symbolic Bayesian Calibration."""
|
|
27
|
+
|
|
28
|
+
def correlate(
|
|
29
|
+
self,
|
|
30
|
+
hypothesis: SecurityHypothesis,
|
|
31
|
+
test_result: Optional[TestExecutionResult],
|
|
32
|
+
apm: AttackPathModel,
|
|
33
|
+
) -> Optional[Finding]:
|
|
34
|
+
from trace_engine.security.fusion import NeuroSymbolicConfidenceEngine, ConfidenceEvidence
|
|
35
|
+
|
|
36
|
+
category_str = hypothesis.category.value
|
|
37
|
+
|
|
38
|
+
# Calculate combined confidence using Neuro-Symbolic Bayesian Fusion
|
|
39
|
+
evidence = ConfidenceEvidence(
|
|
40
|
+
symbolic_ast_score=hypothesis.confidence_prior,
|
|
41
|
+
dynamic_test_score=test_result.confidence if test_result else None,
|
|
42
|
+
is_dynamically_confirmed=bool(test_result and test_result.confirmed),
|
|
43
|
+
)
|
|
44
|
+
fused = NeuroSymbolicConfidenceEngine.fuse_evidence(evidence)
|
|
45
|
+
confidence = fused.confidence_level
|
|
46
|
+
|
|
47
|
+
# If not confirmed and posterior probability is below 0.40, do not generate noisy finding
|
|
48
|
+
if not (test_result and test_result.confirmed) and fused.posterior_probability < 0.40:
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
# Build attack path node trail from APM
|
|
52
|
+
attack_path_nodes = []
|
|
53
|
+
if hypothesis.endpoint_id in apm.graph:
|
|
54
|
+
sinks = apm.find_paths_to_sinks(hypothesis.endpoint_id)
|
|
55
|
+
if sinks:
|
|
56
|
+
attack_path_nodes = sinks[0]
|
|
57
|
+
else:
|
|
58
|
+
paths = apm.find_paths_from_endpoint(hypothesis.endpoint_id, max_depth=3)
|
|
59
|
+
if paths:
|
|
60
|
+
attack_path_nodes = paths[0]
|
|
61
|
+
|
|
62
|
+
# Runtime evidence bullet points
|
|
63
|
+
runtime_evidence = []
|
|
64
|
+
if test_result:
|
|
65
|
+
runtime_evidence.append(test_result.summary)
|
|
66
|
+
for obs in test_result.observations:
|
|
67
|
+
runtime_evidence.append(
|
|
68
|
+
f"HTTP {obs.request_method} {obs.request_url} -> Status {obs.response_status} ({obs.latency_ms}ms)"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Correlation explanation
|
|
72
|
+
if test_result and test_result.confirmed:
|
|
73
|
+
correlation_notes = (
|
|
74
|
+
f"Static Attack-Path Model identified vulnerable structure in source code, "
|
|
75
|
+
f"and active runtime test pack '{test_result.testpack_name}' successfully proved exploitability."
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
correlation_notes = (
|
|
79
|
+
f"Static Attack-Path Model detected missing control boundary; runtime validation pending or inconclusive."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
finding_id = hypothesis.id.replace("HYP-", "TR-")
|
|
83
|
+
severity = SEVERITY_BY_CATEGORY.get(category_str, Severity.MEDIUM)
|
|
84
|
+
|
|
85
|
+
ep_node = apm.get_node(hypothesis.endpoint_id)
|
|
86
|
+
source_loc = ep_node.location if ep_node else None
|
|
87
|
+
|
|
88
|
+
return Finding(
|
|
89
|
+
id=finding_id,
|
|
90
|
+
title=hypothesis.title,
|
|
91
|
+
category=category_str,
|
|
92
|
+
severity=severity,
|
|
93
|
+
confidence=confidence,
|
|
94
|
+
endpoint=hypothesis.endpoint_display,
|
|
95
|
+
source_location=source_loc,
|
|
96
|
+
attack_path=attack_path_nodes,
|
|
97
|
+
static_evidence=hypothesis.static_evidence,
|
|
98
|
+
runtime_evidence=runtime_evidence,
|
|
99
|
+
correlation_notes=correlation_notes,
|
|
100
|
+
remediation=get_remediation_for_category(category_str),
|
|
101
|
+
reproduction_steps=test_result.reproduction_steps if test_result else [],
|
|
102
|
+
observations=test_result.observations if test_result else [],
|
|
103
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Correlated security finding schema."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import List, Optional, Dict, Any
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from trace_engine.parsing.locations import SourceLocation
|
|
7
|
+
from trace_engine.runtime.observations import RuntimeObservation
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Severity(str, Enum):
|
|
11
|
+
CRITICAL = "CRITICAL"
|
|
12
|
+
HIGH = "HIGH"
|
|
13
|
+
MEDIUM = "MEDIUM"
|
|
14
|
+
LOW = "LOW"
|
|
15
|
+
INFO = "INFO"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FindingConfidence(str, Enum):
|
|
19
|
+
CONFIRMED = "CONFIRMED"
|
|
20
|
+
HIGH = "HIGH"
|
|
21
|
+
MEDIUM = "MEDIUM"
|
|
22
|
+
POTENTIAL = "POTENTIAL"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Finding(BaseModel):
|
|
26
|
+
"""Correlated vulnerability finding supported by both static AST and runtime proof."""
|
|
27
|
+
id: str
|
|
28
|
+
title: str
|
|
29
|
+
category: str
|
|
30
|
+
severity: Severity
|
|
31
|
+
confidence: FindingConfidence
|
|
32
|
+
endpoint: str
|
|
33
|
+
source_location: Optional[SourceLocation] = None
|
|
34
|
+
attack_path: List[str] = Field(default_factory=list)
|
|
35
|
+
static_evidence: List[str] = Field(default_factory=list)
|
|
36
|
+
runtime_evidence: List[str] = Field(default_factory=list)
|
|
37
|
+
correlation_notes: str
|
|
38
|
+
remediation: str
|
|
39
|
+
reproduction_steps: List[str] = Field(default_factory=list)
|
|
40
|
+
observations: List[RuntimeObservation] = Field(default_factory=list)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Remediation recommendations for security findings."""
|
|
2
|
+
|
|
3
|
+
REMEDIATION_MAP = {
|
|
4
|
+
"BOLA": (
|
|
5
|
+
"Enforce strict object ownership validation before returning records. "
|
|
6
|
+
"Query using both the object ID and the authenticated user/tenant ID (e.g. `WHERE id = :id AND user_id = :current_user.id`)."
|
|
7
|
+
),
|
|
8
|
+
"BFLA": (
|
|
9
|
+
"Implement declarative role-based access control (RBAC) middleware. "
|
|
10
|
+
"Validate user privileges on the backend for every privileged/administrative route."
|
|
11
|
+
),
|
|
12
|
+
"AUTHENTICATION": (
|
|
13
|
+
"Protect this route with authentication dependencies (e.g. `Depends(get_current_user)` or security middleware) "
|
|
14
|
+
"to ensure unauthorized users cannot trigger state-changing or sensitive operations."
|
|
15
|
+
),
|
|
16
|
+
"SSRF": (
|
|
17
|
+
"Avoid making arbitrary outbound HTTP requests with user-controlled URLs. "
|
|
18
|
+
"Implement a strict destination domain whitelist and block loopback/internal IP addresses (RFC 1918)."
|
|
19
|
+
),
|
|
20
|
+
"INJECTION": (
|
|
21
|
+
"Use parameterized queries, prepared statements, or ORM abstraction for all database operations. "
|
|
22
|
+
"Never format or concatenate raw client inputs into SQL/command strings."
|
|
23
|
+
),
|
|
24
|
+
"MASS_ASSIGNMENT": (
|
|
25
|
+
"Define explicit request DTO schemas (e.g. Pydantic models with only allowed mutable fields). "
|
|
26
|
+
"Do not allow raw dictionary binding directly into database models."
|
|
27
|
+
),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_remediation_for_category(category: str) -> str:
|
|
32
|
+
return REMEDIATION_MAP.get(
|
|
33
|
+
category.upper(),
|
|
34
|
+
"Review access control and input validation on this attack path.",
|
|
35
|
+
)
|