@vk.amogh/trace 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 +208 -0
- package/bin/trace.js +112 -0
- package/package.json +46 -0
- 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,18 @@
|
|
|
1
|
+
"""Runtime test observations and HTTP exchange records."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Any, Optional, List
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RuntimeObservation(BaseModel):
|
|
8
|
+
"""Detailed observation from executing a test request against a target."""
|
|
9
|
+
request_method: str
|
|
10
|
+
request_url: str
|
|
11
|
+
request_headers: Dict[str, str] = Field(default_factory=dict)
|
|
12
|
+
request_body: Optional[str] = None
|
|
13
|
+
response_status: int
|
|
14
|
+
response_headers: Dict[str, str] = Field(default_factory=dict)
|
|
15
|
+
response_body: str = ""
|
|
16
|
+
latency_ms: float = 0.0
|
|
17
|
+
is_anomaly: bool = False
|
|
18
|
+
observation_notes: List[str] = Field(default_factory=list)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Target runtime specification."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Target(BaseModel):
|
|
8
|
+
"""Runtime target specification for security validation."""
|
|
9
|
+
url: str
|
|
10
|
+
scheme: str = "http"
|
|
11
|
+
host: str = "127.0.0.1"
|
|
12
|
+
port: int = 8000
|
|
13
|
+
base_path: str = ""
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_url(cls, url: str) -> "Target":
|
|
17
|
+
parsed = urlparse(url)
|
|
18
|
+
scheme = parsed.scheme or "http"
|
|
19
|
+
host = parsed.hostname or "127.0.0.1"
|
|
20
|
+
port = parsed.port or (443 if scheme == "https" else 80)
|
|
21
|
+
base_path = parsed.path.rstrip("/")
|
|
22
|
+
normalized_url = f"{scheme}://{host}:{port}{base_path}"
|
|
23
|
+
return cls(url=normalized_url, scheme=scheme, host=host, port=port, base_path=base_path)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Security signals and hypotheses module."""
|
|
2
|
+
|
|
3
|
+
from trace_engine.security.signals import SignalType, SecuritySignal
|
|
4
|
+
from trace_engine.security.hypotheses import (
|
|
5
|
+
VulnerabilityCategory,
|
|
6
|
+
SecurityHypothesis,
|
|
7
|
+
HypothesisEngine,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"SignalType",
|
|
12
|
+
"SecuritySignal",
|
|
13
|
+
"VulnerabilityCategory",
|
|
14
|
+
"SecurityHypothesis",
|
|
15
|
+
"HypothesisEngine",
|
|
16
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Neuro-Symbolic Confidence Fusion Engine combining Neural, AST, and Dynamic Evidence via Bayesian calibration.
|
|
2
|
+
|
|
3
|
+
Implements the 3-Signal Bayesian Evidence Fusion model:
|
|
4
|
+
Confidence = 1 - (1 - P_SecureBERT) * (1 - P_AST_Dataflow) * (1 - P_Dynamic_Oracle)
|
|
5
|
+
|
|
6
|
+
Levels:
|
|
7
|
+
- Level 3 (Triple Match): SecureBERT (0.85) + AST dataflow sink + Dynamic probe error -> Confidence ~99.8% (Critical / Zero-False-Positive Verified).
|
|
8
|
+
- Level 2 (Static + Dynamic Partial): Probe blocked by WAF, but AST source-to-sink dataflow is proven -> Confidence ~82% (High / Code-Audited).
|
|
9
|
+
- Level 1 (Single Signal): Only SecureBERT flags it with no AST sink -> Soft hypothesis for agent exploration.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import math
|
|
13
|
+
from typing import Optional, Dict, Any, List
|
|
14
|
+
from pydantic import BaseModel, Field
|
|
15
|
+
from trace_engine.findings.model import FindingConfidence
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfidenceEvidence(BaseModel):
|
|
19
|
+
"""Signals supplied to the Bayesian Fusion Engine."""
|
|
20
|
+
neural_score: Optional[float] = Field(None, description="SecureBERT 2.0 probability [0.0 - 1.0]")
|
|
21
|
+
symbolic_ast_score: float = Field(0.5, description="AST dataflow & reachability confidence [0.0 - 1.0]")
|
|
22
|
+
dynamic_test_score: Optional[float] = Field(None, description="Active probe verification confidence [0.0 - 1.0]")
|
|
23
|
+
is_dynamically_confirmed: bool = Field(False, description="True if exploit payload was actively verified")
|
|
24
|
+
has_sanitizer: bool = Field(False, description="True if defensive guard/sanitizer was identified in AST")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FusedConfidenceResult(BaseModel):
|
|
28
|
+
"""Calibrated result from Neuro-Symbolic Bayesian evidence fusion."""
|
|
29
|
+
posterior_probability: float = Field(..., description="Calibrated Bayesian posterior [0.0 - 1.0]")
|
|
30
|
+
confidence_level: FindingConfidence = Field(..., description="Discrete confidence category")
|
|
31
|
+
log_odds: float = Field(..., description="Log-odds score")
|
|
32
|
+
fusion_level: str = Field(..., description="Level 1, Level 2, or Level 3")
|
|
33
|
+
evidence_breakdown: Dict[str, float] = Field(default_factory=dict)
|
|
34
|
+
rationale: str = Field(..., description="Explanation of signal weights and fusion outcome")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class NeuroSymbolicConfidenceEngine:
|
|
38
|
+
"""Combines Neural (System 1 SecureBERT), Symbolic AST (System 2 Dataflow Sinks),
|
|
39
|
+
and Dynamic Verification signals into mathematically calibrated Bayesian confidence.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def fuse_evidence(cls, evidence: ConfidenceEvidence) -> FusedConfidenceResult:
|
|
44
|
+
"""Calculates calibrated posterior probability from multiple independent security signals:
|
|
45
|
+
Confidence = 1 - (1 - P_SecureBERT) * (1 - P_AST_Dataflow) * (1 - P_Dynamic_Oracle)
|
|
46
|
+
"""
|
|
47
|
+
# 1. Neural Signal (SecureBERT)
|
|
48
|
+
p_bert = 0.0
|
|
49
|
+
if evidence.neural_score is not None:
|
|
50
|
+
p_bert = max(0.0, min(0.99, evidence.neural_score))
|
|
51
|
+
|
|
52
|
+
# 2. Symbolic AST Signal (Dataflow Sinks & Control Flow)
|
|
53
|
+
p_ast = max(0.0, min(0.99, evidence.symbolic_ast_score))
|
|
54
|
+
if evidence.has_sanitizer:
|
|
55
|
+
p_ast = p_ast * 0.15 # 85% penalty if sanitizer or validator detected in AST
|
|
56
|
+
|
|
57
|
+
# 3. Dynamic Signal (Active Testpack Probes & Timing Oracle)
|
|
58
|
+
p_dyn = 0.0
|
|
59
|
+
if evidence.is_dynamically_confirmed:
|
|
60
|
+
p_dyn = 0.99
|
|
61
|
+
elif evidence.dynamic_test_score is not None:
|
|
62
|
+
p_dyn = max(0.0, min(0.99, evidence.dynamic_test_score))
|
|
63
|
+
|
|
64
|
+
# Bayesian Independence Fusion Formula:
|
|
65
|
+
# Confidence = 1 - (1 - P_SecureBERT) * (1 - P_AST_Dataflow) * (1 - P_Dynamic_Oracle)
|
|
66
|
+
prob_not_vuln = (1.0 - p_bert) * (1.0 - p_ast) * (1.0 - p_dyn)
|
|
67
|
+
raw_confidence = 1.0 - prob_not_vuln
|
|
68
|
+
posterior = round(max(0.01, min(0.999, raw_confidence)), 4)
|
|
69
|
+
|
|
70
|
+
# Log-odds representation:
|
|
71
|
+
p_clamped = max(0.001, min(0.999, posterior))
|
|
72
|
+
log_odds = round(math.log(p_clamped / (1.0 - p_clamped)), 3)
|
|
73
|
+
|
|
74
|
+
# Classify into Levels
|
|
75
|
+
signals_present = sum([
|
|
76
|
+
1 if p_bert >= 0.4 else 0,
|
|
77
|
+
1 if p_ast >= 0.4 else 0,
|
|
78
|
+
1 if p_dyn >= 0.4 else 0,
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
if evidence.is_dynamically_confirmed or (signals_present >= 3 and posterior >= 0.95):
|
|
82
|
+
fusion_level = "Level 3 (Triple Match: Neural + AST + Dynamic Runtime Confirmation)"
|
|
83
|
+
level = FindingConfidence.CONFIRMED
|
|
84
|
+
elif (signals_present >= 2 and posterior >= 0.75) or posterior >= 0.80:
|
|
85
|
+
fusion_level = "Level 2 (Static + Dynamic Partial: Proven AST Dataflow Sink)"
|
|
86
|
+
level = FindingConfidence.HIGH
|
|
87
|
+
elif posterior >= 0.50:
|
|
88
|
+
fusion_level = "Level 1 (Single Signal: Exploratory Hypothesis)"
|
|
89
|
+
level = FindingConfidence.MEDIUM
|
|
90
|
+
else:
|
|
91
|
+
fusion_level = "Level 1 (Low Confidence / Mitigated Signal)"
|
|
92
|
+
level = FindingConfidence.POTENTIAL
|
|
93
|
+
|
|
94
|
+
breakdown = {
|
|
95
|
+
"p_securebert": round(p_bert, 3),
|
|
96
|
+
"p_ast_dataflow": round(p_ast, 3),
|
|
97
|
+
"p_dynamic_oracle": round(p_dyn, 3),
|
|
98
|
+
"fused_confidence": posterior,
|
|
99
|
+
"has_sanitizer": 1.0 if evidence.has_sanitizer else 0.0,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
signals = []
|
|
103
|
+
if p_dyn >= 0.90:
|
|
104
|
+
signals.append("empirically verified by dynamic runtime oracle")
|
|
105
|
+
elif p_dyn > 0.3:
|
|
106
|
+
signals.append(f"dynamic response signature ({int(p_dyn*100)}%)")
|
|
107
|
+
if p_bert > 0.4:
|
|
108
|
+
signals.append(f"SecureBERT semantic match ({int(p_bert*100)}%)")
|
|
109
|
+
if p_ast > 0.4:
|
|
110
|
+
signals.append(f"AST unparameterized sink reachability ({int(p_ast*100)}%)")
|
|
111
|
+
if evidence.has_sanitizer:
|
|
112
|
+
signals.append("downweighted: input sanitizer detected in AST")
|
|
113
|
+
|
|
114
|
+
rationale = (
|
|
115
|
+
f"Bayesian Evidence Fusion: {fusion_level} with calibrated confidence {int(posterior*100)}%. "
|
|
116
|
+
f"Signals: {'; '.join(signals) if signals else 'baseline hypothesis'}."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return FusedConfidenceResult(
|
|
120
|
+
posterior_probability=posterior,
|
|
121
|
+
confidence_level=level,
|
|
122
|
+
log_odds=log_odds,
|
|
123
|
+
fusion_level=fusion_level,
|
|
124
|
+
evidence_breakdown=breakdown,
|
|
125
|
+
rationale=rationale,
|
|
126
|
+
)
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Security hypotheses generation from APM and deterministic signals."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import List, Dict, Any, Optional
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from trace_engine.apm.model import AttackPathModel
|
|
7
|
+
from trace_engine.framework.base import Endpoint
|
|
8
|
+
from trace_engine.security.signals import SecuritySignal, SignalType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class VulnerabilityCategory(str, Enum):
|
|
12
|
+
BOLA = "BOLA"
|
|
13
|
+
BFLA = "BFLA"
|
|
14
|
+
AUTHENTICATION = "AUTHENTICATION"
|
|
15
|
+
SSRF = "SSRF"
|
|
16
|
+
INJECTION = "INJECTION"
|
|
17
|
+
MASS_ASSIGNMENT = "MASS_ASSIGNMENT"
|
|
18
|
+
PATH_TRAVERSAL = "PATH_TRAVERSAL"
|
|
19
|
+
SSTI = "SSTI"
|
|
20
|
+
CORS = "CORS"
|
|
21
|
+
DESERIALIZATION = "DESERIALIZATION"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SecurityHypothesis(BaseModel):
|
|
25
|
+
"""A testable hypothesis about an attack path in the application."""
|
|
26
|
+
id: str
|
|
27
|
+
category: VulnerabilityCategory
|
|
28
|
+
endpoint_id: str
|
|
29
|
+
endpoint_display: str
|
|
30
|
+
title: str
|
|
31
|
+
description: str
|
|
32
|
+
recommended_test_pack: str
|
|
33
|
+
confidence_prior: float = 0.5 # Prior confidence from static signals alone
|
|
34
|
+
static_evidence: List[str] = Field(default_factory=list)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class HypothesisEngine:
|
|
38
|
+
"""Analyzes APM and endpoints to derive actionable security hypotheses."""
|
|
39
|
+
|
|
40
|
+
def derive_hypotheses(
|
|
41
|
+
self, apm: AttackPathModel, endpoints: List[Endpoint]
|
|
42
|
+
) -> List[SecurityHypothesis]:
|
|
43
|
+
hypotheses: List[SecurityHypothesis] = []
|
|
44
|
+
hypo_idx = 1
|
|
45
|
+
|
|
46
|
+
for ep in endpoints:
|
|
47
|
+
ep_disp = ep.display_name()
|
|
48
|
+
|
|
49
|
+
# 1. BOLA Hypothesis: Endpoint accepts ID parameter, touches DB, but lacks ownership verification
|
|
50
|
+
if ep.object_identifier and ep.database_access:
|
|
51
|
+
hypotheses.append(
|
|
52
|
+
SecurityHypothesis(
|
|
53
|
+
id=f"HYP-BOLA-{hypo_idx:03d}",
|
|
54
|
+
category=VulnerabilityCategory.BOLA,
|
|
55
|
+
endpoint_id=ep.id,
|
|
56
|
+
endpoint_display=ep_disp,
|
|
57
|
+
title=f"Potential BOLA on {ep_disp}",
|
|
58
|
+
description="Endpoint looks up objects by client-supplied ID with potential absence of tenant/user ownership check.",
|
|
59
|
+
recommended_test_pack="bola",
|
|
60
|
+
confidence_prior=0.75,
|
|
61
|
+
static_evidence=[
|
|
62
|
+
f"Path parameter matches object ID pattern: {[p.name for p in ep.parameters if p.location == 'path']}",
|
|
63
|
+
"Database lookup confirmed in handler source",
|
|
64
|
+
f"Declared auth required: {ep.auth_required}",
|
|
65
|
+
],
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
hypo_idx += 1
|
|
69
|
+
|
|
70
|
+
# 2. BFLA Hypothesis: Endpoint has administrative or privileged semantics
|
|
71
|
+
if "admin" in ep.path.lower() or "admin" in [r.lower() for r in ep.roles]:
|
|
72
|
+
hypotheses.append(
|
|
73
|
+
SecurityHypothesis(
|
|
74
|
+
id=f"HYP-BFLA-{hypo_idx:03d}",
|
|
75
|
+
category=VulnerabilityCategory.BFLA,
|
|
76
|
+
endpoint_id=ep.id,
|
|
77
|
+
endpoint_display=ep_disp,
|
|
78
|
+
title=f"Potential BFLA on privileged endpoint {ep_disp}",
|
|
79
|
+
description="Administrative/privileged action may fail to enforce strict RBAC check at runtime.",
|
|
80
|
+
recommended_test_pack="bfla",
|
|
81
|
+
confidence_prior=0.70,
|
|
82
|
+
static_evidence=[
|
|
83
|
+
f"Privileged path/role identifier present: {ep.roles or ['admin path']}",
|
|
84
|
+
f"Source location: {ep.source}",
|
|
85
|
+
],
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
hypo_idx += 1
|
|
89
|
+
|
|
90
|
+
# 3. Authentication Bypass / Missing Auth: State-changing endpoint without auth
|
|
91
|
+
if not ep.auth_required and (ep.state_changing or ep.sensitive_data):
|
|
92
|
+
if not ep.path.endswith("/login") and not ep.path.endswith("/register"):
|
|
93
|
+
hypotheses.append(
|
|
94
|
+
SecurityHypothesis(
|
|
95
|
+
id=f"HYP-AUTH-{hypo_idx:03d}",
|
|
96
|
+
category=VulnerabilityCategory.AUTHENTICATION,
|
|
97
|
+
endpoint_id=ep.id,
|
|
98
|
+
endpoint_display=ep_disp,
|
|
99
|
+
title=f"Unauthenticated sensitive endpoint {ep_disp}",
|
|
100
|
+
description="Sensitive or state-modifying endpoint does not declare authentication requirement.",
|
|
101
|
+
recommended_test_pack="authentication",
|
|
102
|
+
confidence_prior=0.80,
|
|
103
|
+
static_evidence=[
|
|
104
|
+
f"State-changing: {ep.state_changing}",
|
|
105
|
+
f"Sensitive data indicator: {ep.sensitive_data}",
|
|
106
|
+
"No auth middleware or dependency detected",
|
|
107
|
+
],
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
hypo_idx += 1
|
|
111
|
+
|
|
112
|
+
# 4. SSRF Hypothesis: Endpoint takes URL or makes external network calls
|
|
113
|
+
if ep.external_network or any("url" in p.name.lower() for p in ep.parameters):
|
|
114
|
+
hypotheses.append(
|
|
115
|
+
SecurityHypothesis(
|
|
116
|
+
id=f"HYP-SSRF-{hypo_idx:03d}",
|
|
117
|
+
category=VulnerabilityCategory.SSRF,
|
|
118
|
+
endpoint_id=ep.id,
|
|
119
|
+
endpoint_display=ep_disp,
|
|
120
|
+
title=f"Potential SSRF on {ep_disp}",
|
|
121
|
+
description="Endpoint handles URL parameter and communicates with outbound HTTP client.",
|
|
122
|
+
recommended_test_pack="ssrf",
|
|
123
|
+
confidence_prior=0.85,
|
|
124
|
+
static_evidence=[
|
|
125
|
+
"Outbound HTTP client calls detected in handler body",
|
|
126
|
+
f"URL parameter detected: {[p.name for p in ep.parameters if 'url' in p.name.lower()]}",
|
|
127
|
+
],
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
hypo_idx += 1
|
|
131
|
+
|
|
132
|
+
# 5. Mass Assignment Hypothesis: PATCH or PUT or POST on user or profile
|
|
133
|
+
if ep.method in ("PATCH", "PUT", "POST") and any(term in ep.path.lower() for term in ("user", "profile", "account")):
|
|
134
|
+
hypotheses.append(
|
|
135
|
+
SecurityHypothesis(
|
|
136
|
+
id=f"HYP-MASS-{hypo_idx:03d}",
|
|
137
|
+
category=VulnerabilityCategory.MASS_ASSIGNMENT,
|
|
138
|
+
endpoint_id=ep.id,
|
|
139
|
+
endpoint_display=ep_disp,
|
|
140
|
+
title=f"Potential Mass Assignment on {ep_disp}",
|
|
141
|
+
description="Profile update endpoint may bind sensitive attributes (e.g. is_admin, role) directly from payload.",
|
|
142
|
+
recommended_test_pack="mass_assignment",
|
|
143
|
+
confidence_prior=0.65,
|
|
144
|
+
static_evidence=[
|
|
145
|
+
f"State modifying method {ep.method} on profile/user path",
|
|
146
|
+
"Model payload deserialization detected",
|
|
147
|
+
],
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
hypo_idx += 1
|
|
151
|
+
|
|
152
|
+
# 6. Injection Indicator Hypothesis: Search or query with DB access
|
|
153
|
+
if any(term in ep.path.lower() for term in ("search", "filter", "query")) and ep.database_access:
|
|
154
|
+
hypotheses.append(
|
|
155
|
+
SecurityHypothesis(
|
|
156
|
+
id=f"HYP-INJ-{hypo_idx:03d}",
|
|
157
|
+
category=VulnerabilityCategory.INJECTION,
|
|
158
|
+
endpoint_id=ep.id,
|
|
159
|
+
endpoint_display=ep_disp,
|
|
160
|
+
title=f"Potential Injection Indicator on {ep_disp}",
|
|
161
|
+
description="Dynamic search query routed to database layer without verified parameterization.",
|
|
162
|
+
recommended_test_pack="injection",
|
|
163
|
+
confidence_prior=0.60,
|
|
164
|
+
static_evidence=[
|
|
165
|
+
"User query parameter routed to database handler",
|
|
166
|
+
f"Endpoint parameters: {[p.name for p in ep.parameters]}",
|
|
167
|
+
],
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
hypo_idx += 1
|
|
171
|
+
|
|
172
|
+
# 7. Path Traversal Hypothesis: Endpoints accepting file or path parameters
|
|
173
|
+
file_param_names = [p.name for p in ep.parameters if any(term in p.name.lower() for term in ("file", "path", "doc", "dir", "download", "name", "asset", "key"))]
|
|
174
|
+
if file_param_names or any(term in ep.path.lower() for term in ("file", "doc", "download", "asset", "static")):
|
|
175
|
+
hypotheses.append(
|
|
176
|
+
SecurityHypothesis(
|
|
177
|
+
id=f"HYP-TRAV-{hypo_idx:03d}",
|
|
178
|
+
category=VulnerabilityCategory.PATH_TRAVERSAL,
|
|
179
|
+
endpoint_id=ep.id,
|
|
180
|
+
endpoint_display=ep_disp,
|
|
181
|
+
title=f"Potential Path Traversal on {ep_disp}",
|
|
182
|
+
description="Endpoint handles file or path parameters without verified filesystem boundary isolation.",
|
|
183
|
+
recommended_test_pack="path_traversal",
|
|
184
|
+
confidence_prior=0.65,
|
|
185
|
+
static_evidence=[
|
|
186
|
+
f"File/path parameter names detected: {file_param_names or [ep.path]}",
|
|
187
|
+
f"Source location: {ep.source}",
|
|
188
|
+
],
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
hypo_idx += 1
|
|
192
|
+
|
|
193
|
+
# 8. Server-Side Template Injection (SSTI) Hypothesis
|
|
194
|
+
template_param_names = [p.name for p in ep.parameters if any(term in p.name.lower() for term in ("template", "render", "msg", "format", "view", "preview"))]
|
|
195
|
+
if template_param_names or any(term in ep.path.lower() for term in ("template", "render", "preview", "email", "notify")):
|
|
196
|
+
hypotheses.append(
|
|
197
|
+
SecurityHypothesis(
|
|
198
|
+
id=f"HYP-SSTI-{hypo_idx:03d}",
|
|
199
|
+
category=VulnerabilityCategory.SSTI,
|
|
200
|
+
endpoint_id=ep.id,
|
|
201
|
+
endpoint_display=ep_disp,
|
|
202
|
+
title=f"Potential Template Injection on {ep_disp}",
|
|
203
|
+
description="Endpoint processes dynamic template formatting or user-interpolated rendering.",
|
|
204
|
+
recommended_test_pack="ssti",
|
|
205
|
+
confidence_prior=0.60,
|
|
206
|
+
static_evidence=[
|
|
207
|
+
f"Template rendering parameter detected: {template_param_names or [ep.path]}",
|
|
208
|
+
],
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
hypo_idx += 1
|
|
212
|
+
|
|
213
|
+
# 9. CORS Misconfiguration Hypothesis
|
|
214
|
+
if ep.auth_required and ep.sensitive_data:
|
|
215
|
+
hypotheses.append(
|
|
216
|
+
SecurityHypothesis(
|
|
217
|
+
id=f"HYP-CORS-{hypo_idx:03d}",
|
|
218
|
+
category=VulnerabilityCategory.CORS,
|
|
219
|
+
endpoint_id=ep.id,
|
|
220
|
+
endpoint_display=ep_disp,
|
|
221
|
+
title=f"Potential CORS Misconfiguration on {ep_disp}",
|
|
222
|
+
description="Authenticated sensitive endpoint may reflect arbitrary cross-origin request headers with credentials.",
|
|
223
|
+
recommended_test_pack="cors",
|
|
224
|
+
confidence_prior=0.55,
|
|
225
|
+
static_evidence=[
|
|
226
|
+
f"Authenticated: {ep.auth_required}, Sensitive data: {ep.sensitive_data}",
|
|
227
|
+
],
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
hypo_idx += 1
|
|
231
|
+
|
|
232
|
+
# 10. Insecure Deserialization Hypothesis
|
|
233
|
+
if ep.method in ("POST", "PUT", "PATCH") and any(term in ep.path.lower() for term in ("import", "upload", "state", "event", "session", "restore", "sync")):
|
|
234
|
+
hypotheses.append(
|
|
235
|
+
SecurityHypothesis(
|
|
236
|
+
id=f"HYP-DESER-{hypo_idx:03d}",
|
|
237
|
+
category=VulnerabilityCategory.DESERIALIZATION,
|
|
238
|
+
endpoint_id=ep.id,
|
|
239
|
+
endpoint_display=ep_disp,
|
|
240
|
+
title=f"Potential Insecure Deserialization on {ep_disp}",
|
|
241
|
+
description="State or data restoration endpoint accepting structured object representations.",
|
|
242
|
+
recommended_test_pack="deserialization",
|
|
243
|
+
confidence_prior=0.60,
|
|
244
|
+
static_evidence=[
|
|
245
|
+
f"State restoration path pattern in method {ep.method}: {ep.path}",
|
|
246
|
+
],
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
hypo_idx += 1
|
|
250
|
+
|
|
251
|
+
return hypotheses
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Deterministic security signal models and static detectors."""
|
|
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
|
+
|
|
8
|
+
|
|
9
|
+
class SignalType(str, Enum):
|
|
10
|
+
MISSING_AUTH = "MISSING_AUTH"
|
|
11
|
+
UNTRUSTED_OBJECT_LOOKUP = "UNTRUSTED_OBJECT_LOOKUP"
|
|
12
|
+
PRIVILEGE_MISMATCH = "PRIVILEGE_MISMATCH"
|
|
13
|
+
USER_URL_OUTBOUND = "USER_URL_OUTBOUND"
|
|
14
|
+
DYNAMIC_QUERY_CONSTRUCTION = "DYNAMIC_QUERY_CONSTRUCTION"
|
|
15
|
+
MASS_ASSIGNMENT_SURFACE = "MASS_ASSIGNMENT_SURFACE"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SecuritySignal(BaseModel):
|
|
19
|
+
"""A deterministic indicator of a potential vulnerability path in source code."""
|
|
20
|
+
id: str
|
|
21
|
+
signal_type: SignalType
|
|
22
|
+
endpoint_id: str
|
|
23
|
+
title: str
|
|
24
|
+
description: str
|
|
25
|
+
severity_potential: str # "CRITICAL", "HIGH", "MEDIUM", "LOW"
|
|
26
|
+
source: SourceLocation
|
|
27
|
+
evidence_snippet: Optional[str] = None
|
|
28
|
+
properties: Dict[str, Any] = Field(default_factory=dict)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Statistical Timing Oracle using Welch's t-test for bulletproof blind injection verification."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from typing import List, Tuple, Dict, Any, Optional
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TimingVerificationResult(BaseModel):
|
|
9
|
+
"""Result of statistical timing hypothesis verification."""
|
|
10
|
+
is_confirmed: bool = Field(..., description="True if delay is statistically proven beyond network noise")
|
|
11
|
+
t_statistic: float = Field(..., description="Welch's t-statistic")
|
|
12
|
+
p_value: float = Field(..., description="Two-tailed p-value (significance level)")
|
|
13
|
+
degrees_of_freedom: float = Field(..., description="Welch-Satterthwaite degrees of freedom")
|
|
14
|
+
mean_baseline_sec: float = Field(..., description="Mean baseline latency in seconds")
|
|
15
|
+
mean_delay_sec: float = Field(..., description="Mean injected delay latency in seconds")
|
|
16
|
+
observed_shift_sec: float = Field(..., description="Observed latency delta (mean_delay - mean_baseline)")
|
|
17
|
+
confidence: float = Field(..., description="Bayesian probability score [0.0 - 1.0]")
|
|
18
|
+
summary: str = Field(..., description="Detailed statistical rationale")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StatisticalTimingOracle:
|
|
22
|
+
"""Rigorous statistical testing engine for blind SQL, Command, and Code injection verification.
|
|
23
|
+
|
|
24
|
+
Applies Welch's two-sample t-test to eliminate false positives from network latency jitter and CPU throttling.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def calculate_stats(samples: List[float]) -> Tuple[float, float]:
|
|
29
|
+
"""Calculates sample mean and unbiased sample variance."""
|
|
30
|
+
n = len(samples)
|
|
31
|
+
if n < 2:
|
|
32
|
+
return (samples[0] if n == 1 else 0.0, 0.0)
|
|
33
|
+
mean = sum(samples) / n
|
|
34
|
+
variance = sum((x - mean) ** 2 for x in samples) / (n - 1)
|
|
35
|
+
return mean, max(variance, 1e-9)
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def evaluate_timing_vulnerability(
|
|
39
|
+
cls,
|
|
40
|
+
baseline_latencies: List[float],
|
|
41
|
+
delay_latencies: List[float],
|
|
42
|
+
expected_delay_sec: float = 1.5,
|
|
43
|
+
alpha_threshold: float = 0.01,
|
|
44
|
+
) -> TimingVerificationResult:
|
|
45
|
+
"""Evaluates whether delay latencies represent a statistically significant vulnerability reaction.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
baseline_latencies: List of latency measurements (seconds) for non-delay probes.
|
|
49
|
+
delay_latencies: List of latency measurements (seconds) for delay-injected probes.
|
|
50
|
+
expected_delay_sec: Nominal delay programmed into probe payload (e.g. 1.5s).
|
|
51
|
+
alpha_threshold: Significance level threshold (default 0.01 for 99% confidence).
|
|
52
|
+
"""
|
|
53
|
+
n1 = len(baseline_latencies)
|
|
54
|
+
n2 = len(delay_latencies)
|
|
55
|
+
|
|
56
|
+
if n1 < 2 or n2 < 2:
|
|
57
|
+
return TimingVerificationResult(
|
|
58
|
+
is_confirmed=False,
|
|
59
|
+
t_statistic=0.0,
|
|
60
|
+
p_value=1.0,
|
|
61
|
+
degrees_of_freedom=0.0,
|
|
62
|
+
mean_baseline_sec=sum(baseline_latencies) / max(1, n1),
|
|
63
|
+
mean_delay_sec=sum(delay_latencies) / max(1, n2),
|
|
64
|
+
observed_shift_sec=0.0,
|
|
65
|
+
confidence=0.1,
|
|
66
|
+
summary="Insufficient sample size for statistical verification (minimum 2 samples required per group).",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
mean1, var1 = cls.calculate_stats(baseline_latencies)
|
|
70
|
+
mean2, var2 = cls.calculate_stats(delay_latencies)
|
|
71
|
+
|
|
72
|
+
delta = mean2 - mean1
|
|
73
|
+
se = math.sqrt((var1 / n1) + (var2 / n2))
|
|
74
|
+
|
|
75
|
+
# Welch-Satterthwaite degrees of freedom
|
|
76
|
+
numerator = ((var1 / n1) + (var2 / n2)) ** 2
|
|
77
|
+
denominator = (((var1 / n1) ** 2) / (n1 - 1)) + (((var2 / n2) ** 2) / (n2 - 1))
|
|
78
|
+
df = max(1.0, numerator / max(denominator, 1e-9))
|
|
79
|
+
|
|
80
|
+
t_stat = delta / max(se, 1e-9)
|
|
81
|
+
|
|
82
|
+
# Approximate p-value using standard normal / t-distribution tail
|
|
83
|
+
# For large df, t approximates normal. For small df, Cornish-Fisher / Student approximation
|
|
84
|
+
p_val = cls._approximate_p_value(t_stat, df)
|
|
85
|
+
|
|
86
|
+
# Requirements for confirmation:
|
|
87
|
+
# 1. Statistically significant difference (p < alpha_threshold, default 0.01)
|
|
88
|
+
# 2. Positive direction (t > 2.5)
|
|
89
|
+
# 3. Magnitude matches at least 85% of nominal programmed sleep time (Delta t >= 0.85 * delay)
|
|
90
|
+
shift_ratio = delta / max(expected_delay_sec, 0.1)
|
|
91
|
+
confirmed = (t_stat > 2.5) and (p_val < alpha_threshold) and (shift_ratio >= 0.85)
|
|
92
|
+
|
|
93
|
+
confidence = 0.99 if confirmed else (0.40 if t_stat > 2.0 and delta > 0 else 0.10)
|
|
94
|
+
|
|
95
|
+
if confirmed:
|
|
96
|
+
summary = (
|
|
97
|
+
f"Statistical Timing Oracle CONFIRMED blind injection: Mean latency shifted by +{delta:.2f}s "
|
|
98
|
+
f"(nominal {expected_delay_sec:.1f}s, ratio={shift_ratio:.1%}) with Welch's t={t_stat:.2f} (df={df:.1f}, p={p_val:.2e} < {alpha_threshold}). "
|
|
99
|
+
f"Null hypothesis (network jitter) rejected with >99% confidence."
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
summary = (
|
|
103
|
+
f"Statistical Timing Oracle INCONCLUSIVE: Observed latency shift +{delta:.2f}s "
|
|
104
|
+
f"(t={t_stat:.2f}, p={p_val:.4f}). Did not satisfy 85% expected delay shift ({expected_delay_sec:.1f}s) "
|
|
105
|
+
f"or significance threshold ({alpha_threshold})."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return TimingVerificationResult(
|
|
109
|
+
is_confirmed=confirmed,
|
|
110
|
+
t_statistic=round(t_stat, 3),
|
|
111
|
+
p_value=round(p_val, 6),
|
|
112
|
+
degrees_of_freedom=round(df, 1),
|
|
113
|
+
mean_baseline_sec=round(mean1, 3),
|
|
114
|
+
mean_delay_sec=round(mean2, 3),
|
|
115
|
+
observed_shift_sec=round(delta, 3),
|
|
116
|
+
confidence=confidence,
|
|
117
|
+
summary=summary,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def _approximate_p_value(t: float, df: float) -> float:
|
|
122
|
+
"""Approximates two-tailed p-value for Student's / Welch's t-distribution."""
|
|
123
|
+
if t <= 0:
|
|
124
|
+
return 1.0
|
|
125
|
+
|
|
126
|
+
# Normal approximation with tail correction for degrees of freedom
|
|
127
|
+
# Z = t * (1 - 1/(4*df))
|
|
128
|
+
z = t * (1.0 - (1.0 / (4.0 * max(df, 1.0))))
|
|
129
|
+
# Error function complementary approximation
|
|
130
|
+
erf_arg = z / math.sqrt(2.0)
|
|
131
|
+
p = math.erfc(erf_arg)
|
|
132
|
+
return max(1e-12, min(1.0, p))
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Security test pack system."""
|
|
2
|
+
|
|
3
|
+
from trace_engine.testpacks.base import (
|
|
4
|
+
TestPack,
|
|
5
|
+
TestContext,
|
|
6
|
+
TestExecutionResult,
|
|
7
|
+
UserIdentity,
|
|
8
|
+
)
|
|
9
|
+
from trace_engine.testpacks.registry import TestPackRegistry, default_registry
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"TestPack",
|
|
13
|
+
"TestContext",
|
|
14
|
+
"TestExecutionResult",
|
|
15
|
+
"UserIdentity",
|
|
16
|
+
"TestPackRegistry",
|
|
17
|
+
"default_registry",
|
|
18
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|