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
actshield/config.py ADDED
@@ -0,0 +1,88 @@
1
+ """Configuration module for ActShield SDK."""
2
+
3
+ import re
4
+ from typing import Any, Callable, Dict, List, Optional, Pattern
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ DEFAULT_SECRET_PATTERNS: List[Pattern[str]] = [
9
+ re.compile(r"(?i)(bearer\s+)[a-zA-Z0-9_\-\.]{15,}"),
10
+ re.compile(r"(?i)(api[_\-]?key|secret|token|password|passwd|auth[_\-]?token)\s*[:=]\s*['\"]?([a-zA-Z0-9_\-\.]{8,})['\"]?"),
11
+ re.compile(r"(?i)(sk-[a-zA-Z0-9]{20,})"),
12
+ re.compile(r"(?i)(ghp_[a-zA-Z0-9]{36})"),
13
+ re.compile(r"(?i)(xox[baprs]-[a-zA-Z0-9\-]{10,})"),
14
+ ]
15
+
16
+ SENSITIVE_KEY_NAMES = {
17
+ "api_key", "apikey", "secret", "password", "passwd", "token",
18
+ "access_token", "auth_token", "private_key", "secret_key",
19
+ "credentials", "authorization"
20
+ }
21
+
22
+
23
+ def default_redact_function(data: Any) -> Any:
24
+ """Recursively redacts secrets and sensitive key values."""
25
+ if isinstance(data, str):
26
+ result = data
27
+ for pattern in DEFAULT_SECRET_PATTERNS:
28
+ result = pattern.sub(r"\1[REDACTED]", result)
29
+ return result
30
+ elif isinstance(data, dict):
31
+ redacted_dict: Dict[str, Any] = {}
32
+ for k, v in data.items():
33
+ if str(k).lower() in SENSITIVE_KEY_NAMES:
34
+ redacted_dict[k] = "[REDACTED]"
35
+ else:
36
+ redacted_dict[k] = default_redact_function(v)
37
+ return redacted_dict
38
+ elif isinstance(data, list):
39
+ return [default_redact_function(item) for item in data]
40
+ elif isinstance(data, tuple):
41
+ return tuple(default_redact_function(item) for item in data)
42
+ return data
43
+
44
+
45
+ class ActShieldConfig(BaseModel):
46
+ """Runtime configuration for actshield."""
47
+
48
+ app_name: str = Field(default="ActShield", description="Application or service name")
49
+ environment: str = Field(default="production", description="Runtime environment (development, staging, production)")
50
+ enforcement_mode: str = Field(default="STRICT", description="Control plane enforcement mode (STRICT, MONITOR, AUDIT)")
51
+ enforce_monotonic_delegation: bool = Field(
52
+
53
+ default=True,
54
+ description="Whether an agent is strictly forbidden from delegating authority it was not granted"
55
+ )
56
+ block_tainted_sink_access: bool = Field(
57
+ default=True,
58
+ description="Whether tainted untrusted context is blocked from invoking critical sensitive tools"
59
+ )
60
+ default_risk_threshold: float = Field(
61
+ default=0.7,
62
+ ge=0.0,
63
+ le=1.0,
64
+ description="Risk score above which actions are blocked or require human approval"
65
+ )
66
+ redact_secrets: bool = Field(
67
+ default=True,
68
+ description="Whether to sanitize secret patterns from emitted events, context, and trace payloads"
69
+ )
70
+ custom_redactor: Optional[Callable[[Any], Any]] = Field(
71
+ default=None,
72
+ description="Custom redaction callable if specified"
73
+ )
74
+
75
+ def sanitize(self, data: Any) -> Any:
76
+ """Sanitize data using configured redactor."""
77
+ if not self.redact_secrets:
78
+ return data
79
+ if self.custom_redactor:
80
+ return self.custom_redactor(data)
81
+ return default_redact_function(data)
82
+
83
+
84
+ # Backward compatibility alias
85
+ AgentGuardConfig = ActShieldConfig
86
+
87
+
88
+
@@ -0,0 +1,14 @@
1
+ """Context, Provenance, and Taint tracking package."""
2
+
3
+ from actshield.context.context import Context
4
+ from actshield.context.provenance import ContextSource, Provenance, ProvenanceHop
5
+ from actshield.context.taint import TaintState
6
+
7
+ __all__ = [
8
+ "Context",
9
+ "ContextSource",
10
+ "Provenance",
11
+ "ProvenanceHop",
12
+ "TaintState",
13
+ ]
14
+
@@ -0,0 +1,161 @@
1
+ """Context container tracking data lineage, provenance, and taint state."""
2
+
3
+ from datetime import datetime, timezone
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
5
+ from pydantic import BaseModel, Field
6
+
7
+ from actshield.context.provenance import ContextSource, ContextTrustLevel, Provenance
8
+ from actshield.context.taint import TaintState
9
+ from actshield.tracing.correlation import generate_id
10
+ from actshield.tracing.events import EventType
11
+
12
+ if TYPE_CHECKING:
13
+ from actshield.client import ActShield
14
+
15
+
16
+ class Context(BaseModel):
17
+ """Context and data lineage container tracked by actshield."""
18
+
19
+ context_id: str = Field(default_factory=lambda: generate_id("ctx"), description="Unique context ID")
20
+ data: Any = Field(default=None, description="Payload data content or summary")
21
+ source: ContextSource = Field(default=ContextSource.USER, description="Original source")
22
+ source_type: str = Field(default="text", description="MIME type or semantic type")
23
+ source_uri: Optional[str] = Field(default=None, description="URI or locator of context source")
24
+ trust_level: str = Field(default="trusted", description="Trust level assigned at ingestion ('trusted', 'untrusted', 'verified')")
25
+ taint_state: TaintState = Field(default=TaintState.CLEAN, description="Current taint classification ('CLEAN', 'UNTRUSTED', 'TAINTED', 'UNKNOWN')")
26
+
27
+ # Origin & Lineage
28
+ originating_event_id: Optional[str] = Field(default=None, description="Originating event ID")
29
+ originating_agent_id: Optional[str] = Field(default=None, description="Agent that ingested or produced this")
30
+ current_agent_id: Optional[str] = Field(default=None, description="Agent currently holding this context")
31
+ parent_context_id: Optional[str] = Field(default=None, description="Parent context if derived")
32
+
33
+ provenance: Provenance = Field(
34
+ default_factory=Provenance,
35
+ description="Chronological provenance graph"
36
+ )
37
+
38
+ timestamp: datetime = Field(
39
+ default_factory=lambda: datetime.now(timezone.utc),
40
+ description="Creation timestamp"
41
+ )
42
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Custom metadata tags")
43
+
44
+ def propagate(
45
+ self,
46
+ to_agent_id: str,
47
+ action: str = "propagated",
48
+ new_data: Any = None,
49
+ taint_override: Optional[TaintState] = None,
50
+ guard: Optional["ActShield"] = None,
51
+ ) -> "Context":
52
+ """Propagate or transform this context to another agent, recording the lineage hop.
53
+
54
+ Taint is strictly preserved across agent hops unless explicitly sanitized.
55
+ """
56
+ new_taint = taint_override or self.taint_state
57
+ new_provenance = self.provenance.model_copy(deep=True)
58
+
59
+ derived_ctx = Context(
60
+ context_id=generate_id("ctx"),
61
+ data=new_data if new_data is not None else self.data,
62
+ source=self.source,
63
+ source_type=self.source_type,
64
+ source_uri=self.source_uri,
65
+ trust_level=self.trust_level,
66
+ taint_state=new_taint,
67
+ originating_event_id=self.originating_event_id,
68
+ originating_agent_id=self.originating_agent_id,
69
+ current_agent_id=to_agent_id,
70
+ parent_context_id=self.context_id,
71
+ provenance=new_provenance,
72
+ metadata=dict(self.metadata),
73
+ )
74
+
75
+ event_id = None
76
+ if guard is not None:
77
+ guard.context_registry[derived_ctx.context_id] = derived_ctx
78
+ evt = guard.emit_event(
79
+ event_type=EventType.CONTEXT_PROPAGATED,
80
+ context_id=derived_ctx.context_id,
81
+ agent_id=to_agent_id,
82
+ parent_agent_id=self.current_agent_id,
83
+ payload={
84
+ "parent_context_id": self.context_id,
85
+ "taint_state": derived_ctx.taint_state.value,
86
+ "action": action,
87
+ }
88
+ )
89
+ event_id = evt.event_id
90
+
91
+ derived_ctx.provenance.record_hop(
92
+ agent_id=to_agent_id,
93
+ action=action,
94
+ event_id=event_id,
95
+ details={"from_agent": self.current_agent_id, "taint": new_taint.value},
96
+ )
97
+
98
+ return derived_ctx
99
+
100
+ def sanitize(
101
+ self,
102
+ sanitizer_name: str,
103
+ reason: str,
104
+ transformed_data: Any = None,
105
+ new_taint: TaintState = TaintState.CLEAN,
106
+ guard: Optional["ActShield"] = None,
107
+ ) -> "Context":
108
+ """Perform an explicit, auditable sanitization operation on this context artifact."""
109
+ previous_taint = self.taint_state.value
110
+ new_provenance = self.provenance.model_copy(deep=True)
111
+
112
+ sanitized_ctx = Context(
113
+ context_id=generate_id("ctx"),
114
+ data=transformed_data if transformed_data is not None else self.data,
115
+ source=self.source,
116
+ source_type=f"sanitized:{self.source_type}",
117
+ source_uri=self.source_uri,
118
+ trust_level="verified",
119
+ taint_state=new_taint,
120
+ originating_event_id=self.originating_event_id,
121
+ originating_agent_id=self.originating_agent_id,
122
+ current_agent_id=self.current_agent_id,
123
+ parent_context_id=self.context_id,
124
+ provenance=new_provenance,
125
+ metadata={
126
+ **self.metadata,
127
+ "sanitized_by": sanitizer_name,
128
+ "sanitization_reason": reason,
129
+ },
130
+ )
131
+
132
+ event_id = None
133
+ if guard is not None:
134
+ guard.context_registry[sanitized_ctx.context_id] = sanitized_ctx
135
+ evt = guard.emit_event(
136
+ event_type=EventType.CONTEXT_SANITIZED,
137
+ context_id=sanitized_ctx.context_id,
138
+ agent_id=self.current_agent_id,
139
+ payload={
140
+ "source_context_id": self.context_id,
141
+ "resulting_context_id": sanitized_ctx.context_id,
142
+ "sanitizer_name": sanitizer_name,
143
+ "reason": reason,
144
+ "previous_taint": previous_taint,
145
+ "new_taint": new_taint.value,
146
+ }
147
+ )
148
+ event_id = evt.event_id
149
+
150
+ sanitized_ctx.provenance.record_sanitization(
151
+ sanitizer_name=sanitizer_name,
152
+ reason=reason,
153
+ previous_taint=previous_taint,
154
+ new_taint=new_taint.value,
155
+ event_id=event_id,
156
+ details={"source_context_id": self.context_id},
157
+ )
158
+
159
+ return sanitized_ctx
160
+
161
+
@@ -0,0 +1,108 @@
1
+ """Data provenance and lineage tracking models."""
2
+
3
+ from datetime import datetime, timezone
4
+ from enum import Enum
5
+ from typing import Any, Dict, List, Optional
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class ContextSource(str, Enum):
10
+ """Origin sources for context ingested or generated by agents."""
11
+ USER = "user"
12
+ EXTERNAL_MCP = "external_mcp"
13
+ SYSTEM = "system"
14
+ TOOL_OUTPUT = "tool_output"
15
+ AGENT_GENERATED = "agent_generated"
16
+ EXTERNAL_API = "external_api"
17
+ DATABASE = "database"
18
+ FILE_SYSTEM = "file_system"
19
+
20
+
21
+ class ContextTrustLevel(str, Enum):
22
+ """Source trust classification (orthogonal to data taint)."""
23
+ TRUSTED = "trusted"
24
+ UNTRUSTED = "untrusted"
25
+ VERIFIED = "verified"
26
+ UNKNOWN = "unknown"
27
+
28
+
29
+ class ProvenanceHop(BaseModel):
30
+ """An individual hop or transformation in the lifecycle of a context artifact."""
31
+ agent_id: Optional[str] = Field(default=None, description="Agent performing the hop/transformation")
32
+ action: str = Field(..., description="Action taken (e.g. 'received', 'transformed', 'delegated', 'injected')")
33
+ timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
34
+ event_id: Optional[str] = Field(default=None, description="Event ID correlated with this hop")
35
+ details: Dict[str, Any] = Field(default_factory=dict)
36
+
37
+
38
+ class SanitizationRecord(BaseModel):
39
+ """Auditable record of an explicit context sanitization transformation."""
40
+ sanitizer_name: str
41
+ reason: str
42
+ previous_taint: str
43
+ new_taint: str
44
+ timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
45
+ event_id: Optional[str] = None
46
+ details: Dict[str, Any] = Field(default_factory=dict)
47
+
48
+
49
+ class Provenance(BaseModel):
50
+ """Complete causal history of a piece of context or data."""
51
+
52
+ source: ContextSource = Field(default=ContextSource.USER, description="Original source type")
53
+ source_uri: Optional[str] = Field(default=None, description="URI or identifier of source (e.g. mcp://weather-server, /var/log)")
54
+ trust_level: str = Field(default="trusted", description="Trust level of the origin source")
55
+ originating_event_id: Optional[str] = Field(default=None, description="Initial event ID where context appeared")
56
+ originating_agent_id: Optional[str] = Field(default=None, description="Initial agent that ingested or produced this context")
57
+ originating_timestamp: datetime = Field(
58
+ default_factory=lambda: datetime.now(timezone.utc),
59
+ description="Timestamp of origin"
60
+ )
61
+ hops: List[ProvenanceHop] = Field(
62
+ default_factory=list,
63
+ description="Chronological record of agents and actions touching this context"
64
+ )
65
+ sanitizations: List[SanitizationRecord] = Field(
66
+ default_factory=list,
67
+ description="Chronological record of auditable sanitizations"
68
+ )
69
+
70
+ def record_hop(
71
+ self,
72
+ agent_id: Optional[str],
73
+ action: str,
74
+ event_id: Optional[str] = None,
75
+ details: Optional[Dict[str, Any]] = None,
76
+ ) -> None:
77
+ """Record an agent transformation or propagation step."""
78
+ self.hops.append(
79
+ ProvenanceHop(
80
+ agent_id=agent_id,
81
+ action=action,
82
+ timestamp=datetime.now(timezone.utc),
83
+ event_id=event_id,
84
+ details=details or {},
85
+ )
86
+ )
87
+
88
+ def record_sanitization(
89
+ self,
90
+ sanitizer_name: str,
91
+ reason: str,
92
+ previous_taint: str,
93
+ new_taint: str,
94
+ event_id: Optional[str] = None,
95
+ details: Optional[Dict[str, Any]] = None,
96
+ ) -> None:
97
+ """Record an explicit sanitization event."""
98
+ self.sanitizations.append(
99
+ SanitizationRecord(
100
+ sanitizer_name=sanitizer_name,
101
+ reason=reason,
102
+ previous_taint=previous_taint,
103
+ new_taint=new_taint,
104
+ timestamp=datetime.now(timezone.utc),
105
+ event_id=event_id,
106
+ details=details or {},
107
+ )
108
+ )
@@ -0,0 +1,48 @@
1
+ """Taint tracking models and propagation rules."""
2
+
3
+ from enum import Enum
4
+ from typing import List, Optional
5
+
6
+
7
+ class TaintState(str, Enum):
8
+ """Possible taint states for context and data artifacts.
9
+
10
+ Separates origin trust from data taint:
11
+ - CLEAN / TRUSTED: Verified safe content without malicious or unverified directives.
12
+ - UNTRUSTED: Originating from an unverified source, but containing clean/benign data.
13
+ - TAINTED: Ingested or influenced by unverified/adversarial prompt injections or unauthorized directives.
14
+ - UNKNOWN: Unclassified context.
15
+ """
16
+ CLEAN = "CLEAN"
17
+ TRUSTED = "TRUSTED" # Backward compatibility alias for CLEAN
18
+ UNTRUSTED = "UNTRUSTED"
19
+ TAINTED = "TAINTED"
20
+ UNKNOWN = "UNKNOWN"
21
+
22
+ @property
23
+ def is_safe(self) -> bool:
24
+ """Indicates whether this taint level can flow to sensitive sinks without restrictions."""
25
+ return self in (TaintState.CLEAN, TaintState.TRUSTED)
26
+
27
+ @property
28
+ def is_tainted(self) -> bool:
29
+ """Indicates whether the data is flagged as carrying untrusted/injected payload."""
30
+ return self == TaintState.TAINTED
31
+
32
+ @classmethod
33
+ def combine(cls, *states: "TaintState") -> "TaintState":
34
+ """Combine multiple taint states into a single conservative worst-case taint state."""
35
+ state_list = [s for s in states if s is not None]
36
+ if not state_list:
37
+ return cls.UNKNOWN
38
+
39
+ if cls.TAINTED in state_list:
40
+ return cls.TAINTED
41
+ if cls.UNTRUSTED in state_list:
42
+ return cls.UNTRUSTED
43
+ if cls.UNKNOWN in state_list:
44
+ return cls.UNKNOWN
45
+ if cls.TRUSTED in state_list:
46
+ return cls.TRUSTED
47
+ return cls.CLEAN
48
+
@@ -0,0 +1,74 @@
1
+ """Security enforcement decision models."""
2
+
3
+ from datetime import datetime, timezone
4
+ from enum import Enum
5
+ from typing import Any, Dict, List, Optional
6
+ from pydantic import BaseModel, Field
7
+
8
+ from actshield.risk.models import RiskFactorBreakdown, RiskLevel
9
+ from actshield.tracing.correlation import generate_id
10
+
11
+
12
+ class DecisionAction(str, Enum):
13
+ """Deterministic security enforcement actions."""
14
+ ALLOW = "ALLOW"
15
+ MONITOR = "MONITOR"
16
+ HUMAN_APPROVAL = "HUMAN_APPROVAL"
17
+ QUARANTINE = "QUARANTINE"
18
+ BLOCK = "BLOCK"
19
+ REVOKE = "REVOKE"
20
+
21
+
22
+ class SecurityDecision(BaseModel):
23
+ """A deterministic security decision emitted after policy evaluation."""
24
+
25
+ decision_id: str = Field(default_factory=lambda: generate_id("dec"), description="Unique decision ID")
26
+ action: DecisionAction = Field(..., description="Deterministic enforcement action")
27
+ risk_level: RiskLevel = Field(default=RiskLevel.LOW, description="Assessed risk level")
28
+ risk_score: float = Field(default=0.0, ge=0.0, le=1.0, description="Risk score between 0.0 and 1.0")
29
+ reason_code: str = Field(..., description="Machine-readable reason code")
30
+ explanation: str = Field(..., description="Human-readable explanation of why the decision was rendered")
31
+ trace_id: str = Field(..., description="Trace ID for causal correlation")
32
+ policy_id: Optional[str] = Field(default=None, description="ID of the policy rule that determined this decision")
33
+
34
+ # Causal and structured evaluation details
35
+ evidence_references: List[str] = Field(
36
+ default_factory=list,
37
+ description="References to event_ids, context_ids, or delegation_ids establishing causal evidence"
38
+ )
39
+ influencing_context_ids: List[str] = Field(
40
+ default_factory=list,
41
+ description="IDs of context artifacts that influenced this execution request"
42
+ )
43
+ authority_containment: Dict[str, Any] = Field(
44
+ default_factory=dict,
45
+ description="Structured validation of requested capability vs delegated authority"
46
+ )
47
+ intent_alignment: Dict[str, Any] = Field(
48
+ default_factory=dict,
49
+ description="Structured evaluation of original user intent vs requested action"
50
+ )
51
+ risk_breakdown: Optional[RiskFactorBreakdown] = Field(
52
+ default=None,
53
+ description="Explainable breakdown of risk factor dimensions"
54
+ )
55
+ structured_explanation: Dict[str, Any] = Field(
56
+ default_factory=dict,
57
+ description="Structured breakdown for auditing and SIEM reporting"
58
+ )
59
+
60
+ evaluated_at: datetime = Field(
61
+ default_factory=lambda: datetime.now(timezone.utc),
62
+ description="UTC timestamp of evaluation"
63
+ )
64
+
65
+ @property
66
+ def is_allowed(self) -> bool:
67
+ """Helper to test if execution is permitted to proceed immediately."""
68
+ return self.action in (DecisionAction.ALLOW, DecisionAction.MONITOR)
69
+
70
+ @property
71
+ def is_blocked(self) -> bool:
72
+ """Helper to test if execution was blocked or quarantined."""
73
+ return self.action in (DecisionAction.BLOCK, DecisionAction.QUARANTINE, DecisionAction.REVOKE)
74
+
@@ -0,0 +1,11 @@
1
+ """Delegation and Authority package."""
2
+
3
+ from actshield.delegation.authority import AuthorityGrant
4
+ from actshield.delegation.delegation import Delegation, DelegationScope
5
+
6
+ __all__ = [
7
+ "AuthorityGrant",
8
+ "Delegation",
9
+ "DelegationScope",
10
+ ]
11
+
@@ -0,0 +1,63 @@
1
+ """Authority granting and monotonic delegation validation."""
2
+
3
+ from datetime import datetime, timezone
4
+ from typing import Any, Dict, List, Optional, Set
5
+ from pydantic import BaseModel, Field
6
+
7
+ from actshield.tracing.correlation import generate_id
8
+
9
+
10
+ class AuthorityGrant(BaseModel):
11
+ """Explicit grant of capabilities from a delegator to a delegate."""
12
+
13
+ grant_id: str = Field(default_factory=lambda: generate_id("grnt"))
14
+ delegator_agent_id: str = Field(..., description="Agent ID yielding authority")
15
+ delegate_agent_id: str = Field(..., description="Agent ID receiving authority")
16
+ granted_capabilities: List[str] = Field(
17
+ default_factory=list,
18
+ description="Explicit list of capability names granted"
19
+ )
20
+ constraints: Dict[str, Any] = Field(
21
+ default_factory=dict,
22
+ description="Optional execution constraints (e.g. rate_limit, max_cost, valid_until)"
23
+ )
24
+ created_at: datetime = Field(
25
+ default_factory=lambda: datetime.now(timezone.utc),
26
+ description="UTC grant creation timestamp"
27
+ )
28
+
29
+ def contains_capability(self, capability_name: str) -> bool:
30
+ """Verify if a specific capability is covered by this grant."""
31
+ if "*" in self.granted_capabilities or capability_name in self.granted_capabilities:
32
+ return True
33
+ for cap in self.granted_capabilities:
34
+ if cap.endswith(".*") and capability_name.startswith(cap[:-2]):
35
+ return True
36
+ return False
37
+
38
+ def validate_monotonic_reduction(
39
+ self,
40
+ delegator_available_capabilities: List[str],
41
+ strict: bool = True,
42
+ ) -> bool:
43
+ """Enforce that granted capabilities are a strict subset of delegator available capabilities."""
44
+ if not strict:
45
+ return True
46
+ if "*" in delegator_available_capabilities:
47
+ return True
48
+
49
+ delegator_set: Set[str] = set(delegator_available_capabilities)
50
+ for requested_cap in self.granted_capabilities:
51
+ if requested_cap == "*":
52
+ return False # Cannot grant wildcard if delegator doesn't have wildcard
53
+ if requested_cap not in delegator_set:
54
+ # Check wildcard prefix
55
+ matched = False
56
+ for d_cap in delegator_set:
57
+ if d_cap.endswith(".*") and requested_cap.startswith(d_cap[:-2]):
58
+ matched = True
59
+ break
60
+ if not matched:
61
+ return False
62
+ return True
63
+