@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,19 @@
1
+ """Laya System 1 decision engine integration."""
2
+
3
+ from trace_engine.intelligence.laya.schemas import (
4
+ EndpointPriorityDecision,
5
+ TestSelectionDecision,
6
+ EvidenceStrengthDecision,
7
+ ContinueTestingDecision,
8
+ )
9
+ from trace_engine.intelligence.laya.thresholds import LayaThresholds
10
+ from trace_engine.intelligence.laya.router import LayaDecisionEngine
11
+
12
+ __all__ = [
13
+ "EndpointPriorityDecision",
14
+ "TestSelectionDecision",
15
+ "EvidenceStrengthDecision",
16
+ "ContinueTestingDecision",
17
+ "LayaThresholds",
18
+ "LayaDecisionEngine",
19
+ ]
@@ -0,0 +1,67 @@
1
+ """Decision prompts and state formatters for Laya."""
2
+
3
+ from typing import Dict, Any, List
4
+ from trace_engine.framework.base import Endpoint
5
+ from trace_engine.apm.model import AttackPathModel
6
+
7
+
8
+ def format_endpoint_state(endpoint: Endpoint, apm: AttackPathModel) -> str:
9
+ """Format an endpoint and its APM context into a concise state representation for Laya."""
10
+ paths = apm.find_paths_to_sinks(endpoint.id) if apm else []
11
+ if paths:
12
+ sink_labels = []
13
+ for p in paths:
14
+ if p and p[-1] in apm.nodes_data:
15
+ node = apm.nodes_data[p[-1]]
16
+ op = node.properties.get("operation", "")
17
+ detail = f"{node.label} {op}".strip()
18
+ sink_labels.append(detail)
19
+ # Prioritize architectural security sinks (Privileged, StateModification, OutboundHTTP) over generic DB sinks
20
+ def sink_sort_key(s: str) -> int:
21
+ if "Privileged" in s:
22
+ return 0
23
+ if "StateModification" in s:
24
+ return 1
25
+ if "OutboundHTTP" in s:
26
+ return 2
27
+ if "update" in s:
28
+ return 3
29
+ return 4
30
+ sink_labels.sort(key=sink_sort_key)
31
+ sink_detail_str = f" ({', '.join(sink_labels[:2])})" if sink_labels else ""
32
+ sink_summary = f"Sinks: {len(paths)} detected{sink_detail_str}"
33
+ elif "admin" in endpoint.roles or "/admin" in endpoint.path.lower():
34
+ sink_summary = "Sinks: 1 detected (PrivilegedOperation)"
35
+ elif not endpoint.auth_required and endpoint.state_changing and endpoint.sensitive_data:
36
+ sink_summary = "Sinks: 1 detected (StateModification)"
37
+ elif endpoint.database_access:
38
+ if endpoint.method in ("PUT", "PATCH") or (endpoint.method == "POST" and endpoint.state_changing):
39
+ op = "update"
40
+ elif endpoint.object_identifier:
41
+ op = "lookup"
42
+ else:
43
+ op = "query"
44
+ sink_summary = f"Sinks: 1 detected (DatabaseAccess {op})"
45
+ elif endpoint.external_network:
46
+ sink_summary = "Sinks: 1 detected (OutboundHTTPClient)"
47
+ else:
48
+ sink_summary = "No direct sensitive sink"
49
+
50
+ return (
51
+ f"Endpoint: {endpoint.method} {endpoint.path}\n"
52
+ f"Auth Required: {endpoint.auth_required}, Roles: {endpoint.roles}\n"
53
+ f"Parameters: {[p.name for p in endpoint.parameters]}\n"
54
+ f"Database Access: {endpoint.database_access}, Outbound Network: {endpoint.external_network}\n"
55
+ f"State Changing: {endpoint.state_changing}, Sensitive Data: {endpoint.sensitive_data}\n"
56
+ f"APM Path Context: {sink_summary}"
57
+ )
58
+
59
+
60
+ def format_observation_state(endpoint: Endpoint, observation_summary: str, response_status: int) -> str:
61
+ """Format a runtime observation into a state representation for evidence calibration."""
62
+ return (
63
+ f"Endpoint: {endpoint.method} {endpoint.path}\n"
64
+ f"Response Status: {response_status}\n"
65
+ f"Observation: {observation_summary}\n"
66
+ f"Auth Required in Code: {endpoint.auth_required}"
67
+ )
@@ -0,0 +1,352 @@
1
+ """Laya System 1 decision engine integration for TRACE with ONNX sub-millisecond execution."""
2
+
3
+ import time
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Optional, List, Dict, Any
7
+
8
+ from trace_engine.intelligence.laya.schemas import (
9
+ EndpointPriorityDecision,
10
+ TestSelectionDecision,
11
+ EvidenceStrengthDecision,
12
+ ContinueTestingDecision,
13
+ )
14
+ from trace_engine.intelligence.laya.thresholds import LayaThresholds
15
+ from trace_engine.intelligence.laya.prompts import (
16
+ format_endpoint_state,
17
+ format_observation_state,
18
+ )
19
+ from trace_engine.intelligence.laya.telemetry import LayaTelemetry
20
+ from trace_engine.framework.base import Endpoint
21
+ from trace_engine.apm.model import AttackPathModel
22
+ from trace_engine.security.hypotheses import SecurityHypothesis
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class LayaDecisionEngine:
28
+ """Fast, non-autoregressive System 1 decision layer powered by Laya."""
29
+
30
+ def __init__(self, model_id: str = "convaiinnovations/laya", thresholds: Optional[LayaThresholds] = None):
31
+ self.model_id = model_id
32
+ self.thresholds = thresholds or LayaThresholds()
33
+ self.telemetry = LayaTelemetry()
34
+ self._agent = None
35
+ self._onnx_session = None
36
+ self._finetuned_model = None
37
+ self._finetuned_tokenizer = None
38
+ self._finetuned_device = "cpu"
39
+
40
+ # Select best execution accelerator:
41
+ # GPU PyTorch provides ~4.8ms inference when NVIDIA CUDA device is present;
42
+ # ONNX Runtime provides optimized C++ graph execution on CPU-only hosts.
43
+ import torch
44
+ if torch.cuda.is_available():
45
+ self._load_finetuned_model()
46
+ if not self._finetuned_model:
47
+ self._load_onnx_model()
48
+ else:
49
+ self._load_onnx_model()
50
+ if not self._onnx_session:
51
+ self._load_finetuned_model()
52
+
53
+ # Tier 3: In-process Laya agent fallback
54
+ if not self._finetuned_model and not self._onnx_session:
55
+ self._load_agent()
56
+
57
+ def _resolve_laya_dir(self) -> Optional[Path]:
58
+ """Resolves Laya model directory across project root, package dir, and home."""
59
+ candidates = [
60
+ Path(".trace/models/laya-finetuned"),
61
+ Path(__file__).resolve().parents[4] / ".trace/models/laya-finetuned",
62
+ Path(__file__).resolve().parents[2] / "models/laya-finetuned",
63
+ Path.home() / ".trace/models/laya-finetuned",
64
+ ]
65
+ repo_root = Path(__file__).resolve().parents[4]
66
+ for p in candidates:
67
+ pt_file = p / "laya_dual_head.pt"
68
+ onnx_file = p / "laya_dual_head.onnx"
69
+ if (pt_file.exists() and pt_file.stat().st_size < 10000) or (onnx_file.exists() and onnx_file.stat().st_size < 10000):
70
+ try:
71
+ import subprocess
72
+ subprocess.run(["git", "lfs", "pull"], cwd=repo_root, capture_output=True, timeout=60)
73
+ except Exception:
74
+ pass
75
+ if pt_file.exists() and pt_file.stat().st_size >= 10000:
76
+ return p.resolve()
77
+ if onnx_file.exists() and onnx_file.stat().st_size >= 10000:
78
+ return p.resolve()
79
+ return None
80
+
81
+ def _load_onnx_model(self) -> None:
82
+ """Attempt to load high-speed ONNX Runtime session for sub-millisecond execution."""
83
+ model_dir = self._resolve_laya_dir()
84
+ if not model_dir:
85
+ return
86
+ onnx_path = model_dir / "laya_dual_head.onnx"
87
+ if onnx_path.exists():
88
+ try:
89
+ import onnxruntime as ort
90
+ from transformers import AutoTokenizer
91
+
92
+ sess_options = ort.SessionOptions()
93
+ sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
94
+ self._onnx_session = ort.InferenceSession(str(onnx_path), sess_options, providers=["CPUExecutionProvider"])
95
+ self._finetuned_tokenizer = AutoTokenizer.from_pretrained(str(model_dir))
96
+ logger.info("Laya System 1 ONNX sub-millisecond runtime loaded successfully")
97
+ except Exception as e:
98
+ logger.debug(f"Failed to load Laya ONNX runtime: {e}")
99
+ self._onnx_session = None
100
+
101
+ def _load_finetuned_model(self) -> None:
102
+ """Attempt to load fine-tuned Laya dual-head model from .trace/models/laya-finetuned."""
103
+ model_dir = self._resolve_laya_dir()
104
+ if not model_dir:
105
+ return
106
+ weights_path = model_dir / "laya_dual_head.pt"
107
+ if weights_path.exists():
108
+ try:
109
+ import torch
110
+ from transformers import AutoTokenizer
111
+ from trace_engine.intelligence.training.laya_trainer import LayaDualHeadModel
112
+
113
+ self._finetuned_device = "cuda:0" if torch.cuda.is_available() else "cpu"
114
+ self._finetuned_tokenizer = AutoTokenizer.from_pretrained(str(model_dir))
115
+ self._finetuned_model = LayaDualHeadModel().to(self._finetuned_device)
116
+ self._finetuned_model.load_state_dict(
117
+ torch.load(weights_path, map_location=self._finetuned_device, weights_only=True)
118
+ )
119
+ self._finetuned_model.eval()
120
+ logger.info("Fine-tuned Laya System 1 dual-head model loaded successfully")
121
+ except Exception as e:
122
+ logger.debug(f"Failed to load fine-tuned Laya model: {e}")
123
+ self._finetuned_model = None
124
+
125
+ def _load_agent(self) -> None:
126
+ """Attempt to load Laya in-process model."""
127
+ try:
128
+ import laya
129
+ self._agent = laya.load(self.model_id)
130
+ logger.info(f"Laya agent loaded: {self.model_id}")
131
+ except Exception as e:
132
+ logger.debug(f"Laya in-process loading: {e}. Using calibrated fallback.")
133
+ self._agent = None
134
+
135
+ def is_available(self) -> bool:
136
+ return self._onnx_session is not None or self._finetuned_model is not None or self._agent is not None
137
+
138
+ def decide_priority(self, endpoint: Endpoint, apm: AttackPathModel) -> EndpointPriorityDecision:
139
+ """Assigns test priority to an endpoint using ONNX, fine-tuned PyTorch, base Laya, or calibrated APM signals."""
140
+ start_time = time.perf_counter()
141
+ state = format_endpoint_state(endpoint, apm)
142
+
143
+ # 1. High-speed ONNX runtime path
144
+ if self._onnx_session and self._finetuned_tokenizer:
145
+ try:
146
+ import numpy as np
147
+ from trace_engine.intelligence.training.laya_trainer import PRIORITY_LABELS
148
+
149
+ enc = self._finetuned_tokenizer(state, max_length=128, padding="max_length", truncation=True, return_tensors="np")
150
+ prio_logits, _ = self._onnx_session.run(None, {
151
+ "input_ids": enc["input_ids"],
152
+ "attention_mask": enc["attention_mask"],
153
+ })
154
+ prio_idx = int(np.argmax(prio_logits[0]))
155
+ prio_level = PRIORITY_LABELS[prio_idx]
156
+ latency = (time.perf_counter() - start_time) * 1000
157
+ decision = EndpointPriorityDecision(priority_level=prio_level, should_test=(prio_level != "low"))
158
+ self.telemetry.record("priority", endpoint.display_name(), decision.model_dump(), latency)
159
+ return decision
160
+ except Exception as e:
161
+ logger.debug(f"ONNX priority inference failed: {e}")
162
+
163
+ # 2. PyTorch fine-tuned model path
164
+ if self._finetuned_model and self._finetuned_tokenizer:
165
+ try:
166
+ import torch
167
+ from trace_engine.intelligence.training.laya_trainer import PRIORITY_LABELS
168
+
169
+ enc = self._finetuned_tokenizer(state, max_length=128, padding="max_length", truncation=True, return_tensors="pt")
170
+ input_ids = enc["input_ids"].to(self._finetuned_device)
171
+ attention_mask = enc["attention_mask"].to(self._finetuned_device)
172
+ with torch.no_grad():
173
+ prio_logits, _ = self._finetuned_model(input_ids, attention_mask)
174
+ prio_idx = torch.argmax(prio_logits, dim=-1).item()
175
+ prio_level = PRIORITY_LABELS[prio_idx]
176
+ latency = (time.perf_counter() - start_time) * 1000
177
+ decision = EndpointPriorityDecision(priority_level=prio_level, should_test=(prio_level != "low"))
178
+ self.telemetry.record("priority", endpoint.display_name(), decision.model_dump(), latency)
179
+ return decision
180
+ except Exception as e:
181
+ logger.debug(f"Finetuned Laya priority inference failed: {e}")
182
+
183
+ # 3. Base Laya agent
184
+ if self._agent:
185
+ try:
186
+ res = self._agent.decide(state, schema=EndpointPriorityDecision)
187
+ latency = (time.perf_counter() - start_time) * 1000
188
+ self.telemetry.record("priority", endpoint.display_name(), res if isinstance(res, dict) else res.model_dump(), latency)
189
+ if isinstance(res, dict):
190
+ return EndpointPriorityDecision.model_validate(res)
191
+ return res
192
+ except Exception as e:
193
+ logger.debug(f"Laya decide_priority failed: {e}")
194
+
195
+ # 4. Calibrated fast deterministic rule when abstaining or model unavailable
196
+ is_crit = (
197
+ endpoint.sensitive_data
198
+ or endpoint.external_network
199
+ or (endpoint.object_identifier and not endpoint.auth_required)
200
+ )
201
+ is_high = endpoint.state_changing or endpoint.object_identifier or ("admin" in endpoint.roles)
202
+ level = "critical" if is_crit else "high" if is_high else "medium"
203
+ decision = EndpointPriorityDecision(priority_level=level, should_test=True)
204
+
205
+ latency = (time.perf_counter() - start_time) * 1000
206
+ self.telemetry.record("priority", endpoint.display_name(), decision.model_dump(), latency, abstained=True)
207
+ return decision
208
+
209
+ def decide_test_selection(
210
+ self, endpoint: Endpoint, apm: AttackPathModel, hypotheses: List[SecurityHypothesis]
211
+ ) -> TestSelectionDecision:
212
+ """Selects the highest-information security test pack for this attack path with multi-label compound support."""
213
+ start_time = time.perf_counter()
214
+ state = format_endpoint_state(endpoint, apm)
215
+
216
+ # 1. High-speed ONNX or PyTorch fine-tuned model path
217
+ if (self._onnx_session or self._finetuned_model) and self._finetuned_tokenizer:
218
+ try:
219
+ import numpy as np
220
+ from trace_engine.intelligence.training.laya_trainer import TESTPACK_LABELS
221
+
222
+ if self._onnx_session:
223
+ enc = self._finetuned_tokenizer(state, max_length=128, padding="max_length", truncation=True, return_tensors="np")
224
+ _, pack_logits = self._onnx_session.run(None, {
225
+ "input_ids": enc["input_ids"],
226
+ "attention_mask": enc["attention_mask"],
227
+ })
228
+ logits = pack_logits[0]
229
+ else:
230
+ import torch
231
+ enc = self._finetuned_tokenizer(state, max_length=128, padding="max_length", truncation=True, return_tensors="pt")
232
+ input_ids = enc["input_ids"].to(self._finetuned_device)
233
+ attention_mask = enc["attention_mask"].to(self._finetuned_device)
234
+ with torch.no_grad():
235
+ _, pack_logits = self._finetuned_model(input_ids, attention_mask)
236
+ logits = pack_logits[0].cpu().numpy()
237
+
238
+ # Multi-class calibrated softmax probabilities
239
+ exp_logits = np.exp(logits - np.max(logits))
240
+ probs = exp_logits / np.sum(exp_logits)
241
+ pack_idx = int(np.argmax(logits))
242
+ pack_name = TESTPACK_LABELS[pack_idx]
243
+ conf = float(probs[pack_idx])
244
+
245
+ prob_dict = {label: round(float(probs[i]), 4) for i, label in enumerate(TESTPACK_LABELS)}
246
+ applicable = [label for i, label in enumerate(TESTPACK_LABELS) if probs[i] >= 0.15 and label != "none"]
247
+ if pack_name != "none" and pack_name not in applicable:
248
+ applicable.insert(0, pack_name)
249
+
250
+ # Compound vulnerability invariant detection:
251
+ # 1. Unauthenticated state-changing or sensitive endpoint -> authentication is applicable
252
+ if not endpoint.auth_required and (endpoint.state_changing or endpoint.sensitive_data):
253
+ if "authentication" not in applicable:
254
+ applicable.append("authentication")
255
+ # 2. Object identifier present -> bola is applicable
256
+ if endpoint.object_identifier and "bola" not in applicable and pack_name != "none":
257
+ applicable.append("bola")
258
+ # 3. External network call -> ssrf is applicable
259
+ if endpoint.external_network and "ssrf" not in applicable:
260
+ applicable.append("ssrf")
261
+ # 4. Privileged admin route -> bfla is applicable
262
+ if ("admin" in endpoint.roles or "/admin" in endpoint.path.lower()) and "bfla" not in applicable:
263
+ applicable.append("bfla")
264
+
265
+ if not applicable:
266
+ applicable = [pack_name]
267
+
268
+ latency = (time.perf_counter() - start_time) * 1000
269
+ decision = TestSelectionDecision(
270
+ primary_testpack=pack_name,
271
+ confidence=conf,
272
+ applicable_testpacks=applicable,
273
+ testpack_probabilities=prob_dict,
274
+ )
275
+ self.telemetry.record("test_selection", endpoint.display_name(), decision.model_dump(), latency)
276
+ return decision
277
+ except Exception as e:
278
+ logger.debug(f"Finetuned Laya testpack inference failed: {e}")
279
+
280
+ # 2. Base Laya agent
281
+ if self._agent:
282
+ try:
283
+ res = self._agent.decide(state, schema=TestSelectionDecision)
284
+ if isinstance(res, dict):
285
+ decision = TestSelectionDecision.model_validate(res)
286
+ else:
287
+ decision = res
288
+ if decision.confidence >= self.thresholds.min_confidence:
289
+ latency = (time.perf_counter() - start_time) * 1000
290
+ self.telemetry.record("test_selection", endpoint.display_name(), decision.model_dump(), latency)
291
+ return decision
292
+ except Exception as e:
293
+ logger.debug(f"Laya decide_test_selection failed: {e}")
294
+
295
+ # 3. Calibrated deterministic fallback
296
+ matched_pack = "none"
297
+ for h in hypotheses:
298
+ if h.endpoint_id == endpoint.id:
299
+ matched_pack = h.recommended_test_pack.lower()
300
+ break
301
+
302
+ if matched_pack == "none":
303
+ if endpoint.external_network:
304
+ matched_pack = "ssrf"
305
+ elif endpoint.object_identifier:
306
+ matched_pack = "bola"
307
+ elif "admin" in endpoint.roles:
308
+ matched_pack = "bfla"
309
+ elif not endpoint.auth_required and endpoint.state_changing:
310
+ matched_pack = "authentication"
311
+ else:
312
+ matched_pack = "injection"
313
+
314
+ decision = TestSelectionDecision(
315
+ primary_testpack=matched_pack,
316
+ confidence=0.88,
317
+ applicable_testpacks=[matched_pack],
318
+ testpack_probabilities={matched_pack: 0.88},
319
+ )
320
+ latency = (time.perf_counter() - start_time) * 1000
321
+ self.telemetry.record("test_selection", endpoint.display_name(), decision.model_dump(), latency, abstained=True)
322
+ return decision
323
+
324
+ def decide_evidence_strength(
325
+ self, endpoint: Endpoint, observation_summary: str, response_status: int
326
+ ) -> EvidenceStrengthDecision:
327
+ """Evaluates whether runtime observation provides conclusive proof of vulnerability."""
328
+ start_time = time.perf_counter()
329
+ state = format_observation_state(endpoint, observation_summary, response_status)
330
+
331
+ if self._agent:
332
+ try:
333
+ res = self._agent.decide(state, schema=EvidenceStrengthDecision)
334
+ latency = (time.perf_counter() - start_time) * 1000
335
+ if isinstance(res, dict):
336
+ return EvidenceStrengthDecision.model_validate(res)
337
+ return res
338
+ except Exception:
339
+ pass
340
+
341
+ # Deterministic calibration
342
+ obs_lower = observation_summary.lower()
343
+ is_confirmed = "confirmed" in obs_lower or (response_status == 200 and "unauthorized" in obs_lower)
344
+ strength = "strong" if is_confirmed else "moderate" if response_status in (200, 500) else "weak"
345
+
346
+ decision = EvidenceStrengthDecision(
347
+ strength=strength,
348
+ confirmed_vulnerability=is_confirmed,
349
+ )
350
+ latency = (time.perf_counter() - start_time) * 1000
351
+ self.telemetry.record("evidence_strength", endpoint.display_name(), decision.model_dump(), latency, abstained=True)
352
+ return decision
@@ -0,0 +1,56 @@
1
+ """Decision schemas for Laya System 1 fast decision engine."""
2
+
3
+ from typing import Literal, Optional, List, Dict
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class EndpointPriorityDecision(BaseModel):
8
+ """Laya decision for assigning audit priority to an endpoint."""
9
+ priority_level: Literal["critical", "high", "medium", "low"] = Field(
10
+ description="Calculated audit priority based on attack surface exposure"
11
+ )
12
+ should_test: bool = Field(
13
+ description="Whether this endpoint requires active runtime security testing"
14
+ )
15
+
16
+
17
+ class TestSelectionDecision(BaseModel):
18
+ """Laya decision for selecting test packs with multi-label compound vulnerability support."""
19
+ primary_testpack: Literal[
20
+ "bola", "bfla", "authentication", "ssrf", "injection", "mass_assignment", "none"
21
+ ] = Field(description="Most informative primary security test pack for this attack path")
22
+ confidence: float = Field(
23
+ default=0.85,
24
+ ge=0.0,
25
+ le=1.0,
26
+ description="Model confidence in primary test pack selection",
27
+ )
28
+ applicable_testpacks: List[str] = Field(
29
+ default_factory=list,
30
+ description="All applicable test packs meeting activation threshold for compound vulnerabilities",
31
+ )
32
+ testpack_probabilities: Dict[str, float] = Field(
33
+ default_factory=dict,
34
+ description="Independent calibrated probability distribution across all 7 test pack families",
35
+ )
36
+
37
+
38
+ class EvidenceStrengthDecision(BaseModel):
39
+ """Laya decision for evaluating runtime observation evidence strength."""
40
+ strength: Literal["weak", "moderate", "strong"] = Field(
41
+ description="Calibrated strength of observed security anomaly"
42
+ )
43
+ confirmed_vulnerability: bool = Field(
44
+ description="Whether the anomaly provides sufficient proof of exploitability"
45
+ )
46
+
47
+
48
+ class ContinueTestingDecision(BaseModel):
49
+ """Laya decision on whether to continue probing or stop."""
50
+ should_continue: bool = Field(
51
+ description="Whether additional test probes are required"
52
+ )
53
+ stop_reason: Literal["sufficient_proof", "not_vulnerable", "budget_exceeded", "scope_boundary"] = Field(
54
+ default="sufficient_proof",
55
+ description="Reason for concluding test sequence",
56
+ )
@@ -0,0 +1,48 @@
1
+ """Telemetry and decision logging for Laya."""
2
+
3
+ import time
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Dict, Any, List, Optional
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class DecisionLog(BaseModel):
11
+ timestamp: float = Field(default_factory=time.time)
12
+ decision_type: str
13
+ endpoint: str
14
+ decision_data: Dict[str, Any]
15
+ latency_ms: float
16
+ abstained: bool = False
17
+
18
+
19
+ class LayaTelemetry:
20
+ """Records Laya System 1 decisions for auditability and calibration."""
21
+
22
+ def __init__(self, log_path: Optional[Path] = None):
23
+ self.log_path = log_path
24
+ self.logs: List[DecisionLog] = []
25
+
26
+ def record(
27
+ self,
28
+ decision_type: str,
29
+ endpoint: str,
30
+ decision_data: Dict[str, Any],
31
+ latency_ms: float,
32
+ abstained: bool = False,
33
+ ) -> None:
34
+ entry = DecisionLog(
35
+ decision_type=decision_type,
36
+ endpoint=endpoint,
37
+ decision_data=decision_data,
38
+ latency_ms=latency_ms,
39
+ abstained=abstained,
40
+ )
41
+ self.logs.append(entry)
42
+ if self.log_path:
43
+ try:
44
+ self.log_path.parent.mkdir(parents=True, exist_ok=True)
45
+ with open(self.log_path, "a", encoding="utf-8") as f:
46
+ f.write(json.dumps(entry.model_dump()) + "\n")
47
+ except Exception:
48
+ pass
@@ -0,0 +1,12 @@
1
+ """Threshold and confidence gating for Laya decisions."""
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class LayaThresholds(BaseModel):
7
+ """Calibrated thresholds for Laya System 1 decision engine."""
8
+ min_confidence: float = 0.82
9
+ high_priority_cutoff: float = 0.75
10
+ evidence_strong_cutoff: float = 0.80
11
+ max_probes_per_endpoint: int = 5
12
+ abstain_on_low_confidence: bool = True