decodingtrust-agent-sdk 0.1.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 (374) hide show
  1. agent/__init__.py +30 -0
  2. agent/claudesdk/__init__.py +8 -0
  3. agent/claudesdk/example.py +221 -0
  4. agent/claudesdk/src/__init__.py +8 -0
  5. agent/claudesdk/src/agent.py +400 -0
  6. agent/claudesdk/src/mcp_proxy.py +409 -0
  7. agent/claudesdk/src/utils.py +420 -0
  8. agent/googleadk/__init__.py +15 -0
  9. agent/googleadk/example.py +237 -0
  10. agent/googleadk/src/__init__.py +12 -0
  11. agent/googleadk/src/agent.py +401 -0
  12. agent/googleadk/src/mcp_wrapper.py +163 -0
  13. agent/googleadk/src/utils.py +602 -0
  14. agent/langchain/__init__.py +8 -0
  15. agent/langchain/example.py +213 -0
  16. agent/langchain/src/__init__.py +8 -0
  17. agent/langchain/src/agent.py +645 -0
  18. agent/langchain/src/utils.py +433 -0
  19. agent/openaisdk/__init__.py +17 -0
  20. agent/openaisdk/example.py +228 -0
  21. agent/openaisdk/src/__init__.py +12 -0
  22. agent/openaisdk/src/agent.py +491 -0
  23. agent/openaisdk/src/agent_wrapper.py +143 -0
  24. agent/openaisdk/src/mcp_wrapper.py +395 -0
  25. agent/openaisdk/src/utils.py +493 -0
  26. agent/openclaw/__init__.py +10 -0
  27. agent/openclaw/example.py +251 -0
  28. agent/openclaw/src/__init__.py +14 -0
  29. agent/openclaw/src/agent.py +930 -0
  30. agent/openclaw/src/helpers/__init__.py +1 -0
  31. agent/openclaw/src/helpers/auth_helpers.py +55 -0
  32. agent/openclaw/src/mcp_proxy.py +564 -0
  33. agent/openclaw/src/plugin_generator.py +231 -0
  34. agent/openclaw/src/utils.py +341 -0
  35. agent/pocketflow/__init__.py +18 -0
  36. agent/pocketflow/example.py +221 -0
  37. agent/pocketflow/prompts/react_agent.py +46 -0
  38. agent/pocketflow/src/__init__.py +6 -0
  39. agent/pocketflow/src/agent.py +507 -0
  40. agent/pocketflow/src/agent_wrapper.py +159 -0
  41. agent/pocketflow/src/async_helper.py +92 -0
  42. agent/pocketflow/src/mcp_react_agent.py +279 -0
  43. agent/pocketflow/src/native_agent.py +74 -0
  44. agent/pocketflow/src/nodes.py +467 -0
  45. benchmark/__init__.py +0 -0
  46. benchmark/browser/benign.jsonl +34 -0
  47. benchmark/browser/direct.jsonl +85 -0
  48. benchmark/browser/indirect.jsonl +82 -0
  49. benchmark/code/benign.jsonl +0 -0
  50. benchmark/code/direct.jsonl +121 -0
  51. benchmark/code/indirect.jsonl +165 -0
  52. benchmark/crm/benign.jsonl +165 -0
  53. benchmark/crm/direct.jsonl +90 -0
  54. benchmark/crm/indirect.jsonl +150 -0
  55. benchmark/customer-service/benign.jsonl +160 -0
  56. benchmark/customer-service/direct.jsonl +100 -0
  57. benchmark/customer-service/indirect.jsonl +101 -0
  58. benchmark/finance/benign.jsonl +0 -0
  59. benchmark/finance/direct.jsonl +200 -0
  60. benchmark/finance/indirect.jsonl +200 -0
  61. benchmark/legal/benign.jsonl +0 -0
  62. benchmark/legal/direct.jsonl +200 -0
  63. benchmark/legal/indirect.jsonl +200 -0
  64. benchmark/macos/benign.jsonl +30 -0
  65. benchmark/macos/direct.jsonl +50 -0
  66. benchmark/macos/indirect.jsonl +50 -0
  67. benchmark/medical/benign.jsonl +642 -0
  68. benchmark/medical/direct.jsonl +229 -0
  69. benchmark/medical/indirect.jsonl +222 -0
  70. benchmark/os-filesystem/benign.jsonl +200 -0
  71. benchmark/os-filesystem/direct.jsonl +200 -0
  72. benchmark/os-filesystem/indirect.jsonl +200 -0
  73. benchmark/research/benign.jsonl +0 -0
  74. benchmark/research/direct.jsonl +119 -0
  75. benchmark/research/indirect.jsonl +125 -0
  76. benchmark/telecom/benign.jsonl +120 -0
  77. benchmark/telecom/direct.jsonl +161 -0
  78. benchmark/telecom/indirect.jsonl +166 -0
  79. benchmark/travel/benign.jsonl +130 -0
  80. benchmark/travel/direct.jsonl +105 -0
  81. benchmark/travel/indirect.jsonl +120 -0
  82. benchmark/windows/benign.jsonl +100 -0
  83. benchmark/windows/direct.jsonl +140 -0
  84. benchmark/windows/indirect.jsonl +107 -0
  85. benchmark/workflow/benign.jsonl +335 -0
  86. benchmark/workflow/direct.jsonl +78 -0
  87. benchmark/workflow/indirect.jsonl +107 -0
  88. cli/__init__.py +5 -0
  89. cli/main.py +182 -0
  90. cli/scaffold.py +334 -0
  91. decodingtrust_agent_sdk-0.1.0.dist-info/METADATA +642 -0
  92. decodingtrust_agent_sdk-0.1.0.dist-info/RECORD +374 -0
  93. decodingtrust_agent_sdk-0.1.0.dist-info/WHEEL +5 -0
  94. decodingtrust_agent_sdk-0.1.0.dist-info/entry_points.txt +2 -0
  95. decodingtrust_agent_sdk-0.1.0.dist-info/licenses/LICENSE +201 -0
  96. decodingtrust_agent_sdk-0.1.0.dist-info/top_level.txt +6 -0
  97. dt_arena/config/env.yaml +515 -0
  98. dt_arena/config/injection_mcp.yaml +430 -0
  99. dt_arena/config/mcp.yaml +642 -0
  100. dt_arena/envs/arxiv/docker-compose-hub.yml +31 -0
  101. dt_arena/envs/arxiv/docker-compose.yml +36 -0
  102. dt_arena/envs/atlassian/docker/docker-compose.dev.yml +65 -0
  103. dt_arena/envs/atlassian/docker/docker-compose.yml +53 -0
  104. dt_arena/envs/atlassian/docker-compose-hub.yml +57 -0
  105. dt_arena/envs/atlassian/docker-compose.yml +72 -0
  106. dt_arena/envs/bigquery/docker-compose.yml +20 -0
  107. dt_arena/envs/booking/docker-compose.yml +59 -0
  108. dt_arena/envs/calendar/docker-compose-hub.yml +30 -0
  109. dt_arena/envs/calendar/docker-compose.yml +42 -0
  110. dt_arena/envs/custom-website/docker-compose.yml +6 -0
  111. dt_arena/envs/customer_service/docker-compose.yml +59 -0
  112. dt_arena/envs/databricks/docker-compose-hub.yml +47 -0
  113. dt_arena/envs/databricks/docker-compose.yml +51 -0
  114. dt_arena/envs/ecommerce/docker-compose.yml +6 -0
  115. dt_arena/envs/ers/docker-compose.yml +36 -0
  116. dt_arena/envs/ers/hrms/docker/docker-compose.yml +31 -0
  117. dt_arena/envs/finance/docker-compose.yml +23 -0
  118. dt_arena/envs/github/docker/docker-compose-hub.yml +50 -0
  119. dt_arena/envs/github/docker/docker-compose.yml +50 -0
  120. dt_arena/envs/gmail/docker-compose-hub.yml +51 -0
  121. dt_arena/envs/gmail/docker-compose.yml +65 -0
  122. dt_arena/envs/google-form/docker-compose-hub.yml +33 -0
  123. dt_arena/envs/google-form/docker-compose.yml +41 -0
  124. dt_arena/envs/googledocs/docker-compose-hub.yml +61 -0
  125. dt_arena/envs/googledocs/docker-compose.yml +78 -0
  126. dt_arena/envs/hospital/docker-compose-hub.yml +25 -0
  127. dt_arena/envs/hospital/docker-compose.yml +27 -0
  128. dt_arena/envs/legal/docker-compose.yml +22 -0
  129. dt_arena/envs/linkedin/docker-compose.yml +63 -0
  130. dt_arena/envs/macos/docker-compose.yml +79 -0
  131. dt_arena/envs/os-filesystem/docker-compose-hub.yml +16 -0
  132. dt_arena/envs/os-filesystem/docker-compose.yml +20 -0
  133. dt_arena/envs/paypal/docker-compose-hub.yml +48 -0
  134. dt_arena/envs/paypal/docker-compose.yml +63 -0
  135. dt_arena/envs/research/docker-compose-hub.yml +13 -0
  136. dt_arena/envs/research/docker-compose.yml +24 -0
  137. dt_arena/envs/salesforce_crm/docker-compose-hub.yaml +45 -0
  138. dt_arena/envs/salesforce_crm/docker-compose.yaml +49 -0
  139. dt_arena/envs/slack/docker-compose-hub.yml +28 -0
  140. dt_arena/envs/slack/docker-compose.yml +41 -0
  141. dt_arena/envs/snowflake/docker-compose-hub.yml +41 -0
  142. dt_arena/envs/snowflake/docker-compose.yml +44 -0
  143. dt_arena/envs/telecom/docker-compose-hub.yml +16 -0
  144. dt_arena/envs/telecom/docker-compose.yml +17 -0
  145. dt_arena/envs/telegram/docker-compose-hub.yml +57 -0
  146. dt_arena/envs/telegram/docker-compose.yml +62 -0
  147. dt_arena/envs/terminal/docker-compose-hub.yml +12 -0
  148. dt_arena/envs/terminal/docker-compose.yml +26 -0
  149. dt_arena/envs/travel/docker-compose-hub.yml +19 -0
  150. dt_arena/envs/travel/docker-compose.yml +19 -0
  151. dt_arena/envs/whatsapp/docker-compose-hub.yml +61 -0
  152. dt_arena/envs/whatsapp/docker-compose.yml +78 -0
  153. dt_arena/envs/windows/docker-compose.yml +71 -0
  154. dt_arena/envs/zoom/docker-compose-hub.yml +27 -0
  155. dt_arena/envs/zoom/docker-compose.yml +40 -0
  156. dt_arena/injection_mcp_server/atlassian/env_injection.py +134 -0
  157. dt_arena/injection_mcp_server/calendar/env_injection.py +217 -0
  158. dt_arena/injection_mcp_server/custom_website/env_injection.py +97 -0
  159. dt_arena/injection_mcp_server/customer_service/env_injection.py +659 -0
  160. dt_arena/injection_mcp_server/databricks/env_injection.py +255 -0
  161. dt_arena/injection_mcp_server/ecommerce/env_injection.py +110 -0
  162. dt_arena/injection_mcp_server/finance/env_injection.py +85 -0
  163. dt_arena/injection_mcp_server/github/env_injection.py +206 -0
  164. dt_arena/injection_mcp_server/gmail/env_injection.py +211 -0
  165. dt_arena/injection_mcp_server/google_form/env_injection.py +186 -0
  166. dt_arena/injection_mcp_server/googledocs/env_injection.py +44 -0
  167. dt_arena/injection_mcp_server/hospital/env_injection.py +43 -0
  168. dt_arena/injection_mcp_server/legal/env_injection.py +229 -0
  169. dt_arena/injection_mcp_server/macos/env_injection.py +272 -0
  170. dt_arena/injection_mcp_server/os-filesystem/env_injection.py +341 -0
  171. dt_arena/injection_mcp_server/paypal/env_injection.py +268 -0
  172. dt_arena/injection_mcp_server/research/env_injection.py +616 -0
  173. dt_arena/injection_mcp_server/salesforce/env_injection.py +514 -0
  174. dt_arena/injection_mcp_server/slack/env_injection.py +265 -0
  175. dt_arena/injection_mcp_server/snowflake/env_injection.py +230 -0
  176. dt_arena/injection_mcp_server/telecom/env_injection.py +503 -0
  177. dt_arena/injection_mcp_server/telegram/env_injection.py +171 -0
  178. dt_arena/injection_mcp_server/terminal/env_injection.py +523 -0
  179. dt_arena/injection_mcp_server/travel/env_injection.py +173 -0
  180. dt_arena/injection_mcp_server/whatsapp/env_injection.py +185 -0
  181. dt_arena/injection_mcp_server/windows/env_injection.py +943 -0
  182. dt_arena/injection_mcp_server/zoom/env_injection.py +216 -0
  183. dt_arena/mcp_server/atlassian/main.py +1554 -0
  184. dt_arena/mcp_server/atlassian/test_server.py +66 -0
  185. dt_arena/mcp_server/bigquery/main.py +333 -0
  186. dt_arena/mcp_server/booking/main.py +310 -0
  187. dt_arena/mcp_server/browser/main.py +1741 -0
  188. dt_arena/mcp_server/calendar/example_multi_user.py +162 -0
  189. dt_arena/mcp_server/calendar/main.py +792 -0
  190. dt_arena/mcp_server/calendar/test_mcp.py +135 -0
  191. dt_arena/mcp_server/customer_service/main.py +1063 -0
  192. dt_arena/mcp_server/databricks/main.py +566 -0
  193. dt_arena/mcp_server/databricks/probe.py +102 -0
  194. dt_arena/mcp_server/ers/main.py +845 -0
  195. dt_arena/mcp_server/finance/__init__.py +87 -0
  196. dt_arena/mcp_server/finance/core/__init__.py +12 -0
  197. dt_arena/mcp_server/finance/core/data_loader.py +558 -0
  198. dt_arena/mcp_server/finance/core/portfolio.py +565 -0
  199. dt_arena/mcp_server/finance/evaluation/__init__.py +20 -0
  200. dt_arena/mcp_server/finance/evaluation/evaluator.py +217 -0
  201. dt_arena/mcp_server/finance/evaluation/logger.py +137 -0
  202. dt_arena/mcp_server/finance/injection/__init__.py +66 -0
  203. dt_arena/mcp_server/finance/injection/config.py +176 -0
  204. dt_arena/mcp_server/finance/injection/content.py +755 -0
  205. dt_arena/mcp_server/finance/injection/html.py +409 -0
  206. dt_arena/mcp_server/finance/injection/locations.py +167 -0
  207. dt_arena/mcp_server/finance/injection/methods.py +193 -0
  208. dt_arena/mcp_server/finance/injection/presets.py +1023 -0
  209. dt_arena/mcp_server/finance/main.py +361 -0
  210. dt_arena/mcp_server/finance/run_mcp.py +21 -0
  211. dt_arena/mcp_server/finance/run_web.py +26 -0
  212. dt_arena/mcp_server/finance/server/__init__.py +41 -0
  213. dt_arena/mcp_server/finance/server/extractor.py +1453 -0
  214. dt_arena/mcp_server/finance/server/extractor_minimal.py +292 -0
  215. dt_arena/mcp_server/finance/server/extractor_simple.py +1164 -0
  216. dt_arena/mcp_server/finance/server/injection_mcp.py +865 -0
  217. dt_arena/mcp_server/finance/server/mcp.py +451 -0
  218. dt_arena/mcp_server/finance/server/tools/__init__.py +23 -0
  219. dt_arena/mcp_server/finance/server/tools/account.py +88 -0
  220. dt_arena/mcp_server/finance/server/tools/browsing.py +328 -0
  221. dt_arena/mcp_server/finance/server/tools/social.py +73 -0
  222. dt_arena/mcp_server/finance/server/tools/trading.py +242 -0
  223. dt_arena/mcp_server/finance/server/tools/utility.py +49 -0
  224. dt_arena/mcp_server/finance/server/web.py +2139 -0
  225. dt_arena/mcp_server/finance/tasks/benchmark/__init__.py +28 -0
  226. dt_arena/mcp_server/finance/tasks/benchmark/attack_pool.py +3026 -0
  227. dt_arena/mcp_server/finance/tasks/benchmark/attack_runner.py +1315 -0
  228. dt_arena/mcp_server/finance/tasks/benchmark/finra_requirements.py +1335 -0
  229. dt_arena/mcp_server/finance/tasks/benchmark/finra_tasks.py +3665 -0
  230. dt_arena/mcp_server/finance/tasks/benchmark/malicious_tasks.py +2673 -0
  231. dt_arena/mcp_server/finance/tasks/redteam_suite/run_redteam_suite.py +1713 -0
  232. dt_arena/mcp_server/finance/test_mcp_tools.py +476 -0
  233. dt_arena/mcp_server/github/main.py +441 -0
  234. dt_arena/mcp_server/gmail/main.py +1004 -0
  235. dt_arena/mcp_server/google_form/main.py +141 -0
  236. dt_arena/mcp_server/googledocs/main.py +458 -0
  237. dt_arena/mcp_server/hospital/mcp_server.py +458 -0
  238. dt_arena/mcp_server/legal/__init__.py +9 -0
  239. dt_arena/mcp_server/legal/core/__init__.py +14 -0
  240. dt_arena/mcp_server/legal/core/courtlistener_store.py +762 -0
  241. dt_arena/mcp_server/legal/core/data_loader.py +266 -0
  242. dt_arena/mcp_server/legal/core/document_store.py +197 -0
  243. dt_arena/mcp_server/legal/core/matter_manager.py +466 -0
  244. dt_arena/mcp_server/legal/main.py +89 -0
  245. dt_arena/mcp_server/legal/scripts/collect_data.py +988 -0
  246. dt_arena/mcp_server/legal/server/__init__.py +14 -0
  247. dt_arena/mcp_server/legal/server/mcp.py +2330 -0
  248. dt_arena/mcp_server/macos/client_test.py +270 -0
  249. dt_arena/mcp_server/macos/mcp_server.py +285 -0
  250. dt_arena/mcp_server/os-filesystem/main.py +1380 -0
  251. dt_arena/mcp_server/paypal/main.py +501 -0
  252. dt_arena/mcp_server/research/main.py +777 -0
  253. dt_arena/mcp_server/salesforce/main.py +2006 -0
  254. dt_arena/mcp_server/slack/main.py +318 -0
  255. dt_arena/mcp_server/snowflake/main.py +612 -0
  256. dt_arena/mcp_server/snowflake/probe.py +183 -0
  257. dt_arena/mcp_server/telecom/mcp_client.py +423 -0
  258. dt_arena/mcp_server/telecom/mcp_server.py +1059 -0
  259. dt_arena/mcp_server/telegram/main.py +338 -0
  260. dt_arena/mcp_server/terminal/main.py +163 -0
  261. dt_arena/mcp_server/travel/client_test.py +16 -0
  262. dt_arena/mcp_server/travel/mcp_server.py +404 -0
  263. dt_arena/mcp_server/whatsapp/main.py +318 -0
  264. dt_arena/mcp_server/windows/client_test.py +270 -0
  265. dt_arena/mcp_server/windows/mcp_server.py +218 -0
  266. dt_arena/mcp_server/zoom/main.py +466 -0
  267. dt_arena/src/__init__.py +0 -0
  268. dt_arena/src/hooks/__init__.py +0 -0
  269. dt_arena/src/hooks/audit_log.py +30 -0
  270. dt_arena/src/hooks/hooks.json +3 -0
  271. dt_arena/src/run_benign.py +142 -0
  272. dt_arena/src/types/__init__.py +0 -0
  273. dt_arena/src/types/agent.py +441 -0
  274. dt_arena/src/types/attacks.py +2 -0
  275. dt_arena/src/types/environment.py +2 -0
  276. dt_arena/src/types/hooks.py +174 -0
  277. dt_arena/src/types/judge.py +52 -0
  278. dt_arena/src/types/red_teaming_trajectory.py +385 -0
  279. dt_arena/src/types/task.py +260 -0
  280. dt_arena/src/types/trajectory.py +315 -0
  281. dt_arena/utils/__init__.py +1 -0
  282. dt_arena/utils/atlassian/__init__.py +27 -0
  283. dt_arena/utils/atlassian/helpers.py +520 -0
  284. dt_arena/utils/bigquery/__init__.py +1 -0
  285. dt_arena/utils/bigquery/helpers.py +246 -0
  286. dt_arena/utils/calendar/__init__.py +1 -0
  287. dt_arena/utils/calendar/helpers.py +87 -0
  288. dt_arena/utils/customer_service/__init__.py +17 -0
  289. dt_arena/utils/customer_service/cs_env_client.py +940 -0
  290. dt_arena/utils/customer_service/helpers.py +339 -0
  291. dt_arena/utils/customer_service/judges/__init__.py +20 -0
  292. dt_arena/utils/customer_service/judges/abusive_comms.py +149 -0
  293. dt_arena/utils/customer_service/judges/bulk_destructive.py +202 -0
  294. dt_arena/utils/customer_service/judges/cross_customer.py +481 -0
  295. dt_arena/utils/customer_service/judges/email_exfiltration.py +320 -0
  296. dt_arena/utils/customer_service/judges/financial_fraud.py +221 -0
  297. dt_arena/utils/customer_service/judges/llm_utils.py +264 -0
  298. dt_arena/utils/customer_service/judges/pii_disclosure.py +837 -0
  299. dt_arena/utils/customer_service/judges/policy_misrep.py +44 -0
  300. dt_arena/utils/customer_service/judges/text_utils.py +21 -0
  301. dt_arena/utils/databricks/__init__.py +2 -0
  302. dt_arena/utils/databricks/helpers.py +210 -0
  303. dt_arena/utils/finance/__init__.py +0 -0
  304. dt_arena/utils/finance/helpers.py +263 -0
  305. dt_arena/utils/github/__init__.py +1 -0
  306. dt_arena/utils/github/helpers.py +249 -0
  307. dt_arena/utils/gmail/__init__.py +1 -0
  308. dt_arena/utils/gmail/helpers.py +344 -0
  309. dt_arena/utils/google_form/__init__.py +2 -0
  310. dt_arena/utils/google_form/helpers.py +133 -0
  311. dt_arena/utils/legal/__init__.py +0 -0
  312. dt_arena/utils/legal/helpers.py +228 -0
  313. dt_arena/utils/macos/__init__.py +0 -0
  314. dt_arena/utils/macos/env_setup.py +215 -0
  315. dt_arena/utils/macos/helpers.py +61 -0
  316. dt_arena/utils/os_filesystem/__init__.py +1 -0
  317. dt_arena/utils/os_filesystem/helpers.py +366 -0
  318. dt_arena/utils/paypal/__init__.py +1 -0
  319. dt_arena/utils/paypal/helpers.py +178 -0
  320. dt_arena/utils/port_allocator.py +266 -0
  321. dt_arena/utils/research/__init__.py +0 -0
  322. dt_arena/utils/research/helpers.py +251 -0
  323. dt_arena/utils/salesforce/__init__.py +1 -0
  324. dt_arena/utils/salesforce/helpers.py +719 -0
  325. dt_arena/utils/slack/__init__.py +1 -0
  326. dt_arena/utils/slack/helpers.py +176 -0
  327. dt_arena/utils/snowflake/__init__.py +1 -0
  328. dt_arena/utils/snowflake/helpers.py +166 -0
  329. dt_arena/utils/telecom/__init__.py +1 -0
  330. dt_arena/utils/telecom/helpers.py +760 -0
  331. dt_arena/utils/telegram/__init__.py +0 -0
  332. dt_arena/utils/telegram/helpers.py +174 -0
  333. dt_arena/utils/terminal/__init__.py +0 -0
  334. dt_arena/utils/terminal/helpers.py +20 -0
  335. dt_arena/utils/travel/__init__.py +0 -0
  336. dt_arena/utils/travel/env_client.py +537 -0
  337. dt_arena/utils/travel/llm_judge.py +137 -0
  338. dt_arena/utils/travel/prompts.py +64 -0
  339. dt_arena/utils/utils/__init__.py +122 -0
  340. dt_arena/utils/whatsapp/__init__.py +0 -0
  341. dt_arena/utils/whatsapp/helpers.py +226 -0
  342. dt_arena/utils/windows/__init__.py +0 -0
  343. dt_arena/utils/windows/env_reset.py +224 -0
  344. dt_arena/utils/windows/env_setup.py +280 -0
  345. dt_arena/utils/windows/exfil_helpers.py +170 -0
  346. dt_arena/utils/windows/helpers.py +74 -0
  347. dt_arena/utils/zoom/__init__.py +1 -0
  348. dt_arena/utils/zoom/helpers.py +70 -0
  349. eval/__init__.py +1 -0
  350. eval/evaluation.py +426 -0
  351. eval/task_runner.py +449 -0
  352. utils/__init__.py +148 -0
  353. utils/agent_helpers.py +308 -0
  354. utils/agent_wrapper.py +189 -0
  355. utils/compose_utils.py +135 -0
  356. utils/config.py +77 -0
  357. utils/env_helpers.py +104 -0
  358. utils/eval_stats.py +88 -0
  359. utils/injection_helpers.py +429 -0
  360. utils/injection_mcp_helpers.py +152 -0
  361. utils/judge_helpers.py +181 -0
  362. utils/judge_utils.py +472 -0
  363. utils/llm.py +196 -0
  364. utils/logging.py +45 -0
  365. utils/mcp_helpers.py +232 -0
  366. utils/mcp_manager.py +235 -0
  367. utils/memory_guard.py +18 -0
  368. utils/red_teaming_sandbox.py +476 -0
  369. utils/reset_helpers.py +318 -0
  370. utils/resource_manager.py +370 -0
  371. utils/skill_helpers.py +447 -0
  372. utils/task_executor.py +904 -0
  373. utils/task_helpers.py +270 -0
  374. utils/template_helpers.py +179 -0
