switch-trust-cli 1.7.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.
- switch_trust/__init__.py +0 -0
- switch_trust/cli/__init__.py +0 -0
- switch_trust/cli/__main__.py +3 -0
- switch_trust/cli/builtin_config.json +879 -0
- switch_trust/cli/console.py +127 -0
- switch_trust/cli/eval_cli.py +905 -0
- switch_trust/cli/file_filter.py +172 -0
- switch_trust/cli/init_cli.py +156 -0
- switch_trust/cli/main.py +384 -0
- switch_trust/cli/output_formatters.py +482 -0
- switch_trust/cli/rich_observer.py +89 -0
- switch_trust/cli/runner.py +274 -0
- switch_trust/cli/scan_cli.py +314 -0
- switch_trust/cli/telemetry.py +430 -0
- switch_trust/cli/utils.py +158 -0
- switch_trust/cli/version.py +1 -0
- switch_trust/data/detector_prompts/llm01_adversarial.txt +59 -0
- switch_trust/data/detector_prompts/llm01_fixed.txt +6 -0
- switch_trust/data/detector_prompts/llm02_adversarial.txt +79 -0
- switch_trust/data/detector_prompts/llm02_fixed.txt +6 -0
- switch_trust/data/detector_prompts/llm05_adversarial.txt +67 -0
- switch_trust/data/detector_prompts/llm05_fixed.txt +14 -0
- switch_trust/data/detector_prompts/llm06_adversarial.txt +139 -0
- switch_trust/data/detector_prompts/llm06_fixed.txt +13 -0
- switch_trust/data/detector_prompts/llm07_adversarial.txt +189 -0
- switch_trust/data/detector_prompts/llm07_fixed.txt +12 -0
- switch_trust/data/detector_prompts/llm09_adversarial.txt +83 -0
- switch_trust/data/detector_prompts/llm09_fixed.txt +13 -0
- switch_trust/data/llm01_prompt_injection.csv +6708 -0
- switch_trust/data/llm02_sensitive_information.csv +1069 -0
- switch_trust/data/llm05_unsafe_output.csv +3882 -0
- switch_trust/data/llm06_excessive_agency.csv +778 -0
- switch_trust/data/llm07_system_prompt_leakage.csv +971 -0
- switch_trust/data/llm09_hallucination-goals.csv +32419 -0
- switch_trust/data/llm09_hallucination.csv +599 -0
- switch_trust/data/pii_leakage.csv +706 -0
- switch_trust/data/secret_leakage.csv +1380 -0
- switch_trust/eval/__init__.py +0 -0
- switch_trust/eval/common/__init__.py +0 -0
- switch_trust/eval/common/converter_anthropic.py +172 -0
- switch_trust/eval/common/converter_genai.py +171 -0
- switch_trust/eval/common/converter_openai.py +198 -0
- switch_trust/eval/common/log.py +116 -0
- switch_trust/eval/common/reference.py +25 -0
- switch_trust/eval/common/schema.py +190 -0
- switch_trust/eval/common/utils.py +119 -0
- switch_trust/eval/core/__init__.py +0 -0
- switch_trust/eval/core/detectors/__init__.py +0 -0
- switch_trust/eval/core/detectors/detector.py +25 -0
- switch_trust/eval/core/detectors/detector_garak.py +112 -0
- switch_trust/eval/core/detectors/detector_model.py +123 -0
- switch_trust/eval/core/detectors/detector_model_adversarial.py +91 -0
- switch_trust/eval/core/detectors/detector_pii.py +67 -0
- switch_trust/eval/core/detectors/detector_secret.py +88 -0
- switch_trust/eval/core/detectors/detector_topic_guard.py +63 -0
- switch_trust/eval/core/detectors/detector_toxicity.py +78 -0
- switch_trust/eval/core/eval/__init__.py +0 -0
- switch_trust/eval/core/eval/eval_creator.py +166 -0
- switch_trust/eval/core/eval/evaluation.py +112 -0
- switch_trust/eval/core/eval/evaluation_adversarial.py +412 -0
- switch_trust/eval/core/eval/evaluation_garak_module.py +45 -0
- switch_trust/eval/core/eval/evaluation_garak_probe.py +308 -0
- switch_trust/eval/core/eval/evaluation_message_list.py +46 -0
- switch_trust/eval/core/eval/evaluation_multi.py +276 -0
- switch_trust/eval/core/eval/evaluation_single.py +89 -0
- switch_trust/eval/core/eval/evaluation_single_prompt.py +62 -0
- switch_trust/eval/core/eval/evaluation_topic_guard.py +305 -0
- switch_trust/eval/core/eval/metric_conciseness.py +179 -0
- switch_trust/eval/core/eval/metric_factual_accuracy.py +312 -0
- switch_trust/eval/core/eval/metric_instruction_adherence.py +177 -0
- switch_trust/eval/core/eval/metric_tone.py +180 -0
- switch_trust/eval/core/eval/metric_toxicity.py +146 -0
- switch_trust/eval/core/eval/observer.py +137 -0
- switch_trust/eval/core/message/__init__.py +0 -0
- switch_trust/eval/core/message/message_collection.py +37 -0
- switch_trust/eval/core/message/message_collection_csv.py +44 -0
- switch_trust/eval/core/message/message_collection_garak.py +46 -0
- switch_trust/eval/core/message/message_collection_memory.py +34 -0
- switch_trust/eval/core/models/__init__.py +0 -0
- switch_trust/eval/core/models/generator_model.py +421 -0
- switch_trust/eval/core/models/model.py +172 -0
- switch_trust/eval/core/models/model_adk.py +186 -0
- switch_trust/eval/core/models/model_anthropic.py +79 -0
- switch_trust/eval/core/models/model_anthropic_agent.py +104 -0
- switch_trust/eval/core/models/model_gemini.py +146 -0
- switch_trust/eval/core/models/model_generic_http.py +120 -0
- switch_trust/eval/core/models/model_http.py +135 -0
- switch_trust/eval/core/models/model_huggingface.py +75 -0
- switch_trust/eval/core/models/model_langserve.py +96 -0
- switch_trust/eval/core/models/model_litellm.py +52 -0
- switch_trust/eval/core/models/model_ollama.py +65 -0
- switch_trust/eval/core/models/model_openai.py +71 -0
- switch_trust/eval/core/models/model_openai_agent.py +92 -0
- switch_trust/eval/core/models/model_openai_compatible.py +66 -0
- switch_trust/eval/core/models/model_per_endpoint_limiter.py +80 -0
- switch_trust/eval/core/models/model_retry.py +204 -0
- switch_trust/eval/core/models/model_sync_wrapper.py +38 -0
- switch_trust/eval/core/models/model_vertex_agent_runtime.py +315 -0
- switch_trust/eval/core/models/response_schema.py +148 -0
- switch_trust/eval/core/optional_deps.py +57 -0
- switch_trust/eval/db/__init__.py +0 -0
- switch_trust/eval/db/base/__init__.py +0 -0
- switch_trust/eval/db/base/common_types.py +16 -0
- switch_trust/eval/db/base/detectors/__init__.py +0 -0
- switch_trust/eval/db/base/detectors/detector_helpers.py +88 -0
- switch_trust/eval/db/base/detectors/detector_repository.py +96 -0
- switch_trust/eval/db/base/detectors/detector_types.py +77 -0
- switch_trust/eval/db/base/eval/__init__.py +0 -0
- switch_trust/eval/db/base/eval/eval_helpers.py +186 -0
- switch_trust/eval/db/base/eval/eval_repository.py +120 -0
- switch_trust/eval/db/base/eval/eval_run.py +142 -0
- switch_trust/eval/db/base/eval/eval_types.py +127 -0
- switch_trust/eval/db/base/eval/model_eval_repository.py +79 -0
- switch_trust/eval/db/base/eval/model_eval_run_repository.py +72 -0
- switch_trust/eval/db/base/eval/model_eval_run_result_repository.py +42 -0
- switch_trust/eval/db/base/eval/model_eval_run_result_types.py +21 -0
- switch_trust/eval/db/base/eval/model_eval_run_types.py +78 -0
- switch_trust/eval/db/base/eval/model_eval_types.py +69 -0
- switch_trust/eval/db/base/message/__init__.py +0 -0
- switch_trust/eval/db/base/message/message_collection_helpers.py +50 -0
- switch_trust/eval/db/base/message/message_collection_repository.py +95 -0
- switch_trust/eval/db/base/message/message_collection_types.py +87 -0
- switch_trust/eval/db/base/models/__init__.py +0 -0
- switch_trust/eval/db/base/models/model_helpers.py +253 -0
- switch_trust/eval/db/base/models/model_repository.py +104 -0
- switch_trust/eval/db/base/models/model_types.py +159 -0
- switch_trust/eval/db/json/__init__.py +0 -0
- switch_trust/eval/db/json/repository_json.py +559 -0
- switch_trust/scan/__init__.py +12 -0
- switch_trust/scan/agent_scanner.py +812 -0
- switch_trust/scan/config/agent_opengrep_rules.yaml +533 -0
- switch_trust/scan/config/agent_taxonomy.json +517 -0
- switch_trust/scan/config/agentic_cvss_mapping.yaml +136 -0
- switch_trust/scan/config/compliance_mappings.json +110 -0
- switch_trust/scan/config/triage_prompt.txt +326 -0
- switch_trust/scan/constants.py +15 -0
- switch_trust/scan/exception_classification.py +179 -0
- switch_trust/scan/llm_provider.py +330 -0
- switch_trust/scan/opengrep_resolver.py +12 -0
- switch_trust/scan/reasoner.py +602 -0
- switch_trust/scan/schema.py +315 -0
- switch_trust/scan/scorer.py +372 -0
- switch_trust/scan/static_scanner.py +681 -0
- switch_trust/scan/taxonomy.py +121 -0
- switch_trust/scan/tool_dispatcher.py +773 -0
- switch_trust/scan/trace_logger.py +118 -0
- switch_trust/scan/trace_logger_file.py +143 -0
- switch_trust/scan/trace_logger_log.py +100 -0
- switch_trust/scan/triage.py +614 -0
- switch_trust/schema.py +79 -0
- switch_trust/secret_anonymizer.py +72 -0
- switch_trust_cli-1.7.0.dist-info/METADATA +495 -0
- switch_trust_cli-1.7.0.dist-info/RECORD +157 -0
- switch_trust_cli-1.7.0.dist-info/WHEEL +5 -0
- switch_trust_cli-1.7.0.dist-info/entry_points.txt +2 -0
- switch_trust_cli-1.7.0.dist-info/licenses/LICENSE +210 -0
- switch_trust_cli-1.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
taxonomy.py — AI Agent Vulnerability Taxonomy
|
|
3
|
+
Aligned to OWASP Top 10 for Agentic Applications 2026 (ASI01–ASI10).
|
|
4
|
+
|
|
5
|
+
Every finding maps to one of the 10 ASI categories. Findings that fall
|
|
6
|
+
outside the current OWASP framework are reported under the BEYOND-ASI
|
|
7
|
+
prefix with a descriptive name generated by the scanner or AI layer.
|
|
8
|
+
|
|
9
|
+
Reference: OWASP Agentic Security Initiative (ASI) Top 10 — Dec 2025
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
_CONFIG_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _load_json(filename: str) -> Any:
|
|
21
|
+
with open(os.path.join(_CONFIG_DIR, filename), encoding="utf-8") as f:
|
|
22
|
+
return json.load(f)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
AGENT_TAXONOMY: dict[str, dict[str, Any]] = _load_json("agent_taxonomy.json")
|
|
26
|
+
|
|
27
|
+
COMPLIANCE_MAPPINGS: dict[str, dict[str, list[str]]] = _load_json(
|
|
28
|
+
"compliance_mappings.json"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
BEYOND_ASI_SENTINEL = "BEYOND-ASI"
|
|
32
|
+
|
|
33
|
+
# Flat lookup: subcategory_key → full metadata (used by scorer.py)
|
|
34
|
+
FLAT_TAXONOMY: dict[str, dict[str, Any]] = {}
|
|
35
|
+
for _cat_key, _cat_data in AGENT_TAXONOMY.items():
|
|
36
|
+
for _subcat_key, _subcat_data in _cat_data["subcategories"].items():
|
|
37
|
+
FLAT_TAXONOMY[_subcat_key] = {
|
|
38
|
+
"category": _cat_key,
|
|
39
|
+
"asi_code": _cat_data["asi_code"],
|
|
40
|
+
"asi_title": _cat_data["asi_title"],
|
|
41
|
+
**_subcat_data,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _normalize_key(key: str) -> str:
|
|
46
|
+
"""Fold a key to its comparison form: lowercase, separators unified."""
|
|
47
|
+
return re.sub(r"[^a-z0-9]+", "_", key.strip().lower()).strip("_")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Comparison form → canonical subcategory key. The taxonomy has 36 subcategories
|
|
51
|
+
# and no key appears under two categories, which is what makes
|
|
52
|
+
# `category_for_subcategory` below unambiguous.
|
|
53
|
+
_SUBCATEGORY_BY_NORMALIZED: dict[str, str] = {
|
|
54
|
+
_normalize_key(key): key for key in FLAT_TAXONOMY
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def resolve_subcategory(subcategory: str) -> str | None:
|
|
59
|
+
"""Return the canonical subcategory key for *subcategory*, else ``None``.
|
|
60
|
+
|
|
61
|
+
Only case and separator differences are folded. Nothing fuzzier: guessing
|
|
62
|
+
which taxonomy entry a genuinely unrecognised string "meant" would silently
|
|
63
|
+
file a finding under the wrong ASI category, which is worse than routing it
|
|
64
|
+
to ``beyond_asi`` where a reader can see it did not classify.
|
|
65
|
+
"""
|
|
66
|
+
if not subcategory:
|
|
67
|
+
return None
|
|
68
|
+
if subcategory in FLAT_TAXONOMY:
|
|
69
|
+
return subcategory
|
|
70
|
+
return _SUBCATEGORY_BY_NORMALIZED.get(_normalize_key(subcategory))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def category_for_subcategory(subcategory: str) -> str:
|
|
74
|
+
"""Return the ASI category key that owns *subcategory*.
|
|
75
|
+
|
|
76
|
+
The category is a **function of** the subcategory, never an independent
|
|
77
|
+
input. Asking an LLM for both invites disagreement between them, and that is
|
|
78
|
+
exactly what happened: the system prompt renders each category as its code
|
|
79
|
+
and human title (``ASI03 — Identity and Privilege Abuse``) and never shows
|
|
80
|
+
the key, so the model inferred the key from the title and produced
|
|
81
|
+
``asi03_identity_and_privilege_abuse`` against a canonical
|
|
82
|
+
``asi03_identity_privilege_abuse``. Five of the ten titles do not round-trip
|
|
83
|
+
to their key, so the guess was wrong half the time — and a finding the
|
|
84
|
+
scanner had correctly identified was scored as both a miss and a false
|
|
85
|
+
positive. Derive; do not ask.
|
|
86
|
+
"""
|
|
87
|
+
return get_finding_metadata(subcategory)["category"]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_finding_metadata(subcategory: str) -> dict[str, Any]:
|
|
91
|
+
"""Look up CVSS vector, severity, and ASI metadata for a subcategory."""
|
|
92
|
+
return FLAT_TAXONOMY.get(
|
|
93
|
+
subcategory,
|
|
94
|
+
{
|
|
95
|
+
"category": "beyond_asi",
|
|
96
|
+
"asi_code": "BEYOND-ASI",
|
|
97
|
+
"asi_title": "Beyond Current OWASP ASI Framework",
|
|
98
|
+
"title": subcategory,
|
|
99
|
+
"cvss_vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N",
|
|
100
|
+
"severity": "medium",
|
|
101
|
+
"remediation": (
|
|
102
|
+
"Review the finding and apply appropriate security controls "
|
|
103
|
+
"based on the OWASP Agentic AI guidelines."
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_compliance_mappings(subcategory: str) -> dict[str, list[str]]:
|
|
110
|
+
"""Return OWASP Agentic compliance mappings for a subcategory."""
|
|
111
|
+
return COMPLIANCE_MAPPINGS.get(
|
|
112
|
+
subcategory, {"owasp_agentic": [BEYOND_ASI_SENTINEL]}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def is_owasp_classified(compliance_mappings: dict[str, list[str]]) -> bool:
|
|
117
|
+
"""Return True if at least one mapping starts with 'ASI'."""
|
|
118
|
+
for entry in compliance_mappings.get("owasp_agentic", []):
|
|
119
|
+
if entry.startswith("ASI"):
|
|
120
|
+
return True
|
|
121
|
+
return False
|