actshield 0.9.0__py3-none-any.whl

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 (366) hide show
  1. actshield/__init__.py +439 -0
  2. actshield/__main__.py +46 -0
  3. actshield/agents/__init__.py +12 -0
  4. actshield/agents/agent.py +200 -0
  5. actshield/agents/identity.py +114 -0
  6. actshield/api/__init__.py +34 -0
  7. actshield/api/models.py +198 -0
  8. actshield/api/server.py +669 -0
  9. actshield/api/service.py +599 -0
  10. actshield/approval/__init__.py +15 -0
  11. actshield/approval/approval.py +164 -0
  12. actshield/cli/__init__.py +8 -0
  13. actshield/cli/graph.py +49 -0
  14. actshield/cli/main.py +802 -0
  15. actshield/cli/shell.py +406 -0
  16. actshield/cli/theme.py +110 -0
  17. actshield/client.py +757 -0
  18. actshield/config.py +88 -0
  19. actshield/context/__init__.py +14 -0
  20. actshield/context/context.py +161 -0
  21. actshield/context/provenance.py +108 -0
  22. actshield/context/taint.py +48 -0
  23. actshield/decisions/decision.py +74 -0
  24. actshield/delegation/__init__.py +11 -0
  25. actshield/delegation/authority.py +63 -0
  26. actshield/delegation/delegation.py +180 -0
  27. actshield/demo.py +139 -0
  28. actshield/doctor.py +94 -0
  29. actshield/drift/__init__.py +27 -0
  30. actshield/drift/drift_engine.py +149 -0
  31. actshield/drift/drift_models.py +63 -0
  32. actshield/evaluation/__init__.py +1 -0
  33. actshield/evaluation/cases/__init__.py +1 -0
  34. actshield/evaluation/eval_suite.py +254 -0
  35. actshield/forensics/__init__.py +53 -0
  36. actshield/forensics/access_graph.py +216 -0
  37. actshield/forensics/authority_graph.py +186 -0
  38. actshield/forensics/delegation_graph.py +119 -0
  39. actshield/forensics/explanation.py +254 -0
  40. actshield/forensics/models.py +488 -0
  41. actshield/forensics/queries.py +367 -0
  42. actshield/forensics/serializers.py +68 -0
  43. actshield/forensics/service.py +310 -0
  44. actshield/forensics/snapshots.py +174 -0
  45. actshield/gates/__init__.py +13 -0
  46. actshield/gates/gate_models.py +46 -0
  47. actshield/gates/security_gate.py +191 -0
  48. actshield/gateway/__init__.py +13 -0
  49. actshield/gateway/http.py +191 -0
  50. actshield/gateway/mcp.py +292 -0
  51. actshield/incidents/__init__.py +25 -0
  52. actshield/incidents/incident.py +94 -0
  53. actshield/incidents/incident_engine.py +163 -0
  54. actshield/incidents/incident_rules.py +116 -0
  55. actshield/incidents/incident_state.py +58 -0
  56. actshield/integrations/__init__.py +24 -0
  57. actshield/integrations/ai_secura.py +169 -0
  58. actshield/integrations/ai_secura_analysis.py +236 -0
  59. actshield/integrations/apiris.py +122 -0
  60. actshield/integrations/few_shot.py +104 -0
  61. actshield/integrations/ollama_adapter.py +538 -0
  62. actshield/integrations/prompts/__init__.py +1 -0
  63. actshield/integrations/prompts/examples/__init__.py +1 -0
  64. actshield/integrations/security_packet.py +82 -0
  65. actshield/llm_config.py +89 -0
  66. actshield/offensive/__init__.py +95 -0
  67. actshield/offensive/adaptive/__init__.py +22 -0
  68. actshield/offensive/adaptive/adaptive_engine.py +165 -0
  69. actshield/offensive/adaptive/attack_selector.py +51 -0
  70. actshield/offensive/adaptive/boundary_search.py +99 -0
  71. actshield/offensive/adaptive/campaign_memory.py +123 -0
  72. actshield/offensive/adaptive/feedback.py +84 -0
  73. actshield/offensive/adaptive/mutation_planner.py +57 -0
  74. actshield/offensive/attack.py +78 -0
  75. actshield/offensive/campaign.py +219 -0
  76. actshield/offensive/corpus.py +155 -0
  77. actshield/offensive/engine.py +466 -0
  78. actshield/offensive/evaluator.py +116 -0
  79. actshield/offensive/evidence.py +63 -0
  80. actshield/offensive/generator.py +128 -0
  81. actshield/offensive/mutation.py +101 -0
  82. actshield/offensive/results.py +77 -0
  83. actshield/offensive/safety.py +80 -0
  84. actshield/offensive/strategies/__init__.py +22 -0
  85. actshield/offensive/strategies/authority_escalation.py +100 -0
  86. actshield/offensive/strategies/base.py +34 -0
  87. actshield/offensive/strategies/context_manipulation.py +100 -0
  88. actshield/offensive/strategies/data_exfiltration.py +100 -0
  89. actshield/offensive/strategies/delegation_attacks.py +100 -0
  90. actshield/offensive/strategies/mcp_attacks.py +100 -0
  91. actshield/offensive/strategies/prompt_injection.py +100 -0
  92. actshield/offensive/strategies/tool_poisoning.py +100 -0
  93. actshield/offensive/target.py +113 -0
  94. actshield/offensive/vulnerable_target.py +121 -0
  95. actshield/persistence/__init__.py +18 -0
  96. actshield/persistence/siem.py +102 -0
  97. actshield/persistence/storage.py +240 -0
  98. actshield/policy/__init__.py +12 -0
  99. actshield/policy/evaluator.py +294 -0
  100. actshield/policy/intent.py +113 -0
  101. actshield/policy/policy.py +48 -0
  102. actshield/posture/__init__.py +26 -0
  103. actshield/posture/posture_engine.py +186 -0
  104. actshield/posture/posture_models.py +152 -0
  105. actshield/posture/posture_rules.py +137 -0
  106. actshield/providers/__init__.py +32 -0
  107. actshield/providers/anthropic_provider.py +146 -0
  108. actshield/providers/base.py +160 -0
  109. actshield/providers/gemini_provider.py +133 -0
  110. actshield/providers/null_provider.py +40 -0
  111. actshield/providers/ollama_provider.py +135 -0
  112. actshield/providers/openai_provider.py +142 -0
  113. actshield/providers/registry.py +244 -0
  114. actshield/py.typed +0 -0
  115. actshield/response/__init__.py +14 -0
  116. actshield/response/response_actions.py +39 -0
  117. actshield/response/response_audit.py +32 -0
  118. actshield/response/response_engine.py +139 -0
  119. actshield/risk/models.py +104 -0
  120. actshield/serve.py +117 -0
  121. actshield/static/404.html +1 -0
  122. actshield/static/__next.__PAGE__.txt +24 -0
  123. actshield/static/__next._full.txt +20 -0
  124. actshield/static/__next._tree.txt +4 -0
  125. actshield/static/_next/static/Yp_swxN-C9UoCRNOwkYnY/_buildManifest.js +11 -0
  126. actshield/static/_next/static/Yp_swxN-C9UoCRNOwkYnY/_clientMiddlewareManifest.js +1 -0
  127. actshield/static/_next/static/Yp_swxN-C9UoCRNOwkYnY/_ssgManifest.js +1 -0
  128. actshield/static/_next/static/chunks/09kny2eqjc2or.js +1 -0
  129. actshield/static/_next/static/chunks/0c1zba1u9whdp.js +1 -0
  130. actshield/static/_next/static/chunks/0cz1d0mv5g_q7.js +1 -0
  131. actshield/static/_next/static/chunks/0dbjjeg01x25o.js +1 -0
  132. actshield/static/_next/static/chunks/0duobs55ne238.js +1 -0
  133. actshield/static/_next/static/chunks/0iaz41l6068e4.js +1 -0
  134. actshield/static/_next/static/chunks/0mjhyv72x2idp.js +1 -0
  135. actshield/static/_next/static/chunks/0nelpqfu_ciq6.js +1 -0
  136. actshield/static/_next/static/chunks/0p1bci9ch797v.js +1 -0
  137. actshield/static/_next/static/chunks/0tld_ygho5tgm.js +1 -0
  138. actshield/static/_next/static/chunks/0w1-y7oni66j9.js +1 -0
  139. actshield/static/_next/static/chunks/10537zyl427zj.js +1 -0
  140. actshield/static/_next/static/chunks/11pc_jkl3aggn.js +1 -0
  141. actshield/static/_next/static/chunks/16k7nw0b3t8ys.js +1 -0
  142. actshield/static/_next/static/chunks/19mx3mg6lkumu.js +1 -0
  143. actshield/static/_next/static/chunks/1_8p47b-kxg2a.js +1 -0
  144. actshield/static/_next/static/chunks/1dszjozwfqegx.js +1 -0
  145. actshield/static/_next/static/chunks/1h43jcb90-m2r.js +22 -0
  146. actshield/static/_next/static/chunks/1ly04cvt_wh_j.js +1 -0
  147. actshield/static/_next/static/chunks/1odxrypuwuwp_.js +1 -0
  148. actshield/static/_next/static/chunks/1oes4c35qz5ku.js +1 -0
  149. actshield/static/_next/static/chunks/1ow883jl4ln-r.js +16 -0
  150. actshield/static/_next/static/chunks/1po7bna1as8tc.js +1 -0
  151. actshield/static/_next/static/chunks/1rsh0og8ifkzx.js +1 -0
  152. actshield/static/_next/static/chunks/1v0lwzv-859z_.js +1 -0
  153. actshield/static/_next/static/chunks/25iat2epwx25a.js +1 -0
  154. actshield/static/_next/static/chunks/25uvt4s2f39a4.js +1 -0
  155. actshield/static/_next/static/chunks/27t_qfc-3_lzs.js +1 -0
  156. actshield/static/_next/static/chunks/28bkmy8mez7gm.js +1 -0
  157. actshield/static/_next/static/chunks/2b6brmw_drw1p.js +1 -0
  158. actshield/static/_next/static/chunks/2fnhtxi4j6vge.js +1 -0
  159. actshield/static/_next/static/chunks/2p087eftxemg4.js +6 -0
  160. actshield/static/_next/static/chunks/2s8pao_z8pgtq.js +1 -0
  161. actshield/static/_next/static/chunks/2yj7ooc2c-zpg.js +1 -0
  162. actshield/static/_next/static/chunks/3-mrrr8udt5tk.js +1 -0
  163. actshield/static/_next/static/chunks/310vm2bl3xxpt.js +31 -0
  164. actshield/static/_next/static/chunks/35mvsv251jx1r.css +3 -0
  165. actshield/static/_next/static/chunks/370vf0oetlsx9.js +1 -0
  166. actshield/static/_next/static/chunks/3e-2m72tyo556.js +1 -0
  167. actshield/static/_next/static/chunks/3fntmmi971322.js +1 -0
  168. actshield/static/_next/static/chunks/3fzw-g0hycl2c.js +1 -0
  169. actshield/static/_next/static/chunks/3lopq3lhmcjxg.js +129 -0
  170. actshield/static/_next/static/chunks/3mpe2jejel0v2.css +1 -0
  171. actshield/static/_next/static/chunks/3t8o4f2lzr2sf.js +1 -0
  172. actshield/static/_next/static/chunks/3y-v1neyllne5.js +1 -0
  173. actshield/static/_next/static/chunks/43oiwy8mx6cs1.js +1 -0
  174. actshield/static/_next/static/chunks/turbopack-23wzuebcy_lxm.js +1 -0
  175. actshield/static/_next/static/media/1bffadaabf893a1e-s.3-6t-g6q0vh0a.woff2 +0 -0
  176. actshield/static/_next/static/media/2bbe8d2671613f1f-s.0k62hbripvv8p.woff2 +0 -0
  177. actshield/static/_next/static/media/2c55a0e60120577a-s.0-dom-5bn10r2.woff2 +0 -0
  178. actshield/static/_next/static/media/5476f68d60460930-s.2uwcyprjm3xu3.woff2 +0 -0
  179. actshield/static/_next/static/media/83afe278b6a6bb3c-s.p.2bn3s6zvc0dyp.woff2 +0 -0
  180. actshield/static/_next/static/media/9c72aa0f40e4eef8-s.1y4-pdgsjb-pw.woff2 +0 -0
  181. actshield/static/_next/static/media/ad66f9afd8947f86-s.3lvt2whj97whp.woff2 +0 -0
  182. actshield/static/_next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  183. actshield/static/_not-found/__next._full.txt +17 -0
  184. actshield/static/_not-found/__next._not-found/__PAGE__.txt +23 -0
  185. actshield/static/_not-found/__next._tree.txt +3 -0
  186. actshield/static/_not-found.html +1 -0
  187. actshield/static/_not-found.txt +17 -0
  188. actshield/static/access/matrix/__next._full.txt +24 -0
  189. actshield/static/access/matrix/__next._tree.txt +4 -0
  190. actshield/static/access/matrix/__next.access/matrix/__PAGE__.txt +30 -0
  191. actshield/static/access/matrix.html +1 -0
  192. actshield/static/access/matrix.txt +24 -0
  193. actshield/static/activity/__next._full.txt +22 -0
  194. actshield/static/activity/__next._tree.txt +4 -0
  195. actshield/static/activity/__next.activity/__PAGE__.txt +27 -0
  196. actshield/static/activity.html +1 -0
  197. actshield/static/activity.txt +22 -0
  198. actshield/static/agents/__next._full.txt +22 -0
  199. actshield/static/agents/__next._tree.txt +4 -0
  200. actshield/static/agents/__next.agents/__PAGE__.txt +27 -0
  201. actshield/static/agents.html +1 -0
  202. actshield/static/agents.txt +22 -0
  203. actshield/static/ai-secura/__next._full.txt +22 -0
  204. actshield/static/ai-secura/__next._tree.txt +4 -0
  205. actshield/static/ai-secura/__next.ai-secura/__PAGE__.txt +27 -0
  206. actshield/static/ai-secura.html +1 -0
  207. actshield/static/ai-secura.txt +22 -0
  208. actshield/static/api/__next._full.txt +22 -0
  209. actshield/static/api/__next._tree.txt +4 -0
  210. actshield/static/api/__next.api/__PAGE__.txt +27 -0
  211. actshield/static/api.html +15 -0
  212. actshield/static/api.txt +22 -0
  213. actshield/static/apiris/__next._full.txt +22 -0
  214. actshield/static/apiris/__next._tree.txt +4 -0
  215. actshield/static/apiris/__next.apiris/__PAGE__.txt +27 -0
  216. actshield/static/apiris.html +1 -0
  217. actshield/static/apiris.txt +22 -0
  218. actshield/static/attack-graph/__next._full.txt +23 -0
  219. actshield/static/attack-graph/__next._tree.txt +5 -0
  220. actshield/static/attack-graph/__next.attack-graph/__PAGE__.txt +28 -0
  221. actshield/static/attack-graph.html +2 -0
  222. actshield/static/attack-graph.txt +23 -0
  223. actshield/static/campaigns/__next._full.txt +22 -0
  224. actshield/static/campaigns/__next._tree.txt +4 -0
  225. actshield/static/campaigns/__next.campaigns/__PAGE__.txt +27 -0
  226. actshield/static/campaigns.html +1 -0
  227. actshield/static/campaigns.txt +22 -0
  228. actshield/static/context/__next._full.txt +22 -0
  229. actshield/static/context/__next._tree.txt +4 -0
  230. actshield/static/context/__next.context/__PAGE__.txt +27 -0
  231. actshield/static/context.html +1 -0
  232. actshield/static/context.txt +22 -0
  233. actshield/static/dashboard/__next._full.txt +22 -0
  234. actshield/static/dashboard/__next._tree.txt +4 -0
  235. actshield/static/dashboard/__next.dashboard/__PAGE__.txt +27 -0
  236. actshield/static/dashboard.html +1 -0
  237. actshield/static/dashboard.txt +22 -0
  238. actshield/static/delegations/__next._full.txt +22 -0
  239. actshield/static/delegations/__next._tree.txt +4 -0
  240. actshield/static/delegations/__next.delegations/__PAGE__.txt +27 -0
  241. actshield/static/delegations.html +1 -0
  242. actshield/static/delegations.txt +22 -0
  243. actshield/static/demo/__next._full.txt +22 -0
  244. actshield/static/demo/__next._tree.txt +4 -0
  245. actshield/static/demo/__next.demo/__PAGE__.txt +27 -0
  246. actshield/static/demo.html +1 -0
  247. actshield/static/demo.txt +22 -0
  248. actshield/static/drift/__next._full.txt +22 -0
  249. actshield/static/drift/__next._tree.txt +4 -0
  250. actshield/static/drift/__next.drift/__PAGE__.txt +27 -0
  251. actshield/static/drift.html +1 -0
  252. actshield/static/drift.txt +22 -0
  253. actshield/static/favicon.ico +0 -0
  254. actshield/static/file.svg +1 -0
  255. actshield/static/forensics/__next._full.txt +22 -0
  256. actshield/static/forensics/__next._tree.txt +4 -0
  257. actshield/static/forensics/__next.forensics/__PAGE__.txt +27 -0
  258. actshield/static/forensics.html +1 -0
  259. actshield/static/forensics.txt +22 -0
  260. actshield/static/globe.svg +1 -0
  261. actshield/static/incidents/__next._full.txt +22 -0
  262. actshield/static/incidents/__next._tree.txt +4 -0
  263. actshield/static/incidents/__next.incidents/__PAGE__.txt +27 -0
  264. actshield/static/incidents.html +1 -0
  265. actshield/static/incidents.txt +22 -0
  266. actshield/static/index.html +3 -0
  267. actshield/static/index.txt +20 -0
  268. actshield/static/logo.png +0 -0
  269. actshield/static/metrics/__next._full.txt +22 -0
  270. actshield/static/metrics/__next._tree.txt +4 -0
  271. actshield/static/metrics/__next.metrics/__PAGE__.txt +27 -0
  272. actshield/static/metrics.html +1 -0
  273. actshield/static/metrics.txt +22 -0
  274. actshield/static/next.svg +1 -0
  275. actshield/static/offensive/__next._full.txt +22 -0
  276. actshield/static/offensive/__next._tree.txt +4 -0
  277. actshield/static/offensive/__next.offensive/__PAGE__.txt +27 -0
  278. actshield/static/offensive.html +1 -0
  279. actshield/static/offensive.txt +22 -0
  280. actshield/static/policies/__next._full.txt +22 -0
  281. actshield/static/policies/__next._tree.txt +4 -0
  282. actshield/static/policies/__next.policies/__PAGE__.txt +27 -0
  283. actshield/static/policies.html +1 -0
  284. actshield/static/policies.txt +22 -0
  285. actshield/static/posture/__next._full.txt +22 -0
  286. actshield/static/posture/__next._tree.txt +4 -0
  287. actshield/static/posture/__next.posture/__PAGE__.txt +27 -0
  288. actshield/static/posture.html +1 -0
  289. actshield/static/posture.txt +22 -0
  290. actshield/static/regressions/__next._full.txt +22 -0
  291. actshield/static/regressions/__next._tree.txt +4 -0
  292. actshield/static/regressions/__next.regressions/__PAGE__.txt +27 -0
  293. actshield/static/regressions.html +1 -0
  294. actshield/static/regressions.txt +22 -0
  295. actshield/static/reports/__next._full.txt +22 -0
  296. actshield/static/reports/__next._tree.txt +4 -0
  297. actshield/static/reports/__next.reports/__PAGE__.txt +27 -0
  298. actshield/static/reports.html +1 -0
  299. actshield/static/reports.txt +22 -0
  300. actshield/static/resources/__next._full.txt +22 -0
  301. actshield/static/resources/__next._tree.txt +4 -0
  302. actshield/static/resources/__next.resources/__PAGE__.txt +27 -0
  303. actshield/static/resources.html +1 -0
  304. actshield/static/resources.txt +22 -0
  305. actshield/static/security-gates/__next._full.txt +22 -0
  306. actshield/static/security-gates/__next._tree.txt +4 -0
  307. actshield/static/security-gates/__next.security-gates/__PAGE__.txt +27 -0
  308. actshield/static/security-gates.html +1 -0
  309. actshield/static/security-gates.txt +22 -0
  310. actshield/static/settings/__next._full.txt +22 -0
  311. actshield/static/settings/__next._tree.txt +4 -0
  312. actshield/static/settings/__next.settings/__PAGE__.txt +27 -0
  313. actshield/static/settings.html +1 -0
  314. actshield/static/settings.txt +22 -0
  315. actshield/static/tasks/__next._full.txt +22 -0
  316. actshield/static/tasks/__next._tree.txt +4 -0
  317. actshield/static/tasks/__next.tasks/__PAGE__.txt +27 -0
  318. actshield/static/tasks.html +1 -0
  319. actshield/static/tasks.txt +22 -0
  320. actshield/static/threats/__next._full.txt +22 -0
  321. actshield/static/threats/__next._tree.txt +4 -0
  322. actshield/static/threats/__next.threats/__PAGE__.txt +27 -0
  323. actshield/static/threats.html +1 -0
  324. actshield/static/threats.txt +22 -0
  325. actshield/static/tools/__next._full.txt +22 -0
  326. actshield/static/tools/__next._tree.txt +4 -0
  327. actshield/static/tools/__next.tools/__PAGE__.txt +27 -0
  328. actshield/static/tools.html +1 -0
  329. actshield/static/tools.txt +22 -0
  330. actshield/static/traces/__next._full.txt +22 -0
  331. actshield/static/traces/__next._tree.txt +4 -0
  332. actshield/static/traces/__next.traces/__PAGE__.txt +27 -0
  333. actshield/static/traces.html +1 -0
  334. actshield/static/traces.txt +22 -0
  335. actshield/static/vercel.svg +1 -0
  336. actshield/static/window.svg +1 -0
  337. actshield/tasks/__init__.py +6 -0
  338. actshield/tasks/task.py +143 -0
  339. actshield/threatmodel/__init__.py +70 -0
  340. actshield/threatmodel/actors.py +210 -0
  341. actshield/threatmodel/analyzer.py +131 -0
  342. actshield/threatmodel/assets.py +167 -0
  343. actshield/threatmodel/boundaries.py +165 -0
  344. actshield/threatmodel/exporters.py +49 -0
  345. actshield/threatmodel/mitigations.py +209 -0
  346. actshield/threatmodel/models.py +80 -0
  347. actshield/threatmodel/report.py +341 -0
  348. actshield/threatmodel/scenarios.py +212 -0
  349. actshield/threatmodel/scoring.py +66 -0
  350. actshield/threatmodel/threats.py +387 -0
  351. actshield/tools/__init__.py +20 -0
  352. actshield/tools/interceptor.py +256 -0
  353. actshield/tools/tool.py +71 -0
  354. actshield/tracing/__init__.py +38 -0
  355. actshield/tracing/correlation.py +173 -0
  356. actshield/tracing/events.py +75 -0
  357. actshield/tracing/tracer.py +305 -0
  358. actshield/watch.py +129 -0
  359. actshield-0.9.0.dist-info/METADATA +57 -0
  360. actshield-0.9.0.dist-info/RECORD +366 -0
  361. actshield-0.9.0.dist-info/WHEEL +5 -0
  362. actshield-0.9.0.dist-info/entry_points.txt +4 -0
  363. actshield-0.9.0.dist-info/top_level.txt +2 -0
  364. agentguard/__init__.py +39 -0
  365. agentguard/__main__.py +8 -0
  366. agentguard/py.typed +1 -0