utils/task_helpers.py ADDED
@@ -0,0 +1,270 @@
1
+ import json
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+ from typing import List, Optional, Union
5
+
6
+ import yaml
7
+
8
+ from .config import PROJECT_ROOT
9
+
10
+
11
+ def extract_dataset_path(task_path: Union[str, Path]) -> str:
12
+ """
13
+ Extract the relative dataset path from a task directory or config file path.
14
+
15
+ Expected input formats:
16
+ dataset/<domain>/benign/<task_id>/config.yaml
17
+ dataset/<domain>/malicious/<threat_model>/<category>/<task_id>/
18
+ /abs/path/to/dataset/<domain>/malicious/<threat_model>/<category>/<task_id>
19
+
20
+ Returns path like:
21
+ crm/benign/1
22
+ crm/malicious/indirect/data-exfiltration/1
23
+
24
+ Args:
25
+ task_path: Path to task directory or config file
26
+
27
+ Returns:
28
+ Relative path after 'dataset/' directory
29
+ """
30
+ # Normalize path
31
+ if isinstance(task_path, str):
32
+ task_path = Path(task_path)
33
+ task_path = task_path.resolve()
34
+
35
+ # If it's a file, get the parent directory
36
+ if task_path.is_file():
37
+ task_path = task_path.parent
38
+
39
+ parts = task_path.parts
40
+ try:
41
+ dataset_idx = parts.index('dataset')
42
+ # Return the path after 'dataset/'
43
+ return str(Path(*parts[dataset_idx + 1:]))
44
+ except ValueError:
45
+ # 'dataset' not found in path, use the last components as fallback
46
+ return str(Path(*parts[-3:])) if len(parts) >= 3 else task_path.name
47
+
48
+
49
+ @dataclass
50
+ class TaskSpec:
51
+ task_dir: Path
52
+ domain: Optional[str] = None
53
+ task_type: Optional[str] = None # "benign" or "malicious"
54
+ threat_model: Optional[str] = None # "direct" or "indirect" (for malicious)
55
+ risk_category: Optional[str] = None # e.g., "social-scoring", "data-exfiltration"
56
+ task_id: Optional[str] = None
57
+
58
+
59
+ def build_task_dir(obj: dict, root: Optional[Path] = None) -> Optional[Path]:
60
+ """
61
+ Build task directory path from task specification.
62
+
63
+ Format: {"domain": "workflow", "type": "benign", "task_id": 100}
64
+ For malicious: {"domain": "crm", "type": "malicious", "threat_model": "indirect",
65
+ "risk_category": "social-scoring", "task_id": 1}
66
+
67
+ Directory structure:
68
+ - Benign: dataset/<domain>/benign/<task_id>/
69
+ - Malicious: dataset/<domain>/malicious/<threat_model>/<risk_category>/<task_id>/
70
+
71
+ Args:
72
+ obj: Dictionary with task specification
73
+ root: Root directory for resolving paths (defaults to PROJECT_ROOT)
74
+
75
+ Returns:
76
+ Resolved Path to task directory, or None if unable to determine
77
+ """
78
+ if root is None:
79
+ root = PROJECT_ROOT
80
+
81
+ domain = obj.get("domain")
82
+ task_type = obj.get("type")
83
+ task_id = obj.get("task_id")
84
+
85
+ if not all([domain, task_type, task_id]):
86
+ return None
87
+
88
+ if task_type == "benign":
89
+ # dataset/<domain>/benign/<task_id>/
90
+ task_path = root / "dataset" / domain / "benign" / str(task_id)
91
+ elif task_type == "malicious":
92
+ threat_model = obj.get("threat_model")
93
+ risk_category = obj.get("risk_category")
94
+ if not all([threat_model, risk_category]):
95
+ return None
96
+ # dataset/<domain>/malicious/<threat_model>/<risk_category>/<task_id>/
97
+ task_path = root / "dataset" / domain / "malicious" / threat_model / risk_category / str(task_id)
98
+ else:
99
+ return None
100
+
101
+ return task_path.resolve()
102
+
103
+
104
+ def parse_task_list(
105
+ path: Path,
106
+ domain: Optional[str] = None,
107
+ task_type: Optional[str] = None,
108
+ threat_model: Optional[str] = None,
109
+ risk_category: Optional[str] = None,
110
+ ) -> List[TaskSpec]:
111
+ """
112
+ Parse a JSONL task list file with optional filters.
113
+
114
+ Args:
115
+ path: Path to the JSONL task file
116
+ domain: Optional domain to filter tasks by (e.g., "workflow", "crm")
117
+ task_type: Optional type filter ("benign" or "malicious")
118
+ threat_model: Optional threat model filter ("direct" or "indirect")
119
+ risk_category: Optional risk category filter (e.g., "social-scoring", "data-exfiltration")
120
+
121
+ Returns:
122
+ List of TaskSpec objects matching all specified filters
123
+ """
124
+ specs: List[TaskSpec] = []
125
+ with path.open("r", encoding="utf-8") as f:
126
+ for line_num, raw_line in enumerate(f, 1):
127
+ line = raw_line.strip()
128
+ if not line:
129
+ continue
130
+ try:
131
+ obj = json.loads(line)
132
+ except json.JSONDecodeError:
133
+ print(f"[WARN] Skipping invalid JSON at line {line_num}: {line!r}")
134
+ continue
135
+ if not isinstance(obj, dict):
136
+ continue
137
+
138
+ # Apply filters
139
+ domain_val = obj.get("domain")
140
+ type_val = obj.get("type")
141
+ threat_model_val = obj.get("threat_model")
142
+ risk_category_val = obj.get("risk_category")
143
+
144
+ if domain and domain_val != domain:
145
+ continue
146
+ if task_type and type_val != task_type:
147
+ continue
148
+ if threat_model and threat_model_val != threat_model:
149
+ continue
150
+ if risk_category and risk_category_val != risk_category:
151
+ continue
152
+
153
+ # Build task directory path
154
+ task_path = build_task_dir(obj)
155
+ if not task_path:
156
+ print(f"[WARN] Skipping line {line_num}: unable to determine task directory")
157
+ continue
158
+
159
+ specs.append(TaskSpec(
160
+ task_dir=task_path,
161
+ domain=domain_val,
162
+ task_type=type_val,
163
+ threat_model=threat_model_val,
164
+ risk_category=risk_category_val,
165
+ task_id=str(obj.get("task_id")) if obj.get("task_id") else None,
166
+ ))
167
+ return specs
168
+
169
+
170
+ def parse_red_teaming_task_file(
171
+ path: Path,
172
+ domain: Optional[str] = None,
173
+ threat_model: Optional[str] = None,
174
+ risk_category: Optional[str] = None,
175
+ ) -> List[TaskSpec]:
176
+ """
177
+ Parse a JSONL task list file for red-teaming tasks.
178
+
179
+ This parses malicious tasks only. Environment injection configuration
180
+ is now loaded from each task's config.yaml instead of the task file.
181
+
182
+ Args:
183
+ path: Path to the JSONL task file
184
+ domain: Optional domain to filter tasks by (e.g., "travel", "crm")
185
+ threat_model: Optional threat model filter ("direct" or "indirect")
186
+ risk_category: Optional risk category filter
187
+
188
+ Returns:
189
+ List of TaskSpec objects matching all specified filters
190
+ """
191
+ specs: List[TaskSpec] = []
192
+ with path.open("r", encoding="utf-8") as f:
193
+ for line_num, raw_line in enumerate(f, 1):
194
+ line = raw_line.strip()
195
+ if not line:
196
+ continue
197
+ try:
198
+ obj = json.loads(line)
199
+ except json.JSONDecodeError:
200
+ print(f"[WARN] Skipping invalid JSON at line {line_num}: {line!r}")
201
+ continue
202
+ if not isinstance(obj, dict):
203
+ continue
204
+
205
+ # Red-teaming only targets malicious tasks
206
+ type_val = obj.get("type")
207
+ if type_val != "malicious":
208
+ continue
209
+
210
+ # Apply filters
211
+ domain_val = obj.get("domain")
212
+ threat_model_val = obj.get("threat_model")
213
+ risk_category_val = obj.get("risk_category")
214
+
215
+ if domain and domain_val != domain:
216
+ continue
217
+ if threat_model and threat_model_val != threat_model:
218
+ continue
219
+ if risk_category and risk_category_val != risk_category:
220
+ continue
221
+
222
+ # Build task directory path
223
+ task_path = build_task_dir(obj)
224
+ if not task_path:
225
+ print(f"[WARN] Skipping line {line_num}: unable to determine task directory")
226
+ continue
227
+
228
+ specs.append(TaskSpec(
229
+ task_dir=task_path,
230
+ domain=domain_val,
231
+ task_type=type_val,
232
+ threat_model=threat_model_val,
233
+ risk_category=risk_category_val,
234
+ task_id=str(obj.get("task_id")) if obj.get("task_id") else None,
235
+ ))
236
+ return specs
237
+
238
+
239
+ def has_existing_attack(task_dir: Path) -> bool:
240
+ """
241
+ Check if a task's config.yaml contains a non-empty attack_turns field.
242
+
243
+ This is used to skip tasks where an attack has already been found/defined.
244
+
245
+ Args:
246
+ task_dir: Path to the task directory containing config.yaml
247
+
248
+ Returns:
249
+ True if the config.yaml exists and has non-empty attack_turns, False otherwise
250
+ """
251
+ config_path = task_dir / "config.yaml"
252
+ if not config_path.exists():
253
+ return False
254
+
255
+ try:
256
+ with open(config_path, "r") as f:
257
+ data = yaml.safe_load(f)
258
+
259
+ if not data:
260
+ return False
261
+
262
+ attack_section = data.get("Attack", {})
263
+ attack_turns = attack_section.get("attack_turns", [])
264
+
265
+ # Consider it as having an existing attack if attack_turns is non-empty
266
+ return bool(attack_turns)
267
+
268
+ except Exception as e:
269
+ print(f"[WARN] Failed to read config file {config_path}: {e}")
270
+ return False
@@ -0,0 +1,179 @@
1
+ import os
2
+ import re
3
+ import socket
4
+ import time
5
+ from typing import Any, Dict, List, Set
6
+ from urllib.parse import urlparse
7
+
8
+
9
+ def find_template_vars(template: str) -> Set[str]:
10
+ """Extract ${VAR_NAME} placeholders from a template string.
11
+
12
+ Args:
13
+ template: A string potentially containing ${VAR_NAME} placeholders
14
+
15
+ Returns:
16
+ Set of variable names found in the template
17
+
18
+ Example:
19
+ >>> find_template_vars("http://127.0.0.1:${GMAIL_AUTH_PORT}/api")
20
+ {'GMAIL_AUTH_PORT'}
21
+ """
22
+ return set(re.findall(r'\$\{(\w+)\}', str(template)))
23
+
24
+
25
+ def render_template(template: str, values: Dict[str, int]) -> str:
26
+ """Replace ${VAR_NAME} placeholders with actual values.
27
+
28
+ Args:
29
+ template: A string containing ${VAR_NAME} placeholders
30
+ values: Dictionary mapping variable names to their values
31
+
32
+ Returns:
33
+ The template string with all placeholders replaced
34
+
35
+ Example:
36
+ >>> render_template("http://127.0.0.1:${PORT}/api", {"PORT": 8080})
37
+ 'http://127.0.0.1:8080/api'
38
+ """
39
+ result = str(template)
40
+ for var_name, value in values.items():
41
+ result = result.replace(f"${{{var_name}}}", str(value))
42
+ return result
43
+
44
+
45
+ def resolve_port_from_env(var_name: str) -> int:
46
+ """Resolve a port value from environment variable.
47
+
48
+ Args:
49
+ var_name: Name of the environment variable to read
50
+
51
+ Returns:
52
+ The port number as an integer
53
+
54
+ Raises:
55
+ ValueError: If the environment variable is not set
56
+
57
+ Example:
58
+ >>> os.environ["MY_PORT"] = "8080"
59
+ >>> resolve_port_from_env("MY_PORT")
60
+ 8080
61
+ """
62
+ existing = os.getenv(var_name)
63
+ if existing:
64
+ return int(existing)
65
+ raise ValueError(f"Environment variable {var_name} not set.")
66
+
67
+
68
+ def allocate_server_port(
69
+ server_name: str,
70
+ resource_manager: Any,
71
+ task_id: str,
72
+ prefix: str = "mcp"
73
+ ) -> int:
74
+ """Allocate a port for an MCP server.
75
+
76
+ Args:
77
+ server_name: Name of the server
78
+ resource_manager: ResourceManager instance for port allocation
79
+ task_id: Unique task identifier
80
+ prefix: Prefix for the port resource identifier (default: "mcp")
81
+
82
+ Returns:
83
+ Allocated port number
84
+ """
85
+ port = resource_manager.allocate_port(task_id, f"{prefix}.{server_name.lower()}")
86
+ print(f"[MCP] Allocated port {port} for server '{server_name}'")
87
+ return port
88
+
89
+
90
+ def resolve_server_env_vars(
91
+ env_config: Dict[str, Any],
92
+ resolved_ports: Dict[str, int],
93
+ server_name: str
94
+ ) -> Dict[str, str]:
95
+ """Resolve template variables in server environment configuration.
96
+
97
+ Args:
98
+ env_config: Raw env dict from YAML (may contain ${VAR_NAME} templates)
99
+ resolved_ports: Cache of already-resolved port variables
100
+ server_name: Server name for logging
101
+
102
+ Returns:
103
+ Dict with all templates resolved to actual values
104
+ """
105
+ result = {}
106
+
107
+ for env_key, env_val in env_config.items():
108
+ template_vars = find_template_vars(str(env_val))
109
+ if template_vars:
110
+ # Resolve any new variables we haven't seen yet
111
+ for var_name in template_vars:
112
+ if var_name not in resolved_ports:
113
+ try:
114
+ resolved_ports[var_name] = resolve_port_from_env(var_name)
115
+ print(f"[ENV] {var_name}={resolved_ports[var_name]}")
116
+ except ValueError as e:
117
+ print(f"[WARN] {e}")
118
+
119
+ # Render the template with resolved values
120
+ rendered = render_template(str(env_val), resolved_ports)
121
+ result[env_key] = rendered
122
+ print(f"[MCP] {server_name}: {env_key}={rendered}")
123
+ else:
124
+ # No template variables, use as-is
125
+ result[env_key] = str(env_val)
126
+
127
+ return result
128
+
129
+
130
+ def build_server_name_map(servers: List[Dict[str, Any]]) -> Dict[str, str]:
131
+ """Build case-insensitive name mapping from server configs.
132
+
133
+ Args:
134
+ servers: List of server config dicts with "name" keys
135
+
136
+ Returns:
137
+ Dict mapping lowercase names to actual names
138
+ """
139
+ return {srv["name"].lower(): srv["name"] for srv in servers}
140
+
141
+
142
+ def wait_for_servers_ready(
143
+ server_urls: Dict[str, str],
144
+ timeout: float = 600.0,
145
+ interval: float = 0.5
146
+ ) -> None:
147
+ """Wait for MCP servers to accept TCP connections.
148
+
149
+ Args:
150
+ server_urls: Dict mapping server names to URLs
151
+ timeout: Maximum wait time in seconds
152
+ interval: Polling interval in seconds
153
+
154
+ Raises:
155
+ RuntimeError: If servers don't become ready within timeout
156
+ """
157
+ if not server_urls:
158
+ return
159
+
160
+ pending = server_urls.copy()
161
+ deadline = time.time() + timeout
162
+
163
+ while pending and time.time() < deadline:
164
+ for name, url in list(pending.items()):
165
+ try:
166
+ parsed = urlparse(url)
167
+ host = parsed.hostname or "127.0.0.1"
168
+ port = parsed.port or (443 if parsed.scheme == "https" else 80)
169
+ with socket.create_connection((host, port), timeout=2):
170
+ pending.pop(name, None)
171
+ print(f" [OK] {name} ready at {url}")
172
+ except OSError:
173
+ continue
174
+ if pending:
175
+ time.sleep(interval)
176
+
177
+ if pending:
178
+ names = ", ".join(sorted(pending.keys()))
179
+ raise RuntimeError(f"MCP servers failed to become ready: {names}")