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.
- agent/__init__.py +30 -0
- agent/claudesdk/__init__.py +8 -0
- agent/claudesdk/example.py +221 -0
- agent/claudesdk/src/__init__.py +8 -0
- agent/claudesdk/src/agent.py +400 -0
- agent/claudesdk/src/mcp_proxy.py +409 -0
- agent/claudesdk/src/utils.py +420 -0
- agent/googleadk/__init__.py +15 -0
- agent/googleadk/example.py +237 -0
- agent/googleadk/src/__init__.py +12 -0
- agent/googleadk/src/agent.py +401 -0
- agent/googleadk/src/mcp_wrapper.py +163 -0
- agent/googleadk/src/utils.py +602 -0
- agent/langchain/__init__.py +8 -0
- agent/langchain/example.py +213 -0
- agent/langchain/src/__init__.py +8 -0
- agent/langchain/src/agent.py +645 -0
- agent/langchain/src/utils.py +433 -0
- agent/openaisdk/__init__.py +17 -0
- agent/openaisdk/example.py +228 -0
- agent/openaisdk/src/__init__.py +12 -0
- agent/openaisdk/src/agent.py +491 -0
- agent/openaisdk/src/agent_wrapper.py +143 -0
- agent/openaisdk/src/mcp_wrapper.py +395 -0
- agent/openaisdk/src/utils.py +493 -0
- agent/openclaw/__init__.py +10 -0
- agent/openclaw/example.py +251 -0
- agent/openclaw/src/__init__.py +14 -0
- agent/openclaw/src/agent.py +930 -0
- agent/openclaw/src/helpers/__init__.py +1 -0
- agent/openclaw/src/helpers/auth_helpers.py +55 -0
- agent/openclaw/src/mcp_proxy.py +564 -0
- agent/openclaw/src/plugin_generator.py +231 -0
- agent/openclaw/src/utils.py +341 -0
- agent/pocketflow/__init__.py +18 -0
- agent/pocketflow/example.py +221 -0
- agent/pocketflow/prompts/react_agent.py +46 -0
- agent/pocketflow/src/__init__.py +6 -0
- agent/pocketflow/src/agent.py +507 -0
- agent/pocketflow/src/agent_wrapper.py +159 -0
- agent/pocketflow/src/async_helper.py +92 -0
- agent/pocketflow/src/mcp_react_agent.py +279 -0
- agent/pocketflow/src/native_agent.py +74 -0
- agent/pocketflow/src/nodes.py +467 -0
- benchmark/__init__.py +0 -0
- benchmark/browser/benign.jsonl +34 -0
- benchmark/browser/direct.jsonl +85 -0
- benchmark/browser/indirect.jsonl +82 -0
- benchmark/code/benign.jsonl +0 -0
- benchmark/code/direct.jsonl +121 -0
- benchmark/code/indirect.jsonl +165 -0
- benchmark/crm/benign.jsonl +165 -0
- benchmark/crm/direct.jsonl +90 -0
- benchmark/crm/indirect.jsonl +150 -0
- benchmark/customer-service/benign.jsonl +160 -0
- benchmark/customer-service/direct.jsonl +100 -0
- benchmark/customer-service/indirect.jsonl +101 -0
- benchmark/finance/benign.jsonl +0 -0
- benchmark/finance/direct.jsonl +200 -0
- benchmark/finance/indirect.jsonl +200 -0
- benchmark/legal/benign.jsonl +0 -0
- benchmark/legal/direct.jsonl +200 -0
- benchmark/legal/indirect.jsonl +200 -0
- benchmark/macos/benign.jsonl +30 -0
- benchmark/macos/direct.jsonl +50 -0
- benchmark/macos/indirect.jsonl +50 -0
- benchmark/medical/benign.jsonl +642 -0
- benchmark/medical/direct.jsonl +229 -0
- benchmark/medical/indirect.jsonl +222 -0
- benchmark/os-filesystem/benign.jsonl +200 -0
- benchmark/os-filesystem/direct.jsonl +200 -0
- benchmark/os-filesystem/indirect.jsonl +200 -0
- benchmark/research/benign.jsonl +0 -0
- benchmark/research/direct.jsonl +119 -0
- benchmark/research/indirect.jsonl +125 -0
- benchmark/telecom/benign.jsonl +120 -0
- benchmark/telecom/direct.jsonl +161 -0
- benchmark/telecom/indirect.jsonl +166 -0
- benchmark/travel/benign.jsonl +130 -0
- benchmark/travel/direct.jsonl +105 -0
- benchmark/travel/indirect.jsonl +120 -0
- benchmark/windows/benign.jsonl +100 -0
- benchmark/windows/direct.jsonl +140 -0
- benchmark/windows/indirect.jsonl +107 -0
- benchmark/workflow/benign.jsonl +335 -0
- benchmark/workflow/direct.jsonl +78 -0
- benchmark/workflow/indirect.jsonl +107 -0
- cli/__init__.py +5 -0
- cli/main.py +182 -0
- cli/scaffold.py +334 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/METADATA +642 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/RECORD +374 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/WHEEL +5 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/entry_points.txt +2 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/licenses/LICENSE +201 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/top_level.txt +6 -0
- dt_arena/config/env.yaml +515 -0
- dt_arena/config/injection_mcp.yaml +430 -0
- dt_arena/config/mcp.yaml +642 -0
- dt_arena/envs/arxiv/docker-compose-hub.yml +31 -0
- dt_arena/envs/arxiv/docker-compose.yml +36 -0
- dt_arena/envs/atlassian/docker/docker-compose.dev.yml +65 -0
- dt_arena/envs/atlassian/docker/docker-compose.yml +53 -0
- dt_arena/envs/atlassian/docker-compose-hub.yml +57 -0
- dt_arena/envs/atlassian/docker-compose.yml +72 -0
- dt_arena/envs/bigquery/docker-compose.yml +20 -0
- dt_arena/envs/booking/docker-compose.yml +59 -0
- dt_arena/envs/calendar/docker-compose-hub.yml +30 -0
- dt_arena/envs/calendar/docker-compose.yml +42 -0
- dt_arena/envs/custom-website/docker-compose.yml +6 -0
- dt_arena/envs/customer_service/docker-compose.yml +59 -0
- dt_arena/envs/databricks/docker-compose-hub.yml +47 -0
- dt_arena/envs/databricks/docker-compose.yml +51 -0
- dt_arena/envs/ecommerce/docker-compose.yml +6 -0
- dt_arena/envs/ers/docker-compose.yml +36 -0
- dt_arena/envs/ers/hrms/docker/docker-compose.yml +31 -0
- dt_arena/envs/finance/docker-compose.yml +23 -0
- dt_arena/envs/github/docker/docker-compose-hub.yml +50 -0
- dt_arena/envs/github/docker/docker-compose.yml +50 -0
- dt_arena/envs/gmail/docker-compose-hub.yml +51 -0
- dt_arena/envs/gmail/docker-compose.yml +65 -0
- dt_arena/envs/google-form/docker-compose-hub.yml +33 -0
- dt_arena/envs/google-form/docker-compose.yml +41 -0
- dt_arena/envs/googledocs/docker-compose-hub.yml +61 -0
- dt_arena/envs/googledocs/docker-compose.yml +78 -0
- dt_arena/envs/hospital/docker-compose-hub.yml +25 -0
- dt_arena/envs/hospital/docker-compose.yml +27 -0
- dt_arena/envs/legal/docker-compose.yml +22 -0
- dt_arena/envs/linkedin/docker-compose.yml +63 -0
- dt_arena/envs/macos/docker-compose.yml +79 -0
- dt_arena/envs/os-filesystem/docker-compose-hub.yml +16 -0
- dt_arena/envs/os-filesystem/docker-compose.yml +20 -0
- dt_arena/envs/paypal/docker-compose-hub.yml +48 -0
- dt_arena/envs/paypal/docker-compose.yml +63 -0
- dt_arena/envs/research/docker-compose-hub.yml +13 -0
- dt_arena/envs/research/docker-compose.yml +24 -0
- dt_arena/envs/salesforce_crm/docker-compose-hub.yaml +45 -0
- dt_arena/envs/salesforce_crm/docker-compose.yaml +49 -0
- dt_arena/envs/slack/docker-compose-hub.yml +28 -0
- dt_arena/envs/slack/docker-compose.yml +41 -0
- dt_arena/envs/snowflake/docker-compose-hub.yml +41 -0
- dt_arena/envs/snowflake/docker-compose.yml +44 -0
- dt_arena/envs/telecom/docker-compose-hub.yml +16 -0
- dt_arena/envs/telecom/docker-compose.yml +17 -0
- dt_arena/envs/telegram/docker-compose-hub.yml +57 -0
- dt_arena/envs/telegram/docker-compose.yml +62 -0
- dt_arena/envs/terminal/docker-compose-hub.yml +12 -0
- dt_arena/envs/terminal/docker-compose.yml +26 -0
- dt_arena/envs/travel/docker-compose-hub.yml +19 -0
- dt_arena/envs/travel/docker-compose.yml +19 -0
- dt_arena/envs/whatsapp/docker-compose-hub.yml +61 -0
- dt_arena/envs/whatsapp/docker-compose.yml +78 -0
- dt_arena/envs/windows/docker-compose.yml +71 -0
- dt_arena/envs/zoom/docker-compose-hub.yml +27 -0
- dt_arena/envs/zoom/docker-compose.yml +40 -0
- dt_arena/injection_mcp_server/atlassian/env_injection.py +134 -0
- dt_arena/injection_mcp_server/calendar/env_injection.py +217 -0
- dt_arena/injection_mcp_server/custom_website/env_injection.py +97 -0
- dt_arena/injection_mcp_server/customer_service/env_injection.py +659 -0
- dt_arena/injection_mcp_server/databricks/env_injection.py +255 -0
- dt_arena/injection_mcp_server/ecommerce/env_injection.py +110 -0
- dt_arena/injection_mcp_server/finance/env_injection.py +85 -0
- dt_arena/injection_mcp_server/github/env_injection.py +206 -0
- dt_arena/injection_mcp_server/gmail/env_injection.py +211 -0
- dt_arena/injection_mcp_server/google_form/env_injection.py +186 -0
- dt_arena/injection_mcp_server/googledocs/env_injection.py +44 -0
- dt_arena/injection_mcp_server/hospital/env_injection.py +43 -0
- dt_arena/injection_mcp_server/legal/env_injection.py +229 -0
- dt_arena/injection_mcp_server/macos/env_injection.py +272 -0
- dt_arena/injection_mcp_server/os-filesystem/env_injection.py +341 -0
- dt_arena/injection_mcp_server/paypal/env_injection.py +268 -0
- dt_arena/injection_mcp_server/research/env_injection.py +616 -0
- dt_arena/injection_mcp_server/salesforce/env_injection.py +514 -0
- dt_arena/injection_mcp_server/slack/env_injection.py +265 -0
- dt_arena/injection_mcp_server/snowflake/env_injection.py +230 -0
- dt_arena/injection_mcp_server/telecom/env_injection.py +503 -0
- dt_arena/injection_mcp_server/telegram/env_injection.py +171 -0
- dt_arena/injection_mcp_server/terminal/env_injection.py +523 -0
- dt_arena/injection_mcp_server/travel/env_injection.py +173 -0
- dt_arena/injection_mcp_server/whatsapp/env_injection.py +185 -0
- dt_arena/injection_mcp_server/windows/env_injection.py +943 -0
- dt_arena/injection_mcp_server/zoom/env_injection.py +216 -0
- dt_arena/mcp_server/atlassian/main.py +1554 -0
- dt_arena/mcp_server/atlassian/test_server.py +66 -0
- dt_arena/mcp_server/bigquery/main.py +333 -0
- dt_arena/mcp_server/booking/main.py +310 -0
- dt_arena/mcp_server/browser/main.py +1741 -0
- dt_arena/mcp_server/calendar/example_multi_user.py +162 -0
- dt_arena/mcp_server/calendar/main.py +792 -0
- dt_arena/mcp_server/calendar/test_mcp.py +135 -0
- dt_arena/mcp_server/customer_service/main.py +1063 -0
- dt_arena/mcp_server/databricks/main.py +566 -0
- dt_arena/mcp_server/databricks/probe.py +102 -0
- dt_arena/mcp_server/ers/main.py +845 -0
- dt_arena/mcp_server/finance/__init__.py +87 -0
- dt_arena/mcp_server/finance/core/__init__.py +12 -0
- dt_arena/mcp_server/finance/core/data_loader.py +558 -0
- dt_arena/mcp_server/finance/core/portfolio.py +565 -0
- dt_arena/mcp_server/finance/evaluation/__init__.py +20 -0
- dt_arena/mcp_server/finance/evaluation/evaluator.py +217 -0
- dt_arena/mcp_server/finance/evaluation/logger.py +137 -0
- dt_arena/mcp_server/finance/injection/__init__.py +66 -0
- dt_arena/mcp_server/finance/injection/config.py +176 -0
- dt_arena/mcp_server/finance/injection/content.py +755 -0
- dt_arena/mcp_server/finance/injection/html.py +409 -0
- dt_arena/mcp_server/finance/injection/locations.py +167 -0
- dt_arena/mcp_server/finance/injection/methods.py +193 -0
- dt_arena/mcp_server/finance/injection/presets.py +1023 -0
- dt_arena/mcp_server/finance/main.py +361 -0
- dt_arena/mcp_server/finance/run_mcp.py +21 -0
- dt_arena/mcp_server/finance/run_web.py +26 -0
- dt_arena/mcp_server/finance/server/__init__.py +41 -0
- dt_arena/mcp_server/finance/server/extractor.py +1453 -0
- dt_arena/mcp_server/finance/server/extractor_minimal.py +292 -0
- dt_arena/mcp_server/finance/server/extractor_simple.py +1164 -0
- dt_arena/mcp_server/finance/server/injection_mcp.py +865 -0
- dt_arena/mcp_server/finance/server/mcp.py +451 -0
- dt_arena/mcp_server/finance/server/tools/__init__.py +23 -0
- dt_arena/mcp_server/finance/server/tools/account.py +88 -0
- dt_arena/mcp_server/finance/server/tools/browsing.py +328 -0
- dt_arena/mcp_server/finance/server/tools/social.py +73 -0
- dt_arena/mcp_server/finance/server/tools/trading.py +242 -0
- dt_arena/mcp_server/finance/server/tools/utility.py +49 -0
- dt_arena/mcp_server/finance/server/web.py +2139 -0
- dt_arena/mcp_server/finance/tasks/benchmark/__init__.py +28 -0
- dt_arena/mcp_server/finance/tasks/benchmark/attack_pool.py +3026 -0
- dt_arena/mcp_server/finance/tasks/benchmark/attack_runner.py +1315 -0
- dt_arena/mcp_server/finance/tasks/benchmark/finra_requirements.py +1335 -0
- dt_arena/mcp_server/finance/tasks/benchmark/finra_tasks.py +3665 -0
- dt_arena/mcp_server/finance/tasks/benchmark/malicious_tasks.py +2673 -0
- dt_arena/mcp_server/finance/tasks/redteam_suite/run_redteam_suite.py +1713 -0
- dt_arena/mcp_server/finance/test_mcp_tools.py +476 -0
- dt_arena/mcp_server/github/main.py +441 -0
- dt_arena/mcp_server/gmail/main.py +1004 -0
- dt_arena/mcp_server/google_form/main.py +141 -0
- dt_arena/mcp_server/googledocs/main.py +458 -0
- dt_arena/mcp_server/hospital/mcp_server.py +458 -0
- dt_arena/mcp_server/legal/__init__.py +9 -0
- dt_arena/mcp_server/legal/core/__init__.py +14 -0
- dt_arena/mcp_server/legal/core/courtlistener_store.py +762 -0
- dt_arena/mcp_server/legal/core/data_loader.py +266 -0
- dt_arena/mcp_server/legal/core/document_store.py +197 -0
- dt_arena/mcp_server/legal/core/matter_manager.py +466 -0
- dt_arena/mcp_server/legal/main.py +89 -0
- dt_arena/mcp_server/legal/scripts/collect_data.py +988 -0
- dt_arena/mcp_server/legal/server/__init__.py +14 -0
- dt_arena/mcp_server/legal/server/mcp.py +2330 -0
- dt_arena/mcp_server/macos/client_test.py +270 -0
- dt_arena/mcp_server/macos/mcp_server.py +285 -0
- dt_arena/mcp_server/os-filesystem/main.py +1380 -0
- dt_arena/mcp_server/paypal/main.py +501 -0
- dt_arena/mcp_server/research/main.py +777 -0
- dt_arena/mcp_server/salesforce/main.py +2006 -0
- dt_arena/mcp_server/slack/main.py +318 -0
- dt_arena/mcp_server/snowflake/main.py +612 -0
- dt_arena/mcp_server/snowflake/probe.py +183 -0
- dt_arena/mcp_server/telecom/mcp_client.py +423 -0
- dt_arena/mcp_server/telecom/mcp_server.py +1059 -0
- dt_arena/mcp_server/telegram/main.py +338 -0
- dt_arena/mcp_server/terminal/main.py +163 -0
- dt_arena/mcp_server/travel/client_test.py +16 -0
- dt_arena/mcp_server/travel/mcp_server.py +404 -0
- dt_arena/mcp_server/whatsapp/main.py +318 -0
- dt_arena/mcp_server/windows/client_test.py +270 -0
- dt_arena/mcp_server/windows/mcp_server.py +218 -0
- dt_arena/mcp_server/zoom/main.py +466 -0
- dt_arena/src/__init__.py +0 -0
- dt_arena/src/hooks/__init__.py +0 -0
- dt_arena/src/hooks/audit_log.py +30 -0
- dt_arena/src/hooks/hooks.json +3 -0
- dt_arena/src/run_benign.py +142 -0
- dt_arena/src/types/__init__.py +0 -0
- dt_arena/src/types/agent.py +441 -0
- dt_arena/src/types/attacks.py +2 -0
- dt_arena/src/types/environment.py +2 -0
- dt_arena/src/types/hooks.py +174 -0
- dt_arena/src/types/judge.py +52 -0
- dt_arena/src/types/red_teaming_trajectory.py +385 -0
- dt_arena/src/types/task.py +260 -0
- dt_arena/src/types/trajectory.py +315 -0
- dt_arena/utils/__init__.py +1 -0
- dt_arena/utils/atlassian/__init__.py +27 -0
- dt_arena/utils/atlassian/helpers.py +520 -0
- dt_arena/utils/bigquery/__init__.py +1 -0
- dt_arena/utils/bigquery/helpers.py +246 -0
- dt_arena/utils/calendar/__init__.py +1 -0
- dt_arena/utils/calendar/helpers.py +87 -0
- dt_arena/utils/customer_service/__init__.py +17 -0
- dt_arena/utils/customer_service/cs_env_client.py +940 -0
- dt_arena/utils/customer_service/helpers.py +339 -0
- dt_arena/utils/customer_service/judges/__init__.py +20 -0
- dt_arena/utils/customer_service/judges/abusive_comms.py +149 -0
- dt_arena/utils/customer_service/judges/bulk_destructive.py +202 -0
- dt_arena/utils/customer_service/judges/cross_customer.py +481 -0
- dt_arena/utils/customer_service/judges/email_exfiltration.py +320 -0
- dt_arena/utils/customer_service/judges/financial_fraud.py +221 -0
- dt_arena/utils/customer_service/judges/llm_utils.py +264 -0
- dt_arena/utils/customer_service/judges/pii_disclosure.py +837 -0
- dt_arena/utils/customer_service/judges/policy_misrep.py +44 -0
- dt_arena/utils/customer_service/judges/text_utils.py +21 -0
- dt_arena/utils/databricks/__init__.py +2 -0
- dt_arena/utils/databricks/helpers.py +210 -0
- dt_arena/utils/finance/__init__.py +0 -0
- dt_arena/utils/finance/helpers.py +263 -0
- dt_arena/utils/github/__init__.py +1 -0
- dt_arena/utils/github/helpers.py +249 -0
- dt_arena/utils/gmail/__init__.py +1 -0
- dt_arena/utils/gmail/helpers.py +344 -0
- dt_arena/utils/google_form/__init__.py +2 -0
- dt_arena/utils/google_form/helpers.py +133 -0
- dt_arena/utils/legal/__init__.py +0 -0
- dt_arena/utils/legal/helpers.py +228 -0
- dt_arena/utils/macos/__init__.py +0 -0
- dt_arena/utils/macos/env_setup.py +215 -0
- dt_arena/utils/macos/helpers.py +61 -0
- dt_arena/utils/os_filesystem/__init__.py +1 -0
- dt_arena/utils/os_filesystem/helpers.py +366 -0
- dt_arena/utils/paypal/__init__.py +1 -0
- dt_arena/utils/paypal/helpers.py +178 -0
- dt_arena/utils/port_allocator.py +266 -0
- dt_arena/utils/research/__init__.py +0 -0
- dt_arena/utils/research/helpers.py +251 -0
- dt_arena/utils/salesforce/__init__.py +1 -0
- dt_arena/utils/salesforce/helpers.py +719 -0
- dt_arena/utils/slack/__init__.py +1 -0
- dt_arena/utils/slack/helpers.py +176 -0
- dt_arena/utils/snowflake/__init__.py +1 -0
- dt_arena/utils/snowflake/helpers.py +166 -0
- dt_arena/utils/telecom/__init__.py +1 -0
- dt_arena/utils/telecom/helpers.py +760 -0
- dt_arena/utils/telegram/__init__.py +0 -0
- dt_arena/utils/telegram/helpers.py +174 -0
- dt_arena/utils/terminal/__init__.py +0 -0
- dt_arena/utils/terminal/helpers.py +20 -0
- dt_arena/utils/travel/__init__.py +0 -0
- dt_arena/utils/travel/env_client.py +537 -0
- dt_arena/utils/travel/llm_judge.py +137 -0
- dt_arena/utils/travel/prompts.py +64 -0
- dt_arena/utils/utils/__init__.py +122 -0
- dt_arena/utils/whatsapp/__init__.py +0 -0
- dt_arena/utils/whatsapp/helpers.py +226 -0
- dt_arena/utils/windows/__init__.py +0 -0
- dt_arena/utils/windows/env_reset.py +224 -0
- dt_arena/utils/windows/env_setup.py +280 -0
- dt_arena/utils/windows/exfil_helpers.py +170 -0
- dt_arena/utils/windows/helpers.py +74 -0
- dt_arena/utils/zoom/__init__.py +1 -0
- dt_arena/utils/zoom/helpers.py +70 -0
- eval/__init__.py +1 -0
- eval/evaluation.py +426 -0
- eval/task_runner.py +449 -0
- utils/__init__.py +148 -0
- utils/agent_helpers.py +308 -0
- utils/agent_wrapper.py +189 -0
- utils/compose_utils.py +135 -0
- utils/config.py +77 -0
- utils/env_helpers.py +104 -0
- utils/eval_stats.py +88 -0
- utils/injection_helpers.py +429 -0
- utils/injection_mcp_helpers.py +152 -0
- utils/judge_helpers.py +181 -0
- utils/judge_utils.py +472 -0
- utils/llm.py +196 -0
- utils/logging.py +45 -0
- utils/mcp_helpers.py +232 -0
- utils/mcp_manager.py +235 -0
- utils/memory_guard.py +18 -0
- utils/red_teaming_sandbox.py +476 -0
- utils/reset_helpers.py +318 -0
- utils/resource_manager.py +370 -0
- utils/skill_helpers.py +447 -0
- utils/task_executor.py +904 -0
- utils/task_helpers.py +270 -0
- 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}")
|