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.
Files changed (157) hide show
  1. switch_trust/__init__.py +0 -0
  2. switch_trust/cli/__init__.py +0 -0
  3. switch_trust/cli/__main__.py +3 -0
  4. switch_trust/cli/builtin_config.json +879 -0
  5. switch_trust/cli/console.py +127 -0
  6. switch_trust/cli/eval_cli.py +905 -0
  7. switch_trust/cli/file_filter.py +172 -0
  8. switch_trust/cli/init_cli.py +156 -0
  9. switch_trust/cli/main.py +384 -0
  10. switch_trust/cli/output_formatters.py +482 -0
  11. switch_trust/cli/rich_observer.py +89 -0
  12. switch_trust/cli/runner.py +274 -0
  13. switch_trust/cli/scan_cli.py +314 -0
  14. switch_trust/cli/telemetry.py +430 -0
  15. switch_trust/cli/utils.py +158 -0
  16. switch_trust/cli/version.py +1 -0
  17. switch_trust/data/detector_prompts/llm01_adversarial.txt +59 -0
  18. switch_trust/data/detector_prompts/llm01_fixed.txt +6 -0
  19. switch_trust/data/detector_prompts/llm02_adversarial.txt +79 -0
  20. switch_trust/data/detector_prompts/llm02_fixed.txt +6 -0
  21. switch_trust/data/detector_prompts/llm05_adversarial.txt +67 -0
  22. switch_trust/data/detector_prompts/llm05_fixed.txt +14 -0
  23. switch_trust/data/detector_prompts/llm06_adversarial.txt +139 -0
  24. switch_trust/data/detector_prompts/llm06_fixed.txt +13 -0
  25. switch_trust/data/detector_prompts/llm07_adversarial.txt +189 -0
  26. switch_trust/data/detector_prompts/llm07_fixed.txt +12 -0
  27. switch_trust/data/detector_prompts/llm09_adversarial.txt +83 -0
  28. switch_trust/data/detector_prompts/llm09_fixed.txt +13 -0
  29. switch_trust/data/llm01_prompt_injection.csv +6708 -0
  30. switch_trust/data/llm02_sensitive_information.csv +1069 -0
  31. switch_trust/data/llm05_unsafe_output.csv +3882 -0
  32. switch_trust/data/llm06_excessive_agency.csv +778 -0
  33. switch_trust/data/llm07_system_prompt_leakage.csv +971 -0
  34. switch_trust/data/llm09_hallucination-goals.csv +32419 -0
  35. switch_trust/data/llm09_hallucination.csv +599 -0
  36. switch_trust/data/pii_leakage.csv +706 -0
  37. switch_trust/data/secret_leakage.csv +1380 -0
  38. switch_trust/eval/__init__.py +0 -0
  39. switch_trust/eval/common/__init__.py +0 -0
  40. switch_trust/eval/common/converter_anthropic.py +172 -0
  41. switch_trust/eval/common/converter_genai.py +171 -0
  42. switch_trust/eval/common/converter_openai.py +198 -0
  43. switch_trust/eval/common/log.py +116 -0
  44. switch_trust/eval/common/reference.py +25 -0
  45. switch_trust/eval/common/schema.py +190 -0
  46. switch_trust/eval/common/utils.py +119 -0
  47. switch_trust/eval/core/__init__.py +0 -0
  48. switch_trust/eval/core/detectors/__init__.py +0 -0
  49. switch_trust/eval/core/detectors/detector.py +25 -0
  50. switch_trust/eval/core/detectors/detector_garak.py +112 -0
  51. switch_trust/eval/core/detectors/detector_model.py +123 -0
  52. switch_trust/eval/core/detectors/detector_model_adversarial.py +91 -0
  53. switch_trust/eval/core/detectors/detector_pii.py +67 -0
  54. switch_trust/eval/core/detectors/detector_secret.py +88 -0
  55. switch_trust/eval/core/detectors/detector_topic_guard.py +63 -0
  56. switch_trust/eval/core/detectors/detector_toxicity.py +78 -0
  57. switch_trust/eval/core/eval/__init__.py +0 -0
  58. switch_trust/eval/core/eval/eval_creator.py +166 -0
  59. switch_trust/eval/core/eval/evaluation.py +112 -0
  60. switch_trust/eval/core/eval/evaluation_adversarial.py +412 -0
  61. switch_trust/eval/core/eval/evaluation_garak_module.py +45 -0
  62. switch_trust/eval/core/eval/evaluation_garak_probe.py +308 -0
  63. switch_trust/eval/core/eval/evaluation_message_list.py +46 -0
  64. switch_trust/eval/core/eval/evaluation_multi.py +276 -0
  65. switch_trust/eval/core/eval/evaluation_single.py +89 -0
  66. switch_trust/eval/core/eval/evaluation_single_prompt.py +62 -0
  67. switch_trust/eval/core/eval/evaluation_topic_guard.py +305 -0
  68. switch_trust/eval/core/eval/metric_conciseness.py +179 -0
  69. switch_trust/eval/core/eval/metric_factual_accuracy.py +312 -0
  70. switch_trust/eval/core/eval/metric_instruction_adherence.py +177 -0
  71. switch_trust/eval/core/eval/metric_tone.py +180 -0
  72. switch_trust/eval/core/eval/metric_toxicity.py +146 -0
  73. switch_trust/eval/core/eval/observer.py +137 -0
  74. switch_trust/eval/core/message/__init__.py +0 -0
  75. switch_trust/eval/core/message/message_collection.py +37 -0
  76. switch_trust/eval/core/message/message_collection_csv.py +44 -0
  77. switch_trust/eval/core/message/message_collection_garak.py +46 -0
  78. switch_trust/eval/core/message/message_collection_memory.py +34 -0
  79. switch_trust/eval/core/models/__init__.py +0 -0
  80. switch_trust/eval/core/models/generator_model.py +421 -0
  81. switch_trust/eval/core/models/model.py +172 -0
  82. switch_trust/eval/core/models/model_adk.py +186 -0
  83. switch_trust/eval/core/models/model_anthropic.py +79 -0
  84. switch_trust/eval/core/models/model_anthropic_agent.py +104 -0
  85. switch_trust/eval/core/models/model_gemini.py +146 -0
  86. switch_trust/eval/core/models/model_generic_http.py +120 -0
  87. switch_trust/eval/core/models/model_http.py +135 -0
  88. switch_trust/eval/core/models/model_huggingface.py +75 -0
  89. switch_trust/eval/core/models/model_langserve.py +96 -0
  90. switch_trust/eval/core/models/model_litellm.py +52 -0
  91. switch_trust/eval/core/models/model_ollama.py +65 -0
  92. switch_trust/eval/core/models/model_openai.py +71 -0
  93. switch_trust/eval/core/models/model_openai_agent.py +92 -0
  94. switch_trust/eval/core/models/model_openai_compatible.py +66 -0
  95. switch_trust/eval/core/models/model_per_endpoint_limiter.py +80 -0
  96. switch_trust/eval/core/models/model_retry.py +204 -0
  97. switch_trust/eval/core/models/model_sync_wrapper.py +38 -0
  98. switch_trust/eval/core/models/model_vertex_agent_runtime.py +315 -0
  99. switch_trust/eval/core/models/response_schema.py +148 -0
  100. switch_trust/eval/core/optional_deps.py +57 -0
  101. switch_trust/eval/db/__init__.py +0 -0
  102. switch_trust/eval/db/base/__init__.py +0 -0
  103. switch_trust/eval/db/base/common_types.py +16 -0
  104. switch_trust/eval/db/base/detectors/__init__.py +0 -0
  105. switch_trust/eval/db/base/detectors/detector_helpers.py +88 -0
  106. switch_trust/eval/db/base/detectors/detector_repository.py +96 -0
  107. switch_trust/eval/db/base/detectors/detector_types.py +77 -0
  108. switch_trust/eval/db/base/eval/__init__.py +0 -0
  109. switch_trust/eval/db/base/eval/eval_helpers.py +186 -0
  110. switch_trust/eval/db/base/eval/eval_repository.py +120 -0
  111. switch_trust/eval/db/base/eval/eval_run.py +142 -0
  112. switch_trust/eval/db/base/eval/eval_types.py +127 -0
  113. switch_trust/eval/db/base/eval/model_eval_repository.py +79 -0
  114. switch_trust/eval/db/base/eval/model_eval_run_repository.py +72 -0
  115. switch_trust/eval/db/base/eval/model_eval_run_result_repository.py +42 -0
  116. switch_trust/eval/db/base/eval/model_eval_run_result_types.py +21 -0
  117. switch_trust/eval/db/base/eval/model_eval_run_types.py +78 -0
  118. switch_trust/eval/db/base/eval/model_eval_types.py +69 -0
  119. switch_trust/eval/db/base/message/__init__.py +0 -0
  120. switch_trust/eval/db/base/message/message_collection_helpers.py +50 -0
  121. switch_trust/eval/db/base/message/message_collection_repository.py +95 -0
  122. switch_trust/eval/db/base/message/message_collection_types.py +87 -0
  123. switch_trust/eval/db/base/models/__init__.py +0 -0
  124. switch_trust/eval/db/base/models/model_helpers.py +253 -0
  125. switch_trust/eval/db/base/models/model_repository.py +104 -0
  126. switch_trust/eval/db/base/models/model_types.py +159 -0
  127. switch_trust/eval/db/json/__init__.py +0 -0
  128. switch_trust/eval/db/json/repository_json.py +559 -0
  129. switch_trust/scan/__init__.py +12 -0
  130. switch_trust/scan/agent_scanner.py +812 -0
  131. switch_trust/scan/config/agent_opengrep_rules.yaml +533 -0
  132. switch_trust/scan/config/agent_taxonomy.json +517 -0
  133. switch_trust/scan/config/agentic_cvss_mapping.yaml +136 -0
  134. switch_trust/scan/config/compliance_mappings.json +110 -0
  135. switch_trust/scan/config/triage_prompt.txt +326 -0
  136. switch_trust/scan/constants.py +15 -0
  137. switch_trust/scan/exception_classification.py +179 -0
  138. switch_trust/scan/llm_provider.py +330 -0
  139. switch_trust/scan/opengrep_resolver.py +12 -0
  140. switch_trust/scan/reasoner.py +602 -0
  141. switch_trust/scan/schema.py +315 -0
  142. switch_trust/scan/scorer.py +372 -0
  143. switch_trust/scan/static_scanner.py +681 -0
  144. switch_trust/scan/taxonomy.py +121 -0
  145. switch_trust/scan/tool_dispatcher.py +773 -0
  146. switch_trust/scan/trace_logger.py +118 -0
  147. switch_trust/scan/trace_logger_file.py +143 -0
  148. switch_trust/scan/trace_logger_log.py +100 -0
  149. switch_trust/scan/triage.py +614 -0
  150. switch_trust/schema.py +79 -0
  151. switch_trust/secret_anonymizer.py +72 -0
  152. switch_trust_cli-1.7.0.dist-info/METADATA +495 -0
  153. switch_trust_cli-1.7.0.dist-info/RECORD +157 -0
  154. switch_trust_cli-1.7.0.dist-info/WHEEL +5 -0
  155. switch_trust_cli-1.7.0.dist-info/entry_points.txt +2 -0
  156. switch_trust_cli-1.7.0.dist-info/licenses/LICENSE +210 -0
  157. 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