@@ -0,0 +1,84 @@
1
+ """Defensive feedback analyzer for adaptive offensive exploration."""
2
+
3
+ from enum import Enum
4
+ from typing import Any, Dict, List, Optional
5
+ from pydantic import BaseModel, Field
6
+
7
+ from actshield.offensive.results import AttackStatus, OffensiveAttackResult
8
+
9
+
10
+ class DefenseCategory(str, Enum):
11
+ """Categorization of defensive enforcement mechanisms."""
12
+ TAINT_GUARD = "TAINT_GUARD" # Blocked because tainted context reached a sensitive sink
13
+ AUTHORITY_GUARD = "AUTHORITY_GUARD" # Blocked due to missing capability or exceeded delegation
14
+ INTENT_GUARD = "INTENT_GUARD" # Blocked due to divergence from task original intent
15
+ TRUST_GUARD = "TRUST_GUARD" # Blocked because agent trust level is UNTRUSTED
16
+ BENIGN_ALLOW = "BENIGN_ALLOW" # Allowed under standard policy
17
+ UNKNOWN_DEFENSE = "UNKNOWN_DEFENSE" # Other or unclassified
18
+
19
+
20
+ class DefenseFeedback(BaseModel):
21
+ """Structured analytical feedback derived from previous defense response."""
22
+
23
+ attack_id: str
24
+ decision: str
25
+ reason_code: str
26
+ explanation: str
27
+ defense_category: DefenseCategory
28
+ bypassed: bool
29
+ sensitive_executions: int
30
+
31
+ # Adaptive guidance
32
+ suggested_mutation_strategies: List[str] = Field(default_factory=list)
33
+ boundary_distance_estimate: float = Field(default=1.0, description="1.0 = deeply blocked, 0.0 = bypassed")
34
+
35
+
36
+ class FeedbackAnalyzer:
37
+ """Analyzes offensive attack results to guide subsequent mutation planning."""
38
+
39
+ @staticmethod
40
+ def analyze(result: OffensiveAttackResult) -> DefenseFeedback:
41
+ """Extract actionable feedback from an attack result."""
42
+ actual_action = result.actual_decision
43
+ reason = result.execution_evidence.policy_reason or ""
44
+ bypassed = result.bypassed
45
+ sens_exec = result.execution_evidence.sensitive_db_calls
46
+
47
+ # Classify defense
48
+ if bypassed or actual_action == "ALLOW":
49
+ category = DefenseCategory.BENIGN_ALLOW
50
+ strategies = []
51
+ dist = 0.0
52
+ elif "TAINT" in reason or "tainted" in reason.lower():
53
+ category = DefenseCategory.TAINT_GUARD
54
+ strategies = ["context_repackaging", "nested_roleplay", "base64_encoding", "unicode_homoglyph"]
55
+ dist = 0.7
56
+ elif "AUTHORITY" in reason or "CAPABILITY" in reason or "DELEGATION" in reason:
57
+ category = DefenseCategory.AUTHORITY_GUARD
58
+ strategies = ["authority_impersonation", "semantic_reframing", "delimiter_injection"]
59
+ dist = 0.8
60
+ elif "INTENT" in reason or "intent" in reason.lower():
61
+ category = DefenseCategory.INTENT_GUARD
62
+ strategies = ["semantic_reframing", "typo_injection", "casing_randomization"]
63
+ dist = 0.6
64
+ elif "UNTRUSTED" in reason:
65
+ category = DefenseCategory.TRUST_GUARD
66
+ strategies = ["authority_impersonation", "nested_roleplay"]
67
+ dist = 0.9
68
+ else:
69
+ category = DefenseCategory.UNKNOWN_DEFENSE
70
+ strategies = ["semantic_reframing", "base64_encoding", "nested_roleplay"]
71
+ dist = 0.75
72
+
73
+ return DefenseFeedback(
74
+ attack_id=result.attack_id,
75
+ decision=actual_action,
76
+ reason_code=reason,
77
+ explanation=result.error_message or reason,
78
+ defense_category=category,
79
+ bypassed=bypassed,
80
+ sensitive_executions=sens_exec,
81
+ suggested_mutation_strategies=strategies,
82
+ boundary_distance_estimate=dist,
83
+ )
84
+
@@ -0,0 +1,57 @@
1
+ """Adaptive mutation planner composing multi-step transformations based on defensive feedback."""
2
+
3
+ from typing import List, Optional
4
+ from actshield.offensive.attack import AttackCase, SafetyClass
5
+ from actshield.offensive.mutation import MutationEngine, MutationStrategy
6
+ from actshield.offensive.adaptive.feedback import DefenseFeedback
7
+
8
+
9
+ class MutationPlanner:
10
+ """Plans and composes targeted mutations based on previous defense reasons."""
11
+
12
+ # Map feedback suggested strategy names to MutationStrategy enums
13
+ STRATEGY_MAP = {
14
+ "context_repackaging": MutationStrategy.CONTEXT_PLACEMENT,
15
+ "nested_roleplay": MutationStrategy.ROLEPLAY_FRAMING,
16
+ "base64_encoding": MutationStrategy.ENCODING_OBSCURE,
17
+ "unicode_homoglyph": MutationStrategy.ENCODING_OBSCURE,
18
+ "authority_impersonation": MutationStrategy.AUTHORITY_FABRICATION,
19
+ "semantic_reframing": MutationStrategy.WORDING_VARIATION,
20
+ "delimiter_injection": MutationStrategy.DELIMITER_STUFFING,
21
+ "typo_injection": MutationStrategy.WORDING_VARIATION,
22
+ "casing_randomization": MutationStrategy.WORDING_VARIATION,
23
+ "urgency_escalation": MutationStrategy.URGENCY_INJECTION,
24
+ }
25
+
26
+ @classmethod
27
+ def plan_next_mutations(
28
+ cls,
29
+ parent_attack: AttackCase,
30
+ feedback: DefenseFeedback,
31
+ depth: int,
32
+ branch_factor: int = 2,
33
+ ) -> List[AttackCase]:
34
+ """Generate next generation of mutated attack cases tailored to previous defense response."""
35
+ planned_cases: List[AttackCase] = []
36
+ suggested = feedback.suggested_mutation_strategies or ["semantic_reframing", "nested_roleplay"]
37
+
38
+ for idx, strat_name in enumerate(suggested[:branch_factor], 1):
39
+ strategy_enum = cls.STRATEGY_MAP.get(strat_name, MutationStrategy.WORDING_VARIATION)
40
+
41
+ # Apply transformation
42
+ mutated_cases = MutationEngine.mutate(parent_attack, strategy=strategy_enum, count=1)
43
+ if mutated_cases:
44
+ mut_case = mutated_cases[0]
45
+ mut_id = f"{parent_attack.attack_id}_d{depth}_{strategy_enum.value[:4]}_{idx:02d}"
46
+
47
+ # Enrich metadata
48
+ mut_case = mut_case.model_copy(update={
49
+ "attack_id": mut_id,
50
+ "mutation_of": parent_attack.attack_id,
51
+ "mutation_strategy": strategy_enum.value,
52
+ "safety_class": SafetyClass.LOCAL_SYNTHETIC,
53
+ })
54
+ planned_cases.append(mut_case)
55
+
56
+ return planned_cases
57
+
@@ -0,0 +1,78 @@
1
+ """Attack models and schemas for ActShield offensive validation."""
2
+
3
+ from enum import Enum
4
+ from typing import Any, Dict, List, Optional
5
+ from pydantic import BaseModel, Field
6
+
7
+ from actshield.tracing.correlation import generate_id
8
+
9
+
10
+ class AttackType(str, Enum):
11
+ """Standardized classification of offensive attack vectors."""
12
+ INDIRECT_PROMPT_INJECTION = "indirect_prompt_injection"
13
+ DIRECT_PROMPT_INJECTION = "direct_prompt_injection"
14
+ AUTHORITY_IMPERSONATION = "authority_impersonation"
15
+ AUTHORITY_ESCALATION = "authority_escalation"
16
+ TOOL_POISONING = "tool_poisoning"
17
+ MCP_SERVER_POISONING = "mcp_server_poisoning"
18
+ CONTEXT_MANIPULATION = "context_manipulation"
19
+ TAINT_LAUNDERING = "taint_laundering"
20
+ DELEGATION_ESCALATION = "delegation_escalation"
21
+ MULTI_HOP_ESCALATION = "multi_hop_escalation"
22
+ DATA_EXFILTRATION = "data_exfiltration"
23
+ INTENT_DRIFT = "intent_drift"
24
+ JAILBREAK = "jailbreak"
25
+ CUSTOM = "custom"
26
+
27
+
28
+ class SafetyClass(str, Enum):
29
+ """Execution safety classification."""
30
+ LOCAL_SYNTHETIC = "LOCAL_SYNTHETIC"
31
+ SANDBOX_MOCK = "SANDBOX_MOCK"
32
+ UNSAFE_EXTERNAL = "UNSAFE_EXTERNAL"
33
+
34
+
35
+ class ExpectedBehavior(str, Enum):
36
+ """Expected security outcome when the attack is injected."""
37
+ BLOCK = "BLOCK"
38
+ ALLOW = "ALLOW"
39
+ HITL_REQUIRED = "HITL_REQUIRED"
40
+ SANITIZED = "SANITIZED"
41
+
42
+
43
+ class AttackCase(BaseModel):
44
+ """Structured offensive test case designed to validate security boundaries."""
45
+
46
+ attack_id: str = Field(default_factory=lambda: generate_id("atk"), description="Unique attack identifier")
47
+ attack_type: AttackType = Field(default=AttackType.CUSTOM, description="Classification of attack vector")
48
+ name: str = Field(default="", description="Human-readable name of the attack")
49
+ description: str = Field(default="", description="Detailed description of attack objective and mechanism")
50
+
51
+ # Injection details
52
+ entry_point: str = Field(default="mcp_response", description="Entry point (e.g. 'mcp_response', 'http_request', 'tool_output', 'user_input')")
53
+ target_agent: str = Field(default="DataAgent", description="Target acting agent name in the simulation")
54
+ payload: str = Field(default="", description="The adversarial payload data injected into the context")
55
+
56
+ # Target execution parameters
57
+ target_tool: Optional[str] = Field(default=None, description="The tool the attack attempts to cause execution of")
58
+ target_resource: Optional[str] = Field(default=None, description="Target resource name/sensitivity")
59
+ required_capability: Optional[str] = Field(default=None, description="Capability needed to execute target tool")
60
+ tool_arguments: Dict[str, Any] = Field(default_factory=dict, description="Arguments passed to target tool if triggered")
61
+
62
+ # Preconditions and Context
63
+ task_intent: str = Field(default="Standard authorized operation", description="The legitimate original user intent")
64
+ preconditions: Dict[str, Any] = Field(default_factory=dict, description="Preconditions required for attack setup")
65
+ context_taint: str = Field(default="TAINTED", description="Taint state assigned to injected payload")
66
+
67
+ # Expectations and Safety
68
+ expected_behavior: ExpectedBehavior = Field(default=ExpectedBehavior.BLOCK, description="Expected defense outcome")
69
+ severity: str = Field(default="HIGH", description="Assessed threat severity (LOW/MEDIUM/HIGH/CRITICAL)")
70
+ safety_class: SafetyClass = Field(default=SafetyClass.LOCAL_SYNTHETIC, description="Safety classification")
71
+
72
+ # Mutation Lineage
73
+ mutation_of: Optional[str] = Field(default=None, description="Base attack ID if this is a mutated variant")
74
+ mutation_strategy: Optional[str] = Field(default=None, description="Mutation strategy applied to generate this case")
75
+
76
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional contextual metadata")
77
+
78
+
@@ -0,0 +1,219 @@
1
+ """Attack campaign definitions and scorecard aggregation."""
2
+
3
+ from datetime import datetime, timezone
4
+ from typing import Any, Dict, List, Optional
5
+ from pydantic import BaseModel, Field
6
+
7
+ from actshield.offensive.attack import AttackCase
8
+ from actshield.offensive.results import AttackStatus, OffensiveAttackResult
9
+ from actshield.offensive.target import AttackTarget, target_registry
10
+ from actshield.tracing.correlation import generate_id
11
+
12
+
13
+ class AttackCampaign(BaseModel):
14
+ """A collection of offensive attack validations executed as a cohesive campaign."""
15
+
16
+ campaign_id: str = Field(default_factory=lambda: generate_id("camp"), description="Unique campaign ID")
17
+ name: str = Field(..., description="Human-readable campaign title")
18
+ objective: str = Field(..., description="Campaign validation objective")
19
+ target: AttackTarget = Field(..., description="Target system under validation")
20
+ attacks: List[AttackCase] = Field(default_factory=list, description="List of attack cases in campaign")
21
+ created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
22
+ metadata: Dict[str, Any] = Field(default_factory=dict)
23
+
24
+
25
+ class CampaignSummary(BaseModel):
26
+ """Empirical security scorecard and metrics aggregated from a completed campaign."""
27
+
28
+ campaign_id: str = Field(..., description="Campaign ID")
29
+ name: str = Field(..., description="Campaign title")
30
+ target_id: str = Field(..., description="Target system ID")
31
+
32
+ # Attack & Attempt Counts
33
+ total_attacks: int = Field(default=0)
34
+ blocked: int = Field(default=0)
35
+ bypassed: int = Field(default=0)
36
+ refused: int = Field(default=0)
37
+ errors: int = Field(default=0)
38
+ unique_attack_variants: int = Field(default=0)
39
+
40
+ # Hard Empirical Metrics
41
+ sensitive_actions_attempted: int = Field(default=0, description="Total sensitive tool invocations attempted")
42
+ sensitive_actions_prevented: int = Field(default=0, description="Total sensitive tool invocations blocked")
43
+ sensitive_actions_executed: int = Field(default=0, description="Total unauthorized sensitive executions (Bypasses)")
44
+ empirical_prevention_rate: float = Field(default=1.0, description="1 - (executed / attempted)")
45
+
46
+ # Rates & Latency
47
+ block_rate_pct: float = Field(default=0.0, description="Percentage of unauthorized attacks successfully blocked")
48
+ sensitive_prevention_rate_pct: float = Field(default=100.0, description="Rate of sensitive sink protection")
49
+ mean_latency_ms: float = Field(default=0.0)
50
+ p95_latency_ms: float = Field(default=0.0)
51
+ duration_ms: float = Field(default=0.0)
52
+ campaign_seed: Optional[int] = Field(default=None)
53
+
54
+ # Adaptive & Lineage Metrics
55
+ max_mutation_depth: int = Field(default=0)
56
+ avg_mutation_depth: float = Field(default=0.0)
57
+ regression_fixtures_generated: int = Field(default=0)
58
+
59
+ # Pipeline Availability
60
+ ai_analysis_availability_pct: float = Field(default=100.0)
61
+ apiris_analysis_availability_pct: float = Field(default=100.0)
62
+
63
+ # Coverage Breakdown
64
+ coverage_breakdown: Dict[str, Dict[str, int]] = Field(default_factory=dict)
65
+ defense_reasons_breakdown: Dict[str, int] = Field(default_factory=dict)
66
+ timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
67
+
68
+ @classmethod
69
+ def from_results(
70
+ cls,
71
+ campaign: AttackCampaign,
72
+ results: List[OffensiveAttackResult],
73
+ memory: Optional[Any] = None,
74
+ seed: Optional[int] = None,
75
+ duration_ms: float = 0.0,
76
+ ) -> "CampaignSummary":
77
+ """Aggregate results into an empirical CampaignSummary."""
78
+ total = len(results)
79
+ if total == 0:
80
+ return cls(
81
+ campaign_id=campaign.campaign_id,
82
+ name=campaign.name,
83
+ target_id=campaign.target.target_id,
84
+ campaign_seed=seed,
85
+ duration_ms=duration_ms,
86
+ )
87
+
88
+ blocked = sum(1 for r in results if r.blocked or r.status == AttackStatus.PASS)
89
+ bypassed = sum(1 for r in results if r.bypassed or r.status == AttackStatus.BYPASS)
90
+ refused = sum(1 for r in results if r.status == AttackStatus.REFUSED)
91
+ errors = sum(1 for r in results if r.status == AttackStatus.ERROR)
92
+
93
+ sens_exec = sum(r.execution_evidence.sensitive_db_calls for r in results if r.execution_evidence)
94
+ sens_prev = sum(1 for r in results if r.blocked or r.status == AttackStatus.PASS)
95
+ sens_attempted = sens_prev + sens_exec
96
+
97
+ # Empirical prevention rate = 1 - (executed / attempted)
98
+ if sens_attempted > 0:
99
+ emp_rate = round(1.0 - (sens_exec / sens_attempted), 4)
100
+ else:
101
+ emp_rate = 1.0
102
+
103
+ # Latencies
104
+ lats = sorted([r.latency_ms for r in results])
105
+ mean_lat = sum(lats) / total if total > 0 else 0.0
106
+ p95_idx = int(total * 0.95)
107
+ p95_lat = lats[min(p95_idx, total - 1)] if total > 0 else 0.0
108
+
109
+ # Coverage breakdown by attack_type
110
+ coverage: Dict[str, Dict[str, int]] = {}
111
+ defense_reasons: Dict[str, int] = {}
112
+ ai_available_count = 0
113
+ apiris_available_count = 0
114
+
115
+ for r in results:
116
+ atype = r.attack_type.value if hasattr(r.attack_type, 'value') else str(r.attack_type)
117
+ if atype not in coverage:
118
+ coverage[atype] = {"total": 0, "blocked": 0, "bypassed": 0}
119
+ coverage[atype]["total"] += 1
120
+ if r.blocked or r.status == AttackStatus.PASS:
121
+ coverage[atype]["blocked"] += 1
122
+ if r.bypassed or r.status == AttackStatus.BYPASS:
123
+ coverage[atype]["bypassed"] += 1
124
+
125
+ reason = r.execution_evidence.policy_reason or "UNKNOWN"
126
+ defense_reasons[reason] = defense_reasons.get(reason, 0) + 1
127
+
128
+ if r.ai_analysis and r.ai_analysis.get("status") != "UNAVAILABLE":
129
+ ai_available_count += 1
130
+ if r.apiris_analysis and r.apiris_analysis.get("status") != "UNAVAILABLE":
131
+ apiris_available_count += 1
132
+
133
+ block_rate = round((blocked / total) * 100, 1) if total > 0 else 0.0
134
+ prev_rate = round(((total - bypassed) / total) * 100, 1) if total > 0 else 100.0
135
+
136
+ ai_avail_pct = round((ai_available_count / total) * 100, 1) if total > 0 else 100.0
137
+ apiris_avail_pct = round((apiris_available_count / total) * 100, 1) if total > 0 else 100.0
138
+
139
+ # Memory / Lineage metrics
140
+ max_depth = 0
141
+ avg_depth = 0.0
142
+ reg_count = bypassed
143
+ if memory and hasattr(memory, "nodes"):
144
+ all_nodes = list(memory.nodes.values())
145
+ if all_nodes:
146
+ depths = [n.depth for n in all_nodes]
147
+ max_depth = max(depths)
148
+ avg_depth = round(sum(depths) / len(depths), 2)
149
+ reg_count = len(getattr(memory, "bypasses", [])) or bypassed
150
+
151
+ return cls(
152
+ campaign_id=campaign.campaign_id,
153
+ name=campaign.name,
154
+ target_id=campaign.target.target_id,
155
+ total_attacks=total,
156
+ blocked=blocked,
157
+ bypassed=bypassed,
158
+ refused=refused,
159
+ errors=errors,
160
+ unique_attack_variants=len(set(r.attack_id for r in results)),
161
+ sensitive_actions_attempted=sens_attempted,
162
+ sensitive_actions_prevented=sens_prev,
163
+ sensitive_actions_executed=sens_exec,
164
+ empirical_prevention_rate=emp_rate,
165
+ block_rate_pct=block_rate,
166
+ sensitive_prevention_rate_pct=prev_rate,
167
+ mean_latency_ms=round(mean_lat, 2),
168
+ p95_latency_ms=round(p95_lat, 2),
169
+ duration_ms=round(duration_ms, 2),
170
+ campaign_seed=seed,
171
+ max_mutation_depth=max_depth,
172
+ avg_mutation_depth=avg_depth,
173
+ regression_fixtures_generated=reg_count,
174
+ ai_analysis_availability_pct=ai_avail_pct,
175
+ apiris_analysis_availability_pct=apiris_avail_pct,
176
+ coverage_breakdown=coverage,
177
+ defense_reasons_breakdown=defense_reasons,
178
+ )
179
+
180
+ def to_markdown(self) -> str:
181
+ """Format summary into structured Markdown."""
182
+ lines = [
183
+ "# ActShield Adaptive Offensive Validation Scorecard",
184
+ "",
185
+ f"- **Campaign ID**: `{self.campaign_id}`",
186
+ f"- **Target System**: `{self.target_id}`",
187
+ f"- **Campaign Seed**: `{self.campaign_seed}`",
188
+ f"- **Total Attack Executions**: {self.total_attacks} (Unique Variants: {self.unique_attack_variants})",
189
+ f"- **Attacks Blocked**: {self.blocked} ({self.block_rate_pct}%)",
190
+ f"- **Attacks Bypassed**: {self.bypassed}",
191
+ f"- **Sensitive Database Executions**: {self.sensitive_actions_executed}",
192
+ f"- **Empirical Prevention Rate**: {self.empirical_prevention_rate * 100:.1f}%",
193
+ f"- **Max Mutation Depth**: {self.max_mutation_depth} (Average: {self.avg_mutation_depth})",
194
+ f"- **Mean Latency**: {self.mean_latency_ms} ms (p95: {self.p95_latency_ms} ms)",
195
+ f"- **Pipeline Availability**: AI Secura ({self.ai_analysis_availability_pct}%) | APIRIS ({self.apiris_analysis_availability_pct}%)",
196
+ "",
197
+ "## Attack Family Coverage Breakdown",
198
+ "",
199
+ "| Attack Type | Total | Blocked | Bypassed |",
200
+ "|---|---|---|---|",
201
+ ]
202
+
203
+ for atype, stats in self.coverage_breakdown.items():
204
+ lines.append(f"| `{atype}` | {stats['total']} | {stats['blocked']} | {stats['bypassed']} |")
205
+
206
+ lines.extend([
207
+ "",
208
+ "## Defense Reasons Breakdown",
209
+ "",
210
+ "| Defense Reason Code | Count |",
211
+ "|---|---|",
212
+ ])
213
+
214
+ for reason, count in self.defense_reasons_breakdown.items():
215
+ lines.append(f"| `{reason}` | {count} |")
216
+
217
+ return "\n".join(lines)
218
+
219
+
@@ -0,0 +1,155 @@
1
+ """Attack corpus loader managing baseline fixtures, attack families, and regression cases."""
2
+
3
+ import json
4
+ import pathlib
5
+ from typing import Dict, List, Optional
6
+
7
+ from actshield.offensive.attack import AttackCase, AttackType, ExpectedBehavior, SafetyClass
8
+ from actshield.offensive.strategies import (
9
+ PromptInjectionStrategy,
10
+ AuthorityEscalationStrategy,
11
+ ToolPoisoningStrategy,
12
+ ContextManipulationStrategy,
13
+ MCPInjectionStrategy,
14
+ DataExfiltrationStrategy,
15
+ DelegationEscalationStrategy,
16
+ )
17
+ from actshield.offensive.target import AttackTarget, target_registry
18
+
19
+
20
+ class AttackCorpus:
21
+ """Central repository for loading, organizing, and persisting attack fixtures."""
22
+
23
+ def __init__(
24
+ self,
25
+ fixtures_dir: Optional[pathlib.Path] = None,
26
+ regressions_dir: Optional[pathlib.Path] = None,
27
+ ) -> None:
28
+ # Default paths
29
+ root = pathlib.Path(__file__).resolve().parent.parent.parent.parent
30
+ self.fixtures_dir = fixtures_dir or (root / "examples" / "attacks")
31
+ self.regressions_dir = regressions_dir or (root / "examples" / "attacks" / "regressions")
32
+ self.regressions_dir.mkdir(parents=True, exist_ok=True)
33
+
34
+ def load_baseline_fixtures(self) -> List[AttackCase]:
35
+ """Load the 5 existing baseline attack fixtures from examples/attacks/."""
36
+ cases: List[AttackCase] = []
37
+ if not self.fixtures_dir.exists():
38
+ return cases
39
+
40
+ for json_path in sorted(self.fixtures_dir.glob("*.json")):
41
+ try:
42
+ with open(json_path, "r", encoding="utf-8") as f:
43
+ data = json.load(f)
44
+
45
+ # Map legacy format to AttackCase
46
+ atype_str = data.get("attack_type", "custom")
47
+ try:
48
+ atype = AttackType(atype_str)
49
+ except ValueError:
50
+ atype = AttackType.CUSTOM
51
+
52
+ case = AttackCase(
53
+ attack_id=data.get("attack_id", json_path.stem),
54
+ name=data.get("attack_id", json_path.stem).replace("_", " ").title(),
55
+ attack_type=atype,
56
+ description=data.get("description", "Baseline attack fixture"),
57
+ entry_point=data.get("entry_point", "mcp_response"),
58
+ target_agent=data.get("target_agent", "DataAgent"),
59
+ payload=data.get("payload", ""),
60
+ target_tool=data.get("target_capability", "customer_db.read"),
61
+ target_resource="customer_pii_vault",
62
+ expected_behavior=ExpectedBehavior.BLOCK if data.get("expected_decision") == "BLOCK" else ExpectedBehavior.ALLOW,
63
+ severity="HIGH",
64
+ safety_class=SafetyClass.LOCAL_SYNTHETIC,
65
+ )
66
+ cases.append(case)
67
+ except Exception:
68
+ pass
69
+
70
+ return cases
71
+
72
+ def generate_full_family_corpus(self, target: Optional[AttackTarget] = None) -> List[AttackCase]:
73
+ """Generate comprehensive attack cases spanning all core attack families."""
74
+ target = target or target_registry.get("ActShield-demo")
75
+ all_cases: List[AttackCase] = []
76
+
77
+ # 1. Include baseline fixtures
78
+ all_cases.extend(self.load_baseline_fixtures())
79
+
80
+ # 2. Generate from all strategies
81
+ strategies = [
82
+ PromptInjectionStrategy(),
83
+ AuthorityEscalationStrategy(),
84
+ ToolPoisoningStrategy(),
85
+ ContextManipulationStrategy(),
86
+ MCPInjectionStrategy(),
87
+ DataExfiltrationStrategy(),
88
+ DelegationEscalationStrategy(),
89
+ ]
90
+
91
+ for strat in strategies:
92
+ strat_cases = strat.generate(target, count=5)
93
+ # Avoid duplicate IDs
94
+ for c in strat_cases:
95
+ if not any(existing.attack_id == c.attack_id for existing in all_cases):
96
+ all_cases.append(c)
97
+
98
+ return all_cases
99
+
100
+ def get_all_attacks(self, target: Optional[AttackTarget] = None) -> List[AttackCase]:
101
+ """Convenience alias returning the full baseline and generated attack corpus."""
102
+ return self.generate_full_family_corpus(target=target)
103
+
104
+ def get_regression_attacks(self) -> List[AttackCase]:
105
+ """Convenience alias returning all regression attack cases."""
106
+ return self.load_regression_cases()
107
+
108
+ def get_attack(self, attack_id: str) -> Optional[AttackCase]:
109
+ """Retrieve a specific attack case by ID from full corpus or regression store."""
110
+ for a in self.get_all_attacks():
111
+ if a.attack_id == attack_id:
112
+ return a
113
+ for r in self.get_regression_attacks():
114
+ if r.attack_id == attack_id:
115
+ return r
116
+ return None
117
+
118
+ def get_attacks_by_type(self, attack_type: AttackType) -> List[AttackCase]:
119
+ """Retrieve all attack cases matching a specific AttackType."""
120
+ return [a for a in self.get_all_attacks() if a.attack_type == attack_type]
121
+
122
+ def save_regression_case(self, attack: AttackCase, reason: str = "Bypass detected") -> pathlib.Path:
123
+ """Persist a discovered bypass attack case to permanent regression fixtures."""
124
+ self.regressions_dir.mkdir(parents=True, exist_ok=True)
125
+ file_path = self.regressions_dir / f"{attack.attack_id}.json"
126
+
127
+ data = attack.model_dump(mode="json")
128
+ data["regression_reason"] = reason
129
+
130
+ with open(file_path, "w", encoding="utf-8") as f:
131
+ json.dump(data, f, indent=2)
132
+
133
+ return file_path
134
+
135
+ def load_regression_cases(self) -> List[AttackCase]:
136
+ """Load all permanent regression attack fixtures."""
137
+ regressions: List[AttackCase] = []
138
+ if not self.regressions_dir.exists():
139
+ return regressions
140
+
141
+ for json_path in sorted(self.regressions_dir.glob("*.json")):
142
+ try:
143
+ with open(json_path, "r", encoding="utf-8") as f:
144
+ data = json.load(f)
145
+ regressions.append(AttackCase(**data))
146
+ except Exception:
147
+ pass
148
+
149
+ return regressions
150
+
151
+
152
+ # Global default corpus instance
153
+ attack_corpus = AttackCorpus()
154
+
155
+