@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.
Files changed (249) hide show
  1. package/README.md +208 -0
  2. package/bin/trace.js +112 -0
  3. package/package.json +46 -0
  4. package/pyproject.toml +39 -0
  5. package/src/trace_engine/__init__.py +8 -0
  6. package/src/trace_engine/__pycache__/__init__.cpython-311.pyc +0 -0
  7. package/src/trace_engine/__pycache__/cli.cpython-311.pyc +0 -0
  8. package/src/trace_engine/__pycache__/doctor.cpython-311.pyc +0 -0
  9. package/src/trace_engine/__pycache__/interactive.cpython-311.pyc +0 -0
  10. package/src/trace_engine/__pycache__/verify.cpython-311.pyc +0 -0
  11. package/src/trace_engine/ai/__init__.py +7 -0
  12. package/src/trace_engine/ai/__pycache__/__init__.cpython-311.pyc +0 -0
  13. package/src/trace_engine/ai/__pycache__/base.cpython-311.pyc +0 -0
  14. package/src/trace_engine/ai/__pycache__/ollama.cpython-311.pyc +0 -0
  15. package/src/trace_engine/ai/__pycache__/planner.cpython-311.pyc +0 -0
  16. package/src/trace_engine/ai/base.py +23 -0
  17. package/src/trace_engine/ai/ollama.py +50 -0
  18. package/src/trace_engine/ai/planner.py +40 -0
  19. package/src/trace_engine/apm/__init__.py +18 -0
  20. package/src/trace_engine/apm/__pycache__/__init__.cpython-311.pyc +0 -0
  21. package/src/trace_engine/apm/__pycache__/builder.cpython-311.pyc +0 -0
  22. package/src/trace_engine/apm/__pycache__/edges.cpython-311.pyc +0 -0
  23. package/src/trace_engine/apm/__pycache__/model.cpython-311.pyc +0 -0
  24. package/src/trace_engine/apm/__pycache__/nodes.cpython-311.pyc +0 -0
  25. package/src/trace_engine/apm/__pycache__/serialization.cpython-311.pyc +0 -0
  26. package/src/trace_engine/apm/builder.py +208 -0
  27. package/src/trace_engine/apm/edges.py +25 -0
  28. package/src/trace_engine/apm/model.py +107 -0
  29. package/src/trace_engine/apm/nodes.py +27 -0
  30. package/src/trace_engine/apm/serialization.py +105 -0
  31. package/src/trace_engine/benchmark/__init__.py +5 -0
  32. package/src/trace_engine/benchmark/__pycache__/__init__.cpython-311.pyc +0 -0
  33. package/src/trace_engine/benchmark/__pycache__/owasp.cpython-311.pyc +0 -0
  34. package/src/trace_engine/benchmark/owasp.py +183 -0
  35. package/src/trace_engine/cli.py +1184 -0
  36. package/src/trace_engine/config/__init__.py +35 -0
  37. package/src/trace_engine/config/__pycache__/__init__.cpython-311.pyc +0 -0
  38. package/src/trace_engine/config/__pycache__/defaults.cpython-311.pyc +0 -0
  39. package/src/trace_engine/config/__pycache__/loader.cpython-311.pyc +0 -0
  40. package/src/trace_engine/config/__pycache__/settings.cpython-311.pyc +0 -0
  41. package/src/trace_engine/config/defaults.py +48 -0
  42. package/src/trace_engine/config/loader.py +64 -0
  43. package/src/trace_engine/config/settings.py +72 -0
  44. package/src/trace_engine/doctor.py +250 -0
  45. package/src/trace_engine/findings/__init__.py +15 -0
  46. package/src/trace_engine/findings/__pycache__/__init__.cpython-311.pyc +0 -0
  47. package/src/trace_engine/findings/__pycache__/correlate.cpython-311.pyc +0 -0
  48. package/src/trace_engine/findings/__pycache__/model.cpython-311.pyc +0 -0
  49. package/src/trace_engine/findings/__pycache__/recommendations.cpython-311.pyc +0 -0
  50. package/src/trace_engine/findings/__pycache__/store.cpython-311.pyc +0 -0
  51. package/src/trace_engine/findings/correlate.py +103 -0
  52. package/src/trace_engine/findings/model.py +40 -0
  53. package/src/trace_engine/findings/recommendations.py +35 -0
  54. package/src/trace_engine/findings/store.py +39 -0
  55. package/src/trace_engine/framework/__init__.py +58 -0
  56. package/src/trace_engine/framework/__pycache__/__init__.cpython-311.pyc +0 -0
  57. package/src/trace_engine/framework/__pycache__/base.cpython-311.pyc +0 -0
  58. package/src/trace_engine/framework/__pycache__/csharp.cpython-311.pyc +0 -0
  59. package/src/trace_engine/framework/__pycache__/dart.cpython-311.pyc +0 -0
  60. package/src/trace_engine/framework/__pycache__/django.cpython-311.pyc +0 -0
  61. package/src/trace_engine/framework/__pycache__/express.cpython-311.pyc +0 -0
  62. package/src/trace_engine/framework/__pycache__/fastapi.cpython-311.pyc +0 -0
  63. package/src/trace_engine/framework/__pycache__/flask.cpython-311.pyc +0 -0
  64. package/src/trace_engine/framework/__pycache__/go.cpython-311.pyc +0 -0
  65. package/src/trace_engine/framework/__pycache__/nextjs.cpython-311.pyc +0 -0
  66. package/src/trace_engine/framework/__pycache__/php.cpython-311.pyc +0 -0
  67. package/src/trace_engine/framework/__pycache__/react_router.cpython-311.pyc +0 -0
  68. package/src/trace_engine/framework/__pycache__/ruby.cpython-311.pyc +0 -0
  69. package/src/trace_engine/framework/__pycache__/rust.cpython-311.pyc +0 -0
  70. package/src/trace_engine/framework/__pycache__/springboot.cpython-311.pyc +0 -0
  71. package/src/trace_engine/framework/base.py +49 -0
  72. package/src/trace_engine/framework/csharp.py +111 -0
  73. package/src/trace_engine/framework/dart.py +152 -0
  74. package/src/trace_engine/framework/django.py +188 -0
  75. package/src/trace_engine/framework/express.py +82 -0
  76. package/src/trace_engine/framework/fastapi.py +135 -0
  77. package/src/trace_engine/framework/flask.py +108 -0
  78. package/src/trace_engine/framework/go.py +94 -0
  79. package/src/trace_engine/framework/nextjs.py +200 -0
  80. package/src/trace_engine/framework/php.py +98 -0
  81. package/src/trace_engine/framework/react_router.py +331 -0
  82. package/src/trace_engine/framework/ruby.py +69 -0
  83. package/src/trace_engine/framework/rust.py +101 -0
  84. package/src/trace_engine/framework/springboot.py +145 -0
  85. package/src/trace_engine/harness/__init__.py +19 -0
  86. package/src/trace_engine/harness/__pycache__/__init__.cpython-311.pyc +0 -0
  87. package/src/trace_engine/harness/__pycache__/benchmark.cpython-311.pyc +0 -0
  88. package/src/trace_engine/harness/__pycache__/context.cpython-311.pyc +0 -0
  89. package/src/trace_engine/harness/__pycache__/engine.cpython-311.pyc +0 -0
  90. package/src/trace_engine/harness/__pycache__/patcher.cpython-311.pyc +0 -0
  91. package/src/trace_engine/harness/__pycache__/remediators.cpython-311.pyc +0 -0
  92. package/src/trace_engine/harness/benchmark.py +55 -0
  93. package/src/trace_engine/harness/context.py +49 -0
  94. package/src/trace_engine/harness/engine.py +187 -0
  95. package/src/trace_engine/harness/patcher.py +143 -0
  96. package/src/trace_engine/harness/remediators.py +307 -0
  97. package/src/trace_engine/ingest/__init__.py +15 -0
  98. package/src/trace_engine/ingest/__pycache__/__init__.cpython-311.pyc +0 -0
  99. package/src/trace_engine/ingest/__pycache__/files.cpython-311.pyc +0 -0
  100. package/src/trace_engine/ingest/__pycache__/hashing.cpython-311.pyc +0 -0
  101. package/src/trace_engine/ingest/__pycache__/ignore.cpython-311.pyc +0 -0
  102. package/src/trace_engine/ingest/__pycache__/repository.cpython-311.pyc +0 -0
  103. package/src/trace_engine/ingest/files.py +60 -0
  104. package/src/trace_engine/ingest/hashing.py +21 -0
  105. package/src/trace_engine/ingest/ignore.py +108 -0
  106. package/src/trace_engine/ingest/repository.py +50 -0
  107. package/src/trace_engine/intelligence/__init__.py +15 -0
  108. package/src/trace_engine/intelligence/__pycache__/__init__.cpython-311.pyc +0 -0
  109. package/src/trace_engine/intelligence/__pycache__/evaluation.cpython-311.pyc +0 -0
  110. package/src/trace_engine/intelligence/__pycache__/orchestrator.cpython-311.pyc +0 -0
  111. package/src/trace_engine/intelligence/__pycache__/tracebench.cpython-311.pyc +0 -0
  112. package/src/trace_engine/intelligence/evaluation.py +828 -0
  113. package/src/trace_engine/intelligence/laya/__init__.py +19 -0
  114. package/src/trace_engine/intelligence/laya/__pycache__/__init__.cpython-311.pyc +0 -0
  115. package/src/trace_engine/intelligence/laya/__pycache__/prompts.cpython-311.pyc +0 -0
  116. package/src/trace_engine/intelligence/laya/__pycache__/router.cpython-311.pyc +0 -0
  117. package/src/trace_engine/intelligence/laya/__pycache__/schemas.cpython-311.pyc +0 -0
  118. package/src/trace_engine/intelligence/laya/__pycache__/telemetry.cpython-311.pyc +0 -0
  119. package/src/trace_engine/intelligence/laya/__pycache__/thresholds.cpython-311.pyc +0 -0
  120. package/src/trace_engine/intelligence/laya/prompts.py +67 -0
  121. package/src/trace_engine/intelligence/laya/router.py +352 -0
  122. package/src/trace_engine/intelligence/laya/schemas.py +56 -0
  123. package/src/trace_engine/intelligence/laya/telemetry.py +48 -0
  124. package/src/trace_engine/intelligence/laya/thresholds.py +12 -0
  125. package/src/trace_engine/intelligence/orchestrator.py +130 -0
  126. package/src/trace_engine/intelligence/securebert/__init__.py +6 -0
  127. package/src/trace_engine/intelligence/securebert/__pycache__/__init__.cpython-311.pyc +0 -0
  128. package/src/trace_engine/intelligence/securebert/__pycache__/cache.cpython-311.pyc +0 -0
  129. package/src/trace_engine/intelligence/securebert/__pycache__/classifier.cpython-311.pyc +0 -0
  130. package/src/trace_engine/intelligence/securebert/cache.py +37 -0
  131. package/src/trace_engine/intelligence/securebert/classifier.py +240 -0
  132. package/src/trace_engine/intelligence/tracebench.py +61 -0
  133. package/src/trace_engine/intelligence/training/__init__.py +21 -0
  134. package/src/trace_engine/intelligence/training/__pycache__/__init__.cpython-311.pyc +0 -0
  135. package/src/trace_engine/intelligence/training/__pycache__/dataset.cpython-311.pyc +0 -0
  136. package/src/trace_engine/intelligence/training/__pycache__/dataset_importers.cpython-311.pyc +0 -0
  137. package/src/trace_engine/intelligence/training/__pycache__/laya_trainer.cpython-311.pyc +0 -0
  138. package/src/trace_engine/intelligence/training/__pycache__/lora_system2.cpython-311.pyc +0 -0
  139. package/src/trace_engine/intelligence/training/__pycache__/morefixes_pipeline.cpython-311.pyc +0 -0
  140. package/src/trace_engine/intelligence/training/__pycache__/slicer.cpython-311.pyc +0 -0
  141. package/src/trace_engine/intelligence/training/__pycache__/train_all.cpython-311.pyc +0 -0
  142. package/src/trace_engine/intelligence/training/__pycache__/trainer.cpython-311.pyc +0 -0
  143. package/src/trace_engine/intelligence/training/dataset.py +320 -0
  144. package/src/trace_engine/intelligence/training/dataset_importers.py +349 -0
  145. package/src/trace_engine/intelligence/training/laya_trainer.py +678 -0
  146. package/src/trace_engine/intelligence/training/lora_system2.py +162 -0
  147. package/src/trace_engine/intelligence/training/morefixes_pipeline.py +463 -0
  148. package/src/trace_engine/intelligence/training/slicer.py +129 -0
  149. package/src/trace_engine/intelligence/training/train_all.py +1009 -0
  150. package/src/trace_engine/intelligence/training/trainer.py +321 -0
  151. package/src/trace_engine/interactive.py +623 -0
  152. package/src/trace_engine/mcp/__init__.py +6 -0
  153. package/src/trace_engine/mcp/__pycache__/__init__.cpython-311.pyc +0 -0
  154. package/src/trace_engine/mcp/__pycache__/config.cpython-311.pyc +0 -0
  155. package/src/trace_engine/mcp/__pycache__/server.cpython-311.pyc +0 -0
  156. package/src/trace_engine/mcp/config.py +42 -0
  157. package/src/trace_engine/mcp/server.py +398 -0
  158. package/src/trace_engine/output/__init__.py +28 -0
  159. package/src/trace_engine/output/__pycache__/__init__.cpython-311.pyc +0 -0
  160. package/src/trace_engine/output/__pycache__/html.cpython-311.pyc +0 -0
  161. package/src/trace_engine/output/__pycache__/markdown.cpython-311.pyc +0 -0
  162. package/src/trace_engine/output/__pycache__/sarif.cpython-311.pyc +0 -0
  163. package/src/trace_engine/output/__pycache__/terminal.cpython-311.pyc +0 -0
  164. package/src/trace_engine/output/html.py +142 -0
  165. package/src/trace_engine/output/markdown.py +65 -0
  166. package/src/trace_engine/output/sarif.py +180 -0
  167. package/src/trace_engine/output/terminal.py +537 -0
  168. package/src/trace_engine/parsing/__init__.py +26 -0
  169. package/src/trace_engine/parsing/__pycache__/__init__.cpython-311.pyc +0 -0
  170. package/src/trace_engine/parsing/__pycache__/calls.cpython-311.pyc +0 -0
  171. package/src/trace_engine/parsing/__pycache__/language.cpython-311.pyc +0 -0
  172. package/src/trace_engine/parsing/__pycache__/locations.cpython-311.pyc +0 -0
  173. package/src/trace_engine/parsing/__pycache__/parser.cpython-311.pyc +0 -0
  174. package/src/trace_engine/parsing/__pycache__/symbols.cpython-311.pyc +0 -0
  175. package/src/trace_engine/parsing/calls.py +14 -0
  176. package/src/trace_engine/parsing/language.py +24 -0
  177. package/src/trace_engine/parsing/locations.py +17 -0
  178. package/src/trace_engine/parsing/parser.py +465 -0
  179. package/src/trace_engine/parsing/symbols.py +45 -0
  180. package/src/trace_engine/plugin/__init__.py +157 -0
  181. package/src/trace_engine/plugin/__pycache__/__init__.cpython-311.pyc +0 -0
  182. package/src/trace_engine/plugin/__pycache__/evaluator.cpython-311.pyc +0 -0
  183. package/src/trace_engine/plugin/__pycache__/swebench_adapter.cpython-311.pyc +0 -0
  184. package/src/trace_engine/plugin/__pycache__/task.cpython-311.pyc +0 -0
  185. package/src/trace_engine/plugin/bundle/hooks.json +24 -0
  186. package/src/trace_engine/plugin/bundle/mcp_config.json +11 -0
  187. package/src/trace_engine/plugin/bundle/plugin.json +20 -0
  188. package/src/trace_engine/plugin/bundle/rules/security_remediation.md +40 -0
  189. package/src/trace_engine/plugin/bundle/skills/trace-security-harness/SKILL.md +118 -0
  190. package/src/trace_engine/plugin/evaluator.py +105 -0
  191. package/src/trace_engine/plugin/swebench_adapter.py +96 -0
  192. package/src/trace_engine/plugin/task.py +48 -0
  193. package/src/trace_engine/policy/__init__.py +5 -0
  194. package/src/trace_engine/policy/__pycache__/__init__.cpython-311.pyc +0 -0
  195. package/src/trace_engine/policy/__pycache__/scope.cpython-311.pyc +0 -0
  196. package/src/trace_engine/policy/scope.py +80 -0
  197. package/src/trace_engine/runtime/__init__.py +7 -0
  198. package/src/trace_engine/runtime/__pycache__/__init__.cpython-311.pyc +0 -0
  199. package/src/trace_engine/runtime/__pycache__/client.cpython-311.pyc +0 -0
  200. package/src/trace_engine/runtime/__pycache__/observations.cpython-311.pyc +0 -0
  201. package/src/trace_engine/runtime/__pycache__/target.cpython-311.pyc +0 -0
  202. package/src/trace_engine/runtime/client.py +81 -0
  203. package/src/trace_engine/runtime/observations.py +18 -0
  204. package/src/trace_engine/runtime/target.py +23 -0
  205. package/src/trace_engine/security/__init__.py +16 -0
  206. package/src/trace_engine/security/__pycache__/__init__.cpython-311.pyc +0 -0
  207. package/src/trace_engine/security/__pycache__/fusion.cpython-311.pyc +0 -0
  208. package/src/trace_engine/security/__pycache__/hypotheses.cpython-311.pyc +0 -0
  209. package/src/trace_engine/security/__pycache__/signals.cpython-311.pyc +0 -0
  210. package/src/trace_engine/security/__pycache__/timing.cpython-311.pyc +0 -0
  211. package/src/trace_engine/security/fusion.py +126 -0
  212. package/src/trace_engine/security/hypotheses.py +251 -0
  213. package/src/trace_engine/security/signals.py +28 -0
  214. package/src/trace_engine/security/timing.py +132 -0
  215. package/src/trace_engine/testpacks/__init__.py +18 -0
  216. package/src/trace_engine/testpacks/__pycache__/__init__.cpython-311.pyc +0 -0
  217. package/src/trace_engine/testpacks/__pycache__/authentication.cpython-311.pyc +0 -0
  218. package/src/trace_engine/testpacks/__pycache__/base.cpython-311.pyc +0 -0
  219. package/src/trace_engine/testpacks/__pycache__/bfla.cpython-311.pyc +0 -0
  220. package/src/trace_engine/testpacks/__pycache__/bola.cpython-311.pyc +0 -0
  221. package/src/trace_engine/testpacks/__pycache__/cors.cpython-311.pyc +0 -0
  222. package/src/trace_engine/testpacks/__pycache__/deserialization.cpython-311.pyc +0 -0
  223. package/src/trace_engine/testpacks/__pycache__/injection.cpython-311.pyc +0 -0
  224. package/src/trace_engine/testpacks/__pycache__/mass_assignment.cpython-311.pyc +0 -0
  225. package/src/trace_engine/testpacks/__pycache__/path_traversal.cpython-311.pyc +0 -0
  226. package/src/trace_engine/testpacks/__pycache__/registry.cpython-311.pyc +0 -0
  227. package/src/trace_engine/testpacks/__pycache__/ssrf.cpython-311.pyc +0 -0
  228. package/src/trace_engine/testpacks/__pycache__/ssti.cpython-311.pyc +0 -0
  229. package/src/trace_engine/testpacks/authentication.py +60 -0
  230. package/src/trace_engine/testpacks/base.py +63 -0
  231. package/src/trace_engine/testpacks/bfla.py +70 -0
  232. package/src/trace_engine/testpacks/bola.py +94 -0
  233. package/src/trace_engine/testpacks/cors.py +85 -0
  234. package/src/trace_engine/testpacks/deserialization.py +86 -0
  235. package/src/trace_engine/testpacks/injection.py +179 -0
  236. package/src/trace_engine/testpacks/mass_assignment.py +70 -0
  237. package/src/trace_engine/testpacks/path_traversal.py +117 -0
  238. package/src/trace_engine/testpacks/registry.py +44 -0
  239. package/src/trace_engine/testpacks/ssrf.py +85 -0
  240. package/src/trace_engine/testpacks/ssti.py +96 -0
  241. package/src/trace_engine/tools/__init__.py +6 -0
  242. package/src/trace_engine/tools/__pycache__/__init__.cpython-311.pyc +0 -0
  243. package/src/trace_engine/tools/__pycache__/adapters.cpython-311.pyc +0 -0
  244. package/src/trace_engine/tools/__pycache__/base.cpython-311.pyc +0 -0
  245. package/src/trace_engine/tools/__pycache__/registry.cpython-311.pyc +0 -0
  246. package/src/trace_engine/tools/adapters.py +111 -0
  247. package/src/trace_engine/tools/base.py +68 -0
  248. package/src/trace_engine/tools/registry.py +36 -0
  249. package/src/trace_engine/verify.py +156 -0
@@ -0,0 +1,70 @@
1
+ """Mass assignment test pack."""
2
+
3
+ import re
4
+ from trace_engine.testpacks.base import TestPack, TestContext, TestExecutionResult
5
+ from trace_engine.security.hypotheses import SecurityHypothesis
6
+ from trace_engine.runtime.client import ScopedHttpClient
7
+
8
+
9
+ class MassAssignmentTestPack(TestPack):
10
+ @property
11
+ def name(self) -> str:
12
+ return "mass_assignment"
13
+
14
+ @property
15
+ def description(self) -> str:
16
+ return "Validates whether object property updates accept privileged fields (e.g. is_admin, role)."
17
+
18
+ def execute(
19
+ self,
20
+ hypothesis: SecurityHypothesis,
21
+ client: ScopedHttpClient,
22
+ context: TestContext,
23
+ ) -> TestExecutionResult:
24
+ base_url = context.target_base_url.rstrip("/")
25
+ parts = hypothesis.endpoint_display.split(" ")
26
+ method = parts[0] if len(parts) > 1 else "PATCH"
27
+ raw_path = parts[1] if len(parts) > 1 else parts[0]
28
+
29
+ probe_path = re.sub(r"\{[a-zA-Z0-9_]+\}", "user-a", raw_path)
30
+ target_url = f"{base_url}{probe_path}"
31
+
32
+ token = context.active_tokens.get("user-a", "")
33
+ headers = {"Authorization": f"Bearer {token}"} if token else {}
34
+
35
+ # Inject privileged fields
36
+ payload = {
37
+ "name": "Updated User",
38
+ "is_admin": True,
39
+ "role": "admin",
40
+ }
41
+
42
+ obs = client.execute(method=method, url=target_url, headers=headers, json_body=payload)
43
+ observations = [obs]
44
+
45
+ confirmed = False
46
+ summary = "No mass assignment vulnerability detected."
47
+ steps = []
48
+
49
+ if obs.response_status in (200, 201):
50
+ body_lower = obs.response_body.lower()
51
+ if "is_admin" in body_lower or "role" in body_lower or "admin" in body_lower:
52
+ confirmed = True
53
+ summary = (
54
+ f"Mass Assignment confirmed: Endpoint {raw_path} allowed binding of privileged "
55
+ f"attribute 'is_admin'/'role' into persisted entity."
56
+ )
57
+ steps = [
58
+ f"Send {method} {target_url} with payload: {{'is_admin': True, 'role': 'admin'}}",
59
+ f"Observed HTTP {obs.response_status} with updated administrative privilege in body",
60
+ ]
61
+
62
+ return TestExecutionResult(
63
+ testpack_name=self.name,
64
+ hypothesis_id=hypothesis.id,
65
+ confirmed=confirmed,
66
+ confidence=0.91 if confirmed else 0.20,
67
+ summary=summary,
68
+ observations=observations,
69
+ reproduction_steps=steps,
70
+ )
@@ -0,0 +1,117 @@
1
+ """Path Traversal and Arbitrary File Access test pack (CWE-22 / CWE-73)."""
2
+
3
+ import re
4
+ from typing import List, Dict, Any
5
+ from trace_engine.testpacks.base import TestPack, TestContext, TestExecutionResult
6
+ from trace_engine.security.hypotheses import SecurityHypothesis
7
+ from trace_engine.runtime.client import ScopedHttpClient
8
+ from trace_engine.runtime.observations import RuntimeObservation
9
+
10
+ TRAVERSAL_PAYLOADS = [
11
+ "../../../../etc/passwd",
12
+ "../../../../../../../../etc/passwd",
13
+ "..\\..\\..\\..\\Windows\\win.ini",
14
+ "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
15
+ "../../../../etc/passwd%00.png",
16
+ ]
17
+
18
+ SENSITIVE_SIGNATURES = [
19
+ "root:x:",
20
+ "daemon:x:",
21
+ "[extensions]",
22
+ "[fonts]",
23
+ "[files]",
24
+ "for 16-bit app support",
25
+ ]
26
+
27
+ FS_ERROR_MARKERS = [
28
+ "filenotfoundexception",
29
+ "no such file or directory",
30
+ "java.io.filenotfound",
31
+ "enoent",
32
+ "open_basedir restriction",
33
+ ]
34
+
35
+
36
+ class PathTraversalTestPack(TestPack):
37
+ @property
38
+ def name(self) -> str:
39
+ return "path_traversal"
40
+
41
+ @property
42
+ def description(self) -> str:
43
+ return "Validates whether file/path parameters allow escaping directory boundaries to access arbitrary files."
44
+
45
+ def execute(
46
+ self,
47
+ hypothesis: SecurityHypothesis,
48
+ client: ScopedHttpClient,
49
+ context: TestContext,
50
+ ) -> TestExecutionResult:
51
+ base_url = context.target_base_url.rstrip("/")
52
+ parts = hypothesis.endpoint_display.split(" ")
53
+ method = parts[0] if len(parts) > 1 else "GET"
54
+ raw_path = parts[1] if len(parts) > 1 else parts[0]
55
+
56
+ token = context.active_tokens.get("user-a", "")
57
+ headers = {"Authorization": f"Bearer {token}"} if token else {}
58
+
59
+ observations: List[RuntimeObservation] = []
60
+ confirmed = False
61
+ summary = "No path traversal vulnerability detected."
62
+ steps: List[str] = []
63
+
64
+ for payload in TRAVERSAL_PAYLOADS:
65
+ # 1. Path parameter substitution if endpoint contains {id} or {path} or {file}
66
+ if "{" in raw_path:
67
+ probed_path = re.sub(r"\{[a-zA-Z0-9_]+\}", payload, raw_path)
68
+ target_url = f"{base_url}{probed_path}"
69
+ obs = client.execute(method=method, url=target_url, headers=headers)
70
+ observations.append(obs)
71
+ else:
72
+ # 2. Query parameter probe
73
+ target_url = f"{base_url}{raw_path}?file={payload}&path={payload}"
74
+ obs = client.execute(method=method, url=target_url, headers=headers)
75
+ observations.append(obs)
76
+
77
+ if obs.response_status in (200, 500):
78
+ body_lower = obs.response_body.lower()
79
+ for sig in SENSITIVE_SIGNATURES:
80
+ if sig.lower() in body_lower:
81
+ confirmed = True
82
+ summary = (
83
+ f"Path Traversal (CWE-22) CONFIRMED: Endpoint {raw_path} returned contents of "
84
+ f"system file when supplied with traversal payload '{payload}'."
85
+ )
86
+ steps = [
87
+ f"Send {method} request to {target_url} with traversal sequence '{payload}'",
88
+ f"Observe leaked file system contents containing '{sig}'",
89
+ ]
90
+ break
91
+
92
+ if not confirmed and obs.response_status == 500:
93
+ for marker in FS_ERROR_MARKERS:
94
+ if marker in body_lower:
95
+ confirmed = True
96
+ summary = (
97
+ f"Path Traversal Indicator: File system path error disclosure '{marker}' "
98
+ f"triggered on {raw_path} using traversal payload '{payload}'."
99
+ )
100
+ steps = [
101
+ f"Send {method} {target_url} with traversal probe '{payload}'",
102
+ f"Observe underlying file system path disclosure error '{marker}'",
103
+ ]
104
+ break
105
+
106
+ if confirmed:
107
+ break
108
+
109
+ return TestExecutionResult(
110
+ testpack_name=self.name,
111
+ hypothesis_id=hypothesis.id,
112
+ confirmed=confirmed,
113
+ confidence=0.95 if confirmed else 0.1,
114
+ summary=summary,
115
+ observations=observations,
116
+ reproduction_steps=steps,
117
+ )
@@ -0,0 +1,44 @@
1
+ """Test pack registry for looking up available security packs."""
2
+
3
+ from typing import Dict, List, Optional
4
+ from trace_engine.testpacks.base import TestPack
5
+ from trace_engine.testpacks.bola import BolaTestPack
6
+ from trace_engine.testpacks.bfla import BflaTestPack
7
+ from trace_engine.testpacks.authentication import AuthenticationTestPack
8
+ from trace_engine.testpacks.ssrf import SsrfTestPack
9
+ from trace_engine.testpacks.mass_assignment import MassAssignmentTestPack
10
+ from trace_engine.testpacks.injection import InjectionTestPack
11
+ from trace_engine.testpacks.path_traversal import PathTraversalTestPack
12
+ from trace_engine.testpacks.ssti import SstiTestPack
13
+ from trace_engine.testpacks.cors import CorsTestPack
14
+ from trace_engine.testpacks.deserialization import DeserializationTestPack
15
+
16
+
17
+ class TestPackRegistry:
18
+ """Central registry of registered test packs."""
19
+
20
+ def __init__(self):
21
+ self._packs: Dict[str, TestPack] = {
22
+ "bola": BolaTestPack(),
23
+ "bfla": BflaTestPack(),
24
+ "authentication": AuthenticationTestPack(),
25
+ "ssrf": SsrfTestPack(),
26
+ "mass_assignment": MassAssignmentTestPack(),
27
+ "injection": InjectionTestPack(),
28
+ "path_traversal": PathTraversalTestPack(),
29
+ "ssti": SstiTestPack(),
30
+ "cors": CorsTestPack(),
31
+ "deserialization": DeserializationTestPack(),
32
+ }
33
+
34
+ def get(self, name: str) -> Optional[TestPack]:
35
+ return self._packs.get(name.lower())
36
+
37
+ def list_all(self) -> List[TestPack]:
38
+ return list(self._packs.values())
39
+
40
+ def register(self, pack: TestPack) -> None:
41
+ self._packs[pack.name.lower()] = pack
42
+
43
+
44
+ default_registry = TestPackRegistry()
@@ -0,0 +1,85 @@
1
+ """SSRF (Server-Side Request Forgery) test pack with polymorphic payload mutations."""
2
+
3
+ import re
4
+ from typing import List
5
+ from trace_engine.testpacks.base import TestPack, TestContext, TestExecutionResult
6
+ from trace_engine.security.hypotheses import SecurityHypothesis
7
+ from trace_engine.runtime.client import ScopedHttpClient
8
+ from trace_engine.runtime.observations import RuntimeObservation
9
+
10
+
11
+ class SsrfTestPack(TestPack):
12
+ @property
13
+ def name(self) -> str:
14
+ return "ssrf"
15
+
16
+ @property
17
+ def description(self) -> str:
18
+ return "Validates whether server endpoints can be coerced into arbitrary internal network fetches across mutated IP representations."
19
+
20
+ def execute(
21
+ self,
22
+ hypothesis: SecurityHypothesis,
23
+ client: ScopedHttpClient,
24
+ context: TestContext,
25
+ ) -> TestExecutionResult:
26
+ base_url = context.target_base_url.rstrip("/")
27
+ parts = hypothesis.endpoint_display.split(" ")
28
+ method = parts[0] if len(parts) > 1 else "POST"
29
+ raw_path = parts[1] if len(parts) > 1 else parts[0]
30
+
31
+ clean_path = re.sub(r"\{[a-zA-Z0-9_]+\}", "1", raw_path)
32
+ target_url = f"{base_url}{clean_path}"
33
+ token = context.active_tokens.get("user-a", "")
34
+ headers = {"Authorization": f"Bearer {token}"} if token else {}
35
+
36
+ # Extract port from base_url for loopback mutations
37
+ port_match = re.search(r":(\d+)", base_url)
38
+ port = port_match.group(1) if port_match else "80"
39
+
40
+ # Polymorphic internal probes
41
+ candidate_probes = [
42
+ # Standard internal endpoint
43
+ f"{base_url}/api/products",
44
+ # Loopback aliases & mutations
45
+ f"http://127.0.0.1:{port}/api/products",
46
+ f"http://localhost:{port}/api/products",
47
+ f"http://127.0.0.1:{port}/health",
48
+ ]
49
+
50
+ observations: List[RuntimeObservation] = []
51
+ confirmed = False
52
+ summary = "No SSRF behavior observed."
53
+ steps: List[str] = []
54
+
55
+ for probe_url in candidate_probes:
56
+ payload = {"url": probe_url, "target": probe_url, "callbackUrl": probe_url, "webhookUrl": probe_url}
57
+ try:
58
+ obs = client.execute(method=method, url=target_url, headers=headers, json_body=payload)
59
+ observations.append(obs)
60
+
61
+ if obs.response_status == 200:
62
+ body_lower = obs.response_body.lower()
63
+ if any(k in body_lower for k in ("status", "data", "product", "content", "health", "ok", "items")):
64
+ confirmed = True
65
+ summary = (
66
+ f"SSRF (CWE-918) CONFIRMED: Endpoint {raw_path} fetched internal address "
67
+ f"{probe_url} and returned fetched data."
68
+ )
69
+ steps = [
70
+ f"Send {method} {target_url} with JSON payload containing URL: {probe_url}",
71
+ f"Observe internal service response echoed back in response body",
72
+ ]
73
+ break
74
+ except Exception:
75
+ continue
76
+
77
+ return TestExecutionResult(
78
+ testpack_name=self.name,
79
+ hypothesis_id=hypothesis.id,
80
+ confirmed=confirmed,
81
+ confidence=0.96 if confirmed else 0.25,
82
+ summary=summary,
83
+ observations=observations,
84
+ reproduction_steps=steps,
85
+ )
@@ -0,0 +1,96 @@
1
+ """Server-Side Template Injection (SSTI) test pack (CWE-1336 / CWE-94)."""
2
+
3
+ import re
4
+ from typing import List, Dict, Any
5
+ from trace_engine.testpacks.base import TestPack, TestContext, TestExecutionResult
6
+ from trace_engine.security.hypotheses import SecurityHypothesis
7
+ from trace_engine.runtime.client import ScopedHttpClient
8
+ from trace_engine.runtime.observations import RuntimeObservation
9
+
10
+ SSTI_PROBES = [
11
+ ("{{7*7}}", "49"),
12
+ ("{{7*'7'}}", "7777777"),
13
+ ("${7*7}", "49"),
14
+ ("#{7*7}", "49"),
15
+ ]
16
+
17
+
18
+ class SstiTestPack(TestPack):
19
+ @property
20
+ def name(self) -> str:
21
+ return "ssti"
22
+
23
+ @property
24
+ def description(self) -> str:
25
+ return "Validates whether template engines dynamically evaluate user-supplied mathematical and script expressions."
26
+
27
+ def execute(
28
+ self,
29
+ hypothesis: SecurityHypothesis,
30
+ client: ScopedHttpClient,
31
+ context: TestContext,
32
+ ) -> TestExecutionResult:
33
+ base_url = context.target_base_url.rstrip("/")
34
+ parts = hypothesis.endpoint_display.split(" ")
35
+ method = parts[0] if len(parts) > 1 else "POST"
36
+ raw_path = parts[1] if len(parts) > 1 else parts[0]
37
+
38
+ token = context.active_tokens.get("user-a", "")
39
+ headers = {"Authorization": f"Bearer {token}"} if token else {}
40
+
41
+ observations: List[RuntimeObservation] = []
42
+ confirmed = False
43
+ summary = "No Server-Side Template Injection behavior observed."
44
+ steps: List[str] = []
45
+
46
+ for payload, expected in SSTI_PROBES:
47
+ # 1. Query parameter probe
48
+ target_url = f"{base_url}{raw_path}?template={payload}&name={payload}&msg={payload}"
49
+ obs = client.execute(method="GET", url=target_url, headers=headers)
50
+ observations.append(obs)
51
+
52
+ if obs.response_status == 200:
53
+ if expected in obs.response_body and payload not in obs.response_body:
54
+ confirmed = True
55
+ summary = (
56
+ f"SSTI (CWE-1336) CONFIRMED: Template payload '{payload}' dynamically executed "
57
+ f"and evaluated to '{expected}' in response on {raw_path}."
58
+ )
59
+ steps = [
60
+ f"Send GET {target_url} with template expression '{payload}'",
61
+ f"Observe mathematical execution result '{expected}' evaluated in response body",
62
+ ]
63
+ break
64
+
65
+ # 2. JSON Body probe for mutating methods
66
+ if method in ("POST", "PUT", "PATCH"):
67
+ post_url = f"{base_url}{raw_path}"
68
+ body_payload = {"template": payload, "name": payload, "format": payload}
69
+ post_obs = client.execute(method=method, url=post_url, headers=headers, json_body=body_payload)
70
+ observations.append(post_obs)
71
+
72
+ if post_obs.response_status in (200, 201):
73
+ if expected in post_obs.response_body and payload not in post_obs.response_body:
74
+ confirmed = True
75
+ summary = (
76
+ f"SSTI (CWE-1336) CONFIRMED: JSON payload '{payload}' executed in template engine "
77
+ f"yielding '{expected}' on {raw_path}."
78
+ )
79
+ steps = [
80
+ f"Send {method} {post_url} with JSON payload containing '{payload}'",
81
+ f"Observe template evaluation output '{expected}'",
82
+ ]
83
+ break
84
+
85
+ if confirmed:
86
+ break
87
+
88
+ return TestExecutionResult(
89
+ testpack_name=self.name,
90
+ hypothesis_id=hypothesis.id,
91
+ confirmed=confirmed,
92
+ confidence=0.95 if confirmed else 0.1,
93
+ summary=summary,
94
+ observations=observations,
95
+ reproduction_steps=steps,
96
+ )
@@ -0,0 +1,6 @@
1
+ """External security tools adapter module."""
2
+
3
+ from trace_engine.tools.base import ToolAdapter, ToolInfo
4
+ from trace_engine.tools.registry import ToolRegistry, default_tool_registry
5
+
6
+ __all__ = ["ToolAdapter", "ToolInfo", "ToolRegistry", "default_tool_registry"]
@@ -0,0 +1,111 @@
1
+ """Specific adapters for open-source security tools."""
2
+
3
+ from trace_engine.tools.base import ToolAdapter
4
+
5
+
6
+ class SchemathesisAdapter(ToolAdapter):
7
+ @property
8
+ def name(self) -> str:
9
+ return "Schemathesis"
10
+
11
+ @property
12
+ def command(self) -> str:
13
+ return "schemathesis"
14
+
15
+ @property
16
+ def description(self) -> str:
17
+ return "Specification-based API property fuzzing and contract validation."
18
+
19
+ @property
20
+ def category(self) -> str:
21
+ return "API Fuzzing"
22
+
23
+
24
+ class NucleiAdapter(ToolAdapter):
25
+ @property
26
+ def name(self) -> str:
27
+ return "Nuclei"
28
+
29
+ @property
30
+ def command(self) -> str:
31
+ return "nuclei"
32
+
33
+ @property
34
+ def description(self) -> str:
35
+ return "Fast and customizable vulnerability scanner based on simple YAML DSL."
36
+
37
+ @property
38
+ def category(self) -> str:
39
+ return "DAST / Templates"
40
+
41
+
42
+ class ZapAdapter(ToolAdapter):
43
+ @property
44
+ def name(self) -> str:
45
+ return "OWASP ZAP"
46
+
47
+ @property
48
+ def command(self) -> str:
49
+ return "zap.sh"
50
+
51
+ @property
52
+ def description(self) -> str:
53
+ return "Dynamic application security scanner for web applications."
54
+
55
+ @property
56
+ def category(self) -> str:
57
+ return "DAST Scanner"
58
+
59
+
60
+ class TruffleHogAdapter(ToolAdapter):
61
+ @property
62
+ def name(self) -> str:
63
+ return "TruffleHog"
64
+
65
+ @property
66
+ def command(self) -> str:
67
+ return "trufflehog"
68
+
69
+ @property
70
+ def description(self) -> str:
71
+ return "High-accuracy credentials and secrets scanner in git repositories."
72
+
73
+ @property
74
+ def category(self) -> str:
75
+ return "Secret Detection"
76
+
77
+
78
+ class SqlmapAdapter(ToolAdapter):
79
+ @property
80
+ def name(self) -> str:
81
+ return "sqlmap"
82
+
83
+ @property
84
+ def command(self) -> str:
85
+ return "sqlmap"
86
+
87
+ @property
88
+ def description(self) -> str:
89
+ return "Automatic SQL injection and database takeover tool."
90
+
91
+ @property
92
+ def category(self) -> str:
93
+ return "Injection Testing"
94
+
95
+
96
+ class FfufAdapter(ToolAdapter):
97
+ @property
98
+ def name(self) -> str:
99
+ return "ffuf"
100
+
101
+ @property
102
+ def command(self) -> str:
103
+ return "ffuf"
104
+
105
+ @property
106
+ def description(self) -> str:
107
+ return "Fast web fuzzer for parameter and endpoint discovery."
108
+
109
+ @property
110
+ def category(self) -> str:
111
+ return "Web Fuzzing"
@@ -0,0 +1,68 @@
1
+ """Base interfaces for external tool adapters."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ import shutil
5
+ import subprocess
6
+ from typing import Optional, Dict, Any, List
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class ToolInfo(BaseModel):
11
+ name: str
12
+ command: str
13
+ description: str
14
+ installed: bool
15
+ version: Optional[str] = None
16
+ category: str
17
+
18
+
19
+ class ToolAdapter(ABC):
20
+ """Abstract adapter for external security tools."""
21
+
22
+ @property
23
+ @abstractmethod
24
+ def name(self) -> str:
25
+ pass
26
+
27
+ @property
28
+ @abstractmethod
29
+ def command(self) -> str:
30
+ pass
31
+
32
+ @property
33
+ @abstractmethod
34
+ def description(self) -> str:
35
+ pass
36
+
37
+ @property
38
+ @abstractmethod
39
+ def category(self) -> str:
40
+ pass
41
+
42
+ def is_installed(self) -> bool:
43
+ return shutil.which(self.command) is not None
44
+
45
+ def get_version(self) -> Optional[str]:
46
+ if not self.is_installed():
47
+ return None
48
+ try:
49
+ res = subprocess.run(
50
+ [self.command, "--version"],
51
+ capture_output=True,
52
+ text=True,
53
+ timeout=3,
54
+ )
55
+ out = (res.stdout or res.stderr).strip().splitlines()
56
+ return out[0] if out else "installed"
57
+ except Exception:
58
+ return "installed"
59
+
60
+ def get_info(self) -> ToolInfo:
61
+ return ToolInfo(
62
+ name=self.name,
63
+ command=self.command,
64
+ description=self.description,
65
+ installed=self.is_installed(),
66
+ version=self.get_version(),
67
+ category=self.category,
68
+ )
@@ -0,0 +1,36 @@
1
+ """Registry of external security tool adapters."""
2
+
3
+ from typing import List, Dict, Optional
4
+ from trace_engine.tools.base import ToolAdapter, ToolInfo
5
+ from trace_engine.tools.adapters import (
6
+ SchemathesisAdapter,
7
+ NucleiAdapter,
8
+ ZapAdapter,
9
+ TruffleHogAdapter,
10
+ SqlmapAdapter,
11
+ FfufAdapter,
12
+ )
13
+
14
+
15
+ class ToolRegistry:
16
+ def __init__(self):
17
+ self._tools: Dict[str, ToolAdapter] = {
18
+ "schemathesis": SchemathesisAdapter(),
19
+ "nuclei": NucleiAdapter(),
20
+ "zap": ZapAdapter(),
21
+ "trufflehog": TruffleHogAdapter(),
22
+ "sqlmap": SqlmapAdapter(),
23
+ "ffuf": FfufAdapter(),
24
+ }
25
+
26
+ def list_all(self) -> List[ToolAdapter]:
27
+ return list(self._tools.values())
28
+
29
+ def get(self, name: str) -> Optional[ToolAdapter]:
30
+ return self._tools.get(name.lower())
31
+
32
+ def get_all_info(self) -> List[ToolInfo]:
33
+ return [t.get_info() for t in self._tools.values()]
34
+
35
+
36
+ default_tool_registry = ToolRegistry()