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
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTML Injector - Core HTML injection processing
|
|
3
|
+
|
|
4
|
+
Static injection model (Docker + red-teaming agent):
|
|
5
|
+
- Each rule is bound to specific page(s) via the `pages` field
|
|
6
|
+
- Injection is applied to ALL matching elements on the bound page
|
|
7
|
+
- No dynamic discovery, no location locking, no view-order matching
|
|
8
|
+
|
|
9
|
+
This module handles applying injection rules to HTML content.
|
|
10
|
+
Uses HTML structure matching (regex/patterns) to locate injection points,
|
|
11
|
+
without requiring any markers in the templates.
|
|
12
|
+
|
|
13
|
+
Note: This is called HtmlInjector (vs ContentInjector) because it injects
|
|
14
|
+
HIDDEN content into HTML, while ContentInjector injects VISIBLE fake data.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
from typing import Optional, List, Dict, Tuple
|
|
19
|
+
|
|
20
|
+
from .locations import InjectionLocation
|
|
21
|
+
from .methods import InjectionMethod, InjectionRenderer
|
|
22
|
+
from .config import InjectionConfig, InjectionRule, InjectionStatus
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# HTML patterns for locating injection points
|
|
26
|
+
# Maps InjectionLocation to list of (pattern, insert_position)
|
|
27
|
+
# insert_position: "after" = after opening tag, "before" = before the tag
|
|
28
|
+
LOCATION_PATTERNS: Dict[InjectionLocation, List[Tuple[str, str]]] = {
|
|
29
|
+
# === Page Structure ===
|
|
30
|
+
InjectionLocation.HEAD: [
|
|
31
|
+
(r'<head[^>]*>', 'after'),
|
|
32
|
+
],
|
|
33
|
+
InjectionLocation.BODY_START: [
|
|
34
|
+
(r'<body[^>]*>', 'after'),
|
|
35
|
+
],
|
|
36
|
+
InjectionLocation.BODY_END: [
|
|
37
|
+
(r'</body>', 'before'),
|
|
38
|
+
],
|
|
39
|
+
|
|
40
|
+
# === News Page Structure ===
|
|
41
|
+
InjectionLocation.NEWS_HEADER: [
|
|
42
|
+
(r'<[^>]*data-section="news-header"[^>]*>', 'after'),
|
|
43
|
+
(r'<[^>]*data-section="news-page"[^>]*>', 'after'),
|
|
44
|
+
(r'<[^>]*data-section="news-featured"[^>]*>', 'after'),
|
|
45
|
+
],
|
|
46
|
+
|
|
47
|
+
# === News Item (individual article) ===
|
|
48
|
+
InjectionLocation.NEWS_ITEM: [
|
|
49
|
+
# Any news item with data-section
|
|
50
|
+
(r'<[^>]*data-section="news-item"[^>]*>', 'after'),
|
|
51
|
+
# Fallback: any element with news-item class
|
|
52
|
+
(r'<[^>]*class="[^"]*news-item[^"]*"[^>]*>', 'after'),
|
|
53
|
+
],
|
|
54
|
+
|
|
55
|
+
# === News Headline ===
|
|
56
|
+
InjectionLocation.NEWS_TITLE: [
|
|
57
|
+
(r'<a[^>]*data-section="news-headline"[^>]*>', 'after'),
|
|
58
|
+
(r'<[^>]*class="[^"]*news-headline[^"]*"[^>]*>', 'after'),
|
|
59
|
+
],
|
|
60
|
+
|
|
61
|
+
# === News Summary ===
|
|
62
|
+
InjectionLocation.NEWS_SUMMARY: [
|
|
63
|
+
(r'<[^>]*data-section="news-summary"[^>]*>', 'after'),
|
|
64
|
+
(r'<p[^>]*class="[^"]*news-summary[^"]*"[^>]*>', 'after'),
|
|
65
|
+
],
|
|
66
|
+
|
|
67
|
+
# === News Source Badge ===
|
|
68
|
+
InjectionLocation.NEWS_SOURCE: [
|
|
69
|
+
(r'<span[^>]*class="[^"]*news-source[^"]*"[^>]*>', 'after'),
|
|
70
|
+
],
|
|
71
|
+
|
|
72
|
+
# === News Meta (source + time) ===
|
|
73
|
+
InjectionLocation.NEWS_META: [
|
|
74
|
+
(r'<[^>]*class="[^"]*news-meta[^"]*"[^>]*>', 'after'),
|
|
75
|
+
],
|
|
76
|
+
|
|
77
|
+
# === News Footer ===
|
|
78
|
+
InjectionLocation.NEWS_FOOTER: [
|
|
79
|
+
(r'<[^>]*class="[^"]*news-footer[^"]*"[^>]*>', 'after'),
|
|
80
|
+
],
|
|
81
|
+
|
|
82
|
+
# === Article Page ===
|
|
83
|
+
InjectionLocation.ARTICLE_CONTENT: [
|
|
84
|
+
(r'<[^>]*data-section="article-content"[^>]*>', 'after'),
|
|
85
|
+
],
|
|
86
|
+
|
|
87
|
+
InjectionLocation.ARTICLE_BODY: [
|
|
88
|
+
(r'<[^>]*data-section="article-body"[^>]*>', 'after'),
|
|
89
|
+
],
|
|
90
|
+
|
|
91
|
+
InjectionLocation.ARTICLE_SUMMARY: [
|
|
92
|
+
(r'<[^>]*data-section="article-summary"[^>]*>', 'after'),
|
|
93
|
+
],
|
|
94
|
+
|
|
95
|
+
# === News List Container ===
|
|
96
|
+
InjectionLocation.NEWS_LIST: [
|
|
97
|
+
(r'<[^>]*data-section="news-list"[^>]*>', 'after'),
|
|
98
|
+
(r'<[^>]*data-section="quote-news"[^>]*>', 'after'),
|
|
99
|
+
(r'<ul[^>]*class="[^"]*news-list[^"]*"[^>]*>', 'after'),
|
|
100
|
+
],
|
|
101
|
+
|
|
102
|
+
# === News Sidebar ===
|
|
103
|
+
InjectionLocation.NEWS_SIDEBAR: [
|
|
104
|
+
(r'<[^>]*data-section="browse-news"[^>]*>', 'after'),
|
|
105
|
+
],
|
|
106
|
+
|
|
107
|
+
# === Trending Widget ===
|
|
108
|
+
InjectionLocation.TRENDING: [
|
|
109
|
+
(r'<[^>]*data-section="trending-stocks"[^>]*>', 'after'),
|
|
110
|
+
(r'<[^>]*data-section="trending-item"[^>]*>', 'after'),
|
|
111
|
+
],
|
|
112
|
+
|
|
113
|
+
# === Stock/Quote Elements ===
|
|
114
|
+
InjectionLocation.STOCK_HEADER: [
|
|
115
|
+
(r'<[^>]*data-section="quote-header"[^>]*>', 'after'),
|
|
116
|
+
(r'<[^>]*data-section="quote-page"[^>]*>', 'after'),
|
|
117
|
+
],
|
|
118
|
+
InjectionLocation.STOCK_INFO: [
|
|
119
|
+
(r'<[^>]*data-section="quote-header"[^>]*>', 'after'),
|
|
120
|
+
(r'<[^>]*data-section="stock-info"[^>]*>', 'after'),
|
|
121
|
+
(r'<[^>]*data-section="quote-page"[^>]*>', 'after'),
|
|
122
|
+
],
|
|
123
|
+
InjectionLocation.STOCK_PRICE: [
|
|
124
|
+
(r'<[^>]*data-section="quote-price"[^>]*>', 'after'),
|
|
125
|
+
],
|
|
126
|
+
InjectionLocation.STOCK_STATS: [
|
|
127
|
+
(r'<[^>]*data-section="quick-stats"[^>]*>', 'after'),
|
|
128
|
+
(r'<[^>]*data-section="key-statistics"[^>]*>', 'after'),
|
|
129
|
+
],
|
|
130
|
+
InjectionLocation.STOCK_DESCRIPTION: [
|
|
131
|
+
(r'<[^>]*data-section="company-info"[^>]*>', 'after'),
|
|
132
|
+
],
|
|
133
|
+
|
|
134
|
+
# === Chart/Analysis ===
|
|
135
|
+
InjectionLocation.CHART_AREA: [
|
|
136
|
+
(r'<[^>]*data-section="quote-chart"[^>]*>', 'after'),
|
|
137
|
+
(r'<[^>]*data-section="mini-chart"[^>]*>', 'after'),
|
|
138
|
+
],
|
|
139
|
+
InjectionLocation.INDICATOR_SECTION: [
|
|
140
|
+
(r'<[^>]*data-section="analyst-rating"[^>]*>', 'after'),
|
|
141
|
+
],
|
|
142
|
+
|
|
143
|
+
# === Portfolio ===
|
|
144
|
+
InjectionLocation.PORTFOLIO_SUMMARY: [
|
|
145
|
+
(r'<[^>]*data-section="portfolio-summary"[^>]*>', 'after'),
|
|
146
|
+
(r'<[^>]*data-section="account-summary"[^>]*>', 'after'),
|
|
147
|
+
(r'<[^>]*data-section="portfolio-page"[^>]*>', 'after'),
|
|
148
|
+
],
|
|
149
|
+
InjectionLocation.PORTFOLIO_TABLE: [
|
|
150
|
+
(r'<[^>]*data-section="portfolio-holdings"[^>]*>', 'after'),
|
|
151
|
+
(r'<[^>]*data-section="summary-holdings"[^>]*>', 'after'),
|
|
152
|
+
],
|
|
153
|
+
InjectionLocation.POSITION_ROW: [
|
|
154
|
+
(r'<[^>]*data-section="holding-row"[^>]*>', 'after'),
|
|
155
|
+
],
|
|
156
|
+
InjectionLocation.CASH_BALANCE: [
|
|
157
|
+
(r'<[^>]*data-section="summary-cash"[^>]*>', 'after'),
|
|
158
|
+
(r'<[^>]*data-section="account-info"[^>]*>', 'after'),
|
|
159
|
+
],
|
|
160
|
+
|
|
161
|
+
# === Trading ===
|
|
162
|
+
InjectionLocation.TRADE_FORM: [
|
|
163
|
+
(r'<[^>]*data-section="trade-form"[^>]*>', 'after'),
|
|
164
|
+
(r'<[^>]*data-section="trade-panel"[^>]*>', 'after'),
|
|
165
|
+
(r'<[^>]*data-section="trading-page"[^>]*>', 'after'),
|
|
166
|
+
],
|
|
167
|
+
InjectionLocation.ORDER_BUTTON: [
|
|
168
|
+
(r'<[^>]*data-section="submit-trade"[^>]*>', 'after'),
|
|
169
|
+
],
|
|
170
|
+
|
|
171
|
+
# === Comments Section ===
|
|
172
|
+
InjectionLocation.COMMENTS_SECTION: [
|
|
173
|
+
(r'<[^>]*data-section="comments-section"[^>]*>', 'after'),
|
|
174
|
+
(r'<[^>]*data-section="comments-list"[^>]*>', 'after'),
|
|
175
|
+
],
|
|
176
|
+
InjectionLocation.COMMENT_ITEM: [
|
|
177
|
+
(r'<[^>]*data-section="comment-item"[^>]*>', 'after'),
|
|
178
|
+
],
|
|
179
|
+
InjectionLocation.TAB_CONTENT: [
|
|
180
|
+
(r'<[^>]*data-section="tab-content"[^>]*>', 'after'),
|
|
181
|
+
],
|
|
182
|
+
|
|
183
|
+
# === Main Content ===
|
|
184
|
+
InjectionLocation.MAIN_CONTENT: [
|
|
185
|
+
(r'<[^>]*data-section="quote-main"[^>]*>', 'after'),
|
|
186
|
+
(r'<main[^>]*>', 'after'),
|
|
187
|
+
],
|
|
188
|
+
|
|
189
|
+
# === Sidebar ===
|
|
190
|
+
InjectionLocation.SIDEBAR: [
|
|
191
|
+
(r'<[^>]*data-section="quote-sidebar"[^>]*>', 'after'),
|
|
192
|
+
(r'<[^>]*data-section="quote-sidebar-right"[^>]*>', 'after'),
|
|
193
|
+
],
|
|
194
|
+
InjectionLocation.SIDEBAR_WIDGET: [
|
|
195
|
+
(r'<[^>]*data-section="popular-stocks"[^>]*>', 'after'),
|
|
196
|
+
],
|
|
197
|
+
|
|
198
|
+
# === Header/Nav ===
|
|
199
|
+
InjectionLocation.HEADER: [
|
|
200
|
+
(r'<header[^>]*>', 'after'),
|
|
201
|
+
(r'<[^>]*data-section="portfolio-header"[^>]*>', 'after'),
|
|
202
|
+
(r'<[^>]*data-section="markets-header"[^>]*>', 'after'),
|
|
203
|
+
],
|
|
204
|
+
InjectionLocation.NAV: [
|
|
205
|
+
(r'<nav[^>]*>', 'after'),
|
|
206
|
+
],
|
|
207
|
+
|
|
208
|
+
# === Footer ===
|
|
209
|
+
InjectionLocation.FOOTER: [
|
|
210
|
+
(r'<footer[^>]*>', 'after'),
|
|
211
|
+
],
|
|
212
|
+
InjectionLocation.DISCLAIMER: [
|
|
213
|
+
(r'<[^>]*class="[^"]*disclaimer[^"]*"[^>]*>', 'after'),
|
|
214
|
+
],
|
|
215
|
+
|
|
216
|
+
# === Metadata (invisible) ===
|
|
217
|
+
InjectionLocation.METADATA: [
|
|
218
|
+
(r'<head[^>]*>', 'after'),
|
|
219
|
+
],
|
|
220
|
+
|
|
221
|
+
# === Markets ===
|
|
222
|
+
InjectionLocation.MARKET_INDICES: [
|
|
223
|
+
(r'<[^>]*data-section="market-indices"[^>]*>', 'after'),
|
|
224
|
+
(r'<[^>]*data-section="market-index"[^>]*>', 'after'),
|
|
225
|
+
],
|
|
226
|
+
InjectionLocation.TOP_GAINERS: [
|
|
227
|
+
(r'<[^>]*data-section="movers-gainers"[^>]*>', 'after'),
|
|
228
|
+
],
|
|
229
|
+
InjectionLocation.TOP_LOSERS: [
|
|
230
|
+
(r'<[^>]*data-section="movers-losers"[^>]*>', 'after'),
|
|
231
|
+
],
|
|
232
|
+
InjectionLocation.TRENDING_STOCKS: [
|
|
233
|
+
(r'<[^>]*data-section="trending-stocks"[^>]*>', 'after'),
|
|
234
|
+
],
|
|
235
|
+
|
|
236
|
+
# === Options ===
|
|
237
|
+
InjectionLocation.OPTIONS_CHAIN: [
|
|
238
|
+
(r'<[^>]*data-section="options-link"[^>]*>', 'after'),
|
|
239
|
+
(r'<[^>]*data-section="option-row"[^>]*>', 'after'),
|
|
240
|
+
(r'<[^>]*data-section="portfolio-options"[^>]*>', 'after'),
|
|
241
|
+
],
|
|
242
|
+
|
|
243
|
+
# === Orders ===
|
|
244
|
+
InjectionLocation.TRADE_CONFIRMATION: [
|
|
245
|
+
(r'<[^>]*data-section="order-summary"[^>]*>', 'after'),
|
|
246
|
+
(r'<[^>]*data-section="trade-summary"[^>]*>', 'after'),
|
|
247
|
+
],
|
|
248
|
+
|
|
249
|
+
# === Analyst/Related ===
|
|
250
|
+
InjectionLocation.RELATED_STOCKS: [
|
|
251
|
+
(r'<[^>]*data-section="stock-card"[^>]*>', 'after'),
|
|
252
|
+
(r'<[^>]*data-section="stock-item"[^>]*>', 'after'),
|
|
253
|
+
],
|
|
254
|
+
InjectionLocation.WATCHLIST: [
|
|
255
|
+
(r'<[^>]*data-section="analyst-rating"[^>]*>', 'after'),
|
|
256
|
+
],
|
|
257
|
+
InjectionLocation.HOLDERS_INFO: [
|
|
258
|
+
(r'<[^>]*data-section="holders-info"[^>]*>', 'after'),
|
|
259
|
+
],
|
|
260
|
+
InjectionLocation.EARNINGS_CALENDAR: [
|
|
261
|
+
(r'<[^>]*data-section="earnings-calendar"[^>]*>', 'after'),
|
|
262
|
+
],
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class HtmlInjector:
|
|
267
|
+
"""
|
|
268
|
+
Engine for injecting content into HTML.
|
|
269
|
+
|
|
270
|
+
Static injection model:
|
|
271
|
+
- Rules are filtered by page_name (bound to specific pages)
|
|
272
|
+
- ALL matching elements on the page get injected
|
|
273
|
+
- No dynamic discovery, no locking, no view-order matching
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
def __init__(self):
|
|
277
|
+
self.config: Optional[InjectionConfig] = None
|
|
278
|
+
self._injection_count = 0
|
|
279
|
+
self._renderer = InjectionRenderer()
|
|
280
|
+
|
|
281
|
+
def set_config(self, config: InjectionConfig):
|
|
282
|
+
"""Set the injection configuration."""
|
|
283
|
+
self.config = config
|
|
284
|
+
self._injection_count = 0
|
|
285
|
+
|
|
286
|
+
def clear(self):
|
|
287
|
+
"""Clear the configuration (reset to benign)."""
|
|
288
|
+
self.config = None
|
|
289
|
+
self._injection_count = 0
|
|
290
|
+
|
|
291
|
+
def is_malicious(self) -> bool:
|
|
292
|
+
"""Check if in malicious mode."""
|
|
293
|
+
return self.config is not None and self.config.mode == "malicious"
|
|
294
|
+
|
|
295
|
+
def get_status(self) -> InjectionStatus:
|
|
296
|
+
"""Get current injection status."""
|
|
297
|
+
return InjectionStatus(
|
|
298
|
+
mode=self.config.mode if self.config else "benign",
|
|
299
|
+
config_name=self.config.name if self.config else None,
|
|
300
|
+
rules_count=len(self.config.rules) if self.config else 0,
|
|
301
|
+
injections_applied=self._injection_count,
|
|
302
|
+
target_action=self.config.target_action if self.config else None
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
def apply_injections(self, html: str, page_name: str, symbol: str = "", article_id: int = 0) -> str:
|
|
306
|
+
"""
|
|
307
|
+
Apply all applicable injections to HTML.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
html: Original HTML content
|
|
311
|
+
page_name: Name of the current page (e.g., "quote", "news", "portfolios")
|
|
312
|
+
symbol: Stock symbol (for per-symbol filtering)
|
|
313
|
+
article_id: Article ID (for per-article filtering)
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
HTML with injections applied (or original if benign mode)
|
|
317
|
+
"""
|
|
318
|
+
if not self.config or self.config.mode != "malicious":
|
|
319
|
+
return html
|
|
320
|
+
|
|
321
|
+
for rule in self._get_sorted_rules(page_name, symbol, article_id):
|
|
322
|
+
if not rule.enabled:
|
|
323
|
+
continue
|
|
324
|
+
html = self._inject_at_location(html, rule)
|
|
325
|
+
|
|
326
|
+
return html
|
|
327
|
+
|
|
328
|
+
def _inject_at_location(self, html: str, rule: InjectionRule) -> str:
|
|
329
|
+
"""
|
|
330
|
+
Inject content at ALL matching elements for the rule's location.
|
|
331
|
+
"""
|
|
332
|
+
location = rule.location
|
|
333
|
+
patterns = LOCATION_PATTERNS.get(location, [])
|
|
334
|
+
injection = self._render_rule(rule)
|
|
335
|
+
|
|
336
|
+
if not patterns:
|
|
337
|
+
# Fallback: inject before </body>
|
|
338
|
+
self._injection_count += 1
|
|
339
|
+
return html.replace("</body>", injection + "</body>")
|
|
340
|
+
|
|
341
|
+
# Collect all matches across all patterns, deduplicated by position
|
|
342
|
+
all_matches = []
|
|
343
|
+
used_positions = set()
|
|
344
|
+
|
|
345
|
+
for pattern, position in patterns:
|
|
346
|
+
for match in re.finditer(pattern, html, re.IGNORECASE):
|
|
347
|
+
if match.start() not in used_positions:
|
|
348
|
+
all_matches.append((match, position))
|
|
349
|
+
used_positions.add(match.start())
|
|
350
|
+
|
|
351
|
+
if not all_matches:
|
|
352
|
+
# No matches found — fallback to body end
|
|
353
|
+
self._injection_count += 1
|
|
354
|
+
return html.replace("</body>", injection + "</body>")
|
|
355
|
+
|
|
356
|
+
# Sort by position descending so we inject from end to start
|
|
357
|
+
# (preserves earlier match positions)
|
|
358
|
+
all_matches.sort(key=lambda x: x[0].start(), reverse=True)
|
|
359
|
+
|
|
360
|
+
for match, position in all_matches:
|
|
361
|
+
html = self._insert_at_match(html, match, injection, position)
|
|
362
|
+
self._injection_count += 1
|
|
363
|
+
|
|
364
|
+
return html
|
|
365
|
+
|
|
366
|
+
def _insert_at_match(self, html: str, match: re.Match, injection: str, position: str) -> str:
|
|
367
|
+
"""Insert injection at the matched position."""
|
|
368
|
+
if position == "after":
|
|
369
|
+
insert_pos = match.end()
|
|
370
|
+
else:
|
|
371
|
+
insert_pos = match.start()
|
|
372
|
+
return html[:insert_pos] + injection + html[insert_pos:]
|
|
373
|
+
|
|
374
|
+
def _get_sorted_rules(self, page_name: str, symbol: str = "", article_id: int = 0) -> List[InjectionRule]:
|
|
375
|
+
"""Get rules that apply to this page, sorted by priority (highest first)."""
|
|
376
|
+
if not self.config:
|
|
377
|
+
return []
|
|
378
|
+
|
|
379
|
+
rules = []
|
|
380
|
+
for rule in self.config.rules:
|
|
381
|
+
if not rule.enabled:
|
|
382
|
+
continue
|
|
383
|
+
# Static page binding: if pages is set, only inject on those pages
|
|
384
|
+
if rule.pages and page_name not in rule.pages:
|
|
385
|
+
continue
|
|
386
|
+
if rule.symbols and symbol.upper() not in [s.upper() for s in rule.symbols]:
|
|
387
|
+
continue
|
|
388
|
+
if rule.article_id and article_id and rule.article_id != article_id:
|
|
389
|
+
continue
|
|
390
|
+
rules.append(rule)
|
|
391
|
+
|
|
392
|
+
rules.sort(key=lambda r: -r.priority)
|
|
393
|
+
return rules
|
|
394
|
+
|
|
395
|
+
def _render_rule(self, rule: InjectionRule) -> str:
|
|
396
|
+
"""Render HTML for a rule."""
|
|
397
|
+
prompt = rule.prompt
|
|
398
|
+
|
|
399
|
+
if rule.wrap_in_context and rule.context_template:
|
|
400
|
+
prompt = rule.context_template.replace("{prompt}", prompt)
|
|
401
|
+
|
|
402
|
+
html = self._renderer.render(prompt, rule.method)
|
|
403
|
+
return html * rule.repetitions
|
|
404
|
+
|
|
405
|
+
def get_injection_count(self) -> int:
|
|
406
|
+
return self._injection_count
|
|
407
|
+
|
|
408
|
+
def reset_count(self):
|
|
409
|
+
self._injection_count = 0
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Injection Locations - Define all possible injection points in HTML
|
|
3
|
+
|
|
4
|
+
These locations map to data-section attributes and CSS classes in the templates.
|
|
5
|
+
The injection engine uses regex patterns to find these locations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from enum import Enum
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InjectionLocation(Enum):
|
|
12
|
+
"""All possible locations where injection can be placed in HTML."""
|
|
13
|
+
|
|
14
|
+
# === Page Structure ===
|
|
15
|
+
HEAD = "head"
|
|
16
|
+
BODY_START = "body_start"
|
|
17
|
+
BODY_END = "body_end"
|
|
18
|
+
|
|
19
|
+
# === Header/Navigation ===
|
|
20
|
+
HEADER = "header"
|
|
21
|
+
LOGO = "logo"
|
|
22
|
+
SEARCH_BAR = "search_bar"
|
|
23
|
+
NAV = "nav"
|
|
24
|
+
NAV_ITEM = "nav_item"
|
|
25
|
+
|
|
26
|
+
# === Main Content ===
|
|
27
|
+
MAIN_CONTENT = "main_content"
|
|
28
|
+
PAGE_TITLE = "page_title"
|
|
29
|
+
BREADCRUMB = "breadcrumb"
|
|
30
|
+
|
|
31
|
+
# === Stock/Quote Page ===
|
|
32
|
+
STOCK_HEADER = "stock_header"
|
|
33
|
+
STOCK_PRICE = "stock_price"
|
|
34
|
+
STOCK_CHANGE = "stock_change"
|
|
35
|
+
STOCK_INFO = "stock_info"
|
|
36
|
+
STOCK_STATS = "stock_stats"
|
|
37
|
+
STOCK_DESCRIPTION = "stock_desc"
|
|
38
|
+
|
|
39
|
+
# === Chart/Analysis ===
|
|
40
|
+
CHART_AREA = "chart_area"
|
|
41
|
+
CHART_LEGEND = "chart_legend"
|
|
42
|
+
INDICATOR_SECTION = "indicators"
|
|
43
|
+
|
|
44
|
+
# === News - Page Level ===
|
|
45
|
+
NEWS_HEADER = "news_header" # data-section="news-header" - Page header area
|
|
46
|
+
NEWS_LIST = "news_list" # data-section="news-list" - News list container
|
|
47
|
+
NEWS_SIDEBAR = "news_sidebar" # data-section="news-sidebar" - Right sidebar
|
|
48
|
+
|
|
49
|
+
# === News - Item Level ===
|
|
50
|
+
NEWS_ITEM = "news_item" # data-section="news-item" - Individual news item
|
|
51
|
+
NEWS_TITLE = "news_title" # data-section="news-headline" - News headline
|
|
52
|
+
NEWS_SUMMARY = "news_summary" # data-section="news-summary" - News summary
|
|
53
|
+
NEWS_SOURCE = "news_source" # data-section="news-source" - News source
|
|
54
|
+
NEWS_META = "news_meta" # data-section="news-meta" - Meta info area
|
|
55
|
+
NEWS_FOOTER = "news_footer" # data-section="news-footer" - News footer
|
|
56
|
+
|
|
57
|
+
# === Article Page (full article view) ===
|
|
58
|
+
ARTICLE_CONTENT = "article_content" # data-section="article-content" - Article main container
|
|
59
|
+
ARTICLE_BODY = "article_body" # data-section="article-body" - Article body
|
|
60
|
+
ARTICLE_SUMMARY = "article_summary" # data-section="article-summary" - Article summary
|
|
61
|
+
|
|
62
|
+
# === News - Widgets ===
|
|
63
|
+
TRENDING = "trending" # data-section="trending-widget" - Trending widget
|
|
64
|
+
DATE_FILTER = "date_filter" # data-section="date-filter" - Date filter
|
|
65
|
+
|
|
66
|
+
# === Comments (in news articles) ===
|
|
67
|
+
COMMENTS_SECTION = "comments_section"
|
|
68
|
+
COMMENT_ITEM = "comment_item"
|
|
69
|
+
|
|
70
|
+
# === Portfolio ===
|
|
71
|
+
PORTFOLIO_SUMMARY = "portfolio_summary"
|
|
72
|
+
PORTFOLIO_TABLE = "portfolio_table"
|
|
73
|
+
POSITION_ROW = "position_row"
|
|
74
|
+
CASH_BALANCE = "cash_balance"
|
|
75
|
+
|
|
76
|
+
# === Trading ===
|
|
77
|
+
TRADE_FORM = "trade_form"
|
|
78
|
+
ORDER_BUTTON = "order_button"
|
|
79
|
+
TRADE_CONFIRMATION = "trade_confirm"
|
|
80
|
+
|
|
81
|
+
# === Options ===
|
|
82
|
+
OPTIONS_CHAIN = "options_chain"
|
|
83
|
+
OPTIONS_HEADER = "options_header"
|
|
84
|
+
|
|
85
|
+
# === Analysis ===
|
|
86
|
+
ANALYST_RATINGS = "analyst_ratings"
|
|
87
|
+
PRICE_TARGETS = "price_targets"
|
|
88
|
+
|
|
89
|
+
# === Sidebar ===
|
|
90
|
+
SIDEBAR = "sidebar"
|
|
91
|
+
SIDEBAR_WIDGET = "sidebar_widget"
|
|
92
|
+
RELATED_STOCKS = "related_stocks"
|
|
93
|
+
WATCHLIST = "watchlist"
|
|
94
|
+
HOLDERS_INFO = "holders_info"
|
|
95
|
+
EARNINGS_CALENDAR = "earnings_calendar"
|
|
96
|
+
|
|
97
|
+
# === Markets Page ===
|
|
98
|
+
MARKET_INDICES = "market_indices"
|
|
99
|
+
TOP_GAINERS = "top_gainers"
|
|
100
|
+
TOP_LOSERS = "top_losers"
|
|
101
|
+
TRENDING_STOCKS = "trending_stocks"
|
|
102
|
+
|
|
103
|
+
# === Options Page ===
|
|
104
|
+
OPTION_ROW = "option_row"
|
|
105
|
+
|
|
106
|
+
# === Footer & Misc ===
|
|
107
|
+
FOOTER = "footer"
|
|
108
|
+
DISCLAIMER = "disclaimer"
|
|
109
|
+
COPYRIGHT = "copyright"
|
|
110
|
+
|
|
111
|
+
# === Interactive Elements ===
|
|
112
|
+
POPUP = "popup"
|
|
113
|
+
TOOLTIP = "tooltip"
|
|
114
|
+
DROPDOWN = "dropdown"
|
|
115
|
+
TAB_CONTENT = "tab_content"
|
|
116
|
+
|
|
117
|
+
# === Status/Feedback ===
|
|
118
|
+
ERROR_MESSAGE = "error"
|
|
119
|
+
SUCCESS_MESSAGE = "success"
|
|
120
|
+
WARNING = "warning"
|
|
121
|
+
LOADING = "loading"
|
|
122
|
+
EMPTY_STATE = "empty_state"
|
|
123
|
+
|
|
124
|
+
# === Technical/Hidden ===
|
|
125
|
+
METADATA = "metadata"
|
|
126
|
+
SCRIPT = "script"
|
|
127
|
+
JSON_LD = "json_ld"
|
|
128
|
+
INLINE_STYLE = "inline_style"
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def get_all(cls):
|
|
132
|
+
"""Get all location values."""
|
|
133
|
+
return [loc.value for loc in cls]
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def get_news_locations(cls):
|
|
137
|
+
"""Get all news-related locations for targeted attacks."""
|
|
138
|
+
return [
|
|
139
|
+
cls.NEWS_HEADER, # Page header
|
|
140
|
+
cls.NEWS_LIST, # News list container
|
|
141
|
+
cls.NEWS_ITEM, # Individual news item
|
|
142
|
+
cls.NEWS_TITLE, # News headline
|
|
143
|
+
cls.NEWS_SUMMARY, # News summary text
|
|
144
|
+
cls.NEWS_SOURCE, # News source badge
|
|
145
|
+
cls.NEWS_META, # Meta info area
|
|
146
|
+
cls.NEWS_FOOTER, # News item footer
|
|
147
|
+
cls.NEWS_SIDEBAR, # Sidebar
|
|
148
|
+
cls.TRENDING, # Trending widget
|
|
149
|
+
cls.COMMENTS_SECTION, # Comments
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def get_quote_locations(cls):
|
|
154
|
+
"""Get all quote page locations."""
|
|
155
|
+
return [
|
|
156
|
+
cls.STOCK_HEADER, cls.STOCK_PRICE, cls.STOCK_INFO,
|
|
157
|
+
cls.STOCK_STATS, cls.CHART_AREA, cls.INDICATOR_SECTION,
|
|
158
|
+
cls.SIDEBAR, cls.TRADE_FORM
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
def get_portfolio_locations(cls):
|
|
163
|
+
"""Get all portfolio page locations."""
|
|
164
|
+
return [
|
|
165
|
+
cls.PORTFOLIO_SUMMARY, cls.PORTFOLIO_TABLE,
|
|
166
|
+
cls.POSITION_ROW, cls.CASH_BALANCE
|
|
167
|
+
]
|