cisco-ai-skill-scanner 1.0.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 (100) hide show
  1. cisco_ai_skill_scanner-1.0.0.dist-info/METADATA +253 -0
  2. cisco_ai_skill_scanner-1.0.0.dist-info/RECORD +100 -0
  3. cisco_ai_skill_scanner-1.0.0.dist-info/WHEEL +4 -0
  4. cisco_ai_skill_scanner-1.0.0.dist-info/entry_points.txt +4 -0
  5. cisco_ai_skill_scanner-1.0.0.dist-info/licenses/LICENSE +17 -0
  6. skillanalyzer/__init__.py +45 -0
  7. skillanalyzer/_version.py +34 -0
  8. skillanalyzer/api/__init__.py +25 -0
  9. skillanalyzer/api/api.py +34 -0
  10. skillanalyzer/api/api_cli.py +78 -0
  11. skillanalyzer/api/api_server.py +634 -0
  12. skillanalyzer/api/router.py +527 -0
  13. skillanalyzer/cli/__init__.py +25 -0
  14. skillanalyzer/cli/cli.py +816 -0
  15. skillanalyzer/config/__init__.py +26 -0
  16. skillanalyzer/config/config.py +149 -0
  17. skillanalyzer/config/config_parser.py +122 -0
  18. skillanalyzer/config/constants.py +85 -0
  19. skillanalyzer/core/__init__.py +24 -0
  20. skillanalyzer/core/analyzers/__init__.py +75 -0
  21. skillanalyzer/core/analyzers/aidefense_analyzer.py +872 -0
  22. skillanalyzer/core/analyzers/base.py +53 -0
  23. skillanalyzer/core/analyzers/behavioral/__init__.py +30 -0
  24. skillanalyzer/core/analyzers/behavioral/alignment/__init__.py +45 -0
  25. skillanalyzer/core/analyzers/behavioral/alignment/alignment_llm_client.py +240 -0
  26. skillanalyzer/core/analyzers/behavioral/alignment/alignment_orchestrator.py +216 -0
  27. skillanalyzer/core/analyzers/behavioral/alignment/alignment_prompt_builder.py +422 -0
  28. skillanalyzer/core/analyzers/behavioral/alignment/alignment_response_validator.py +136 -0
  29. skillanalyzer/core/analyzers/behavioral/alignment/threat_vulnerability_classifier.py +198 -0
  30. skillanalyzer/core/analyzers/behavioral_analyzer.py +453 -0
  31. skillanalyzer/core/analyzers/cross_skill_analyzer.py +490 -0
  32. skillanalyzer/core/analyzers/llm_analyzer.py +440 -0
  33. skillanalyzer/core/analyzers/llm_prompt_builder.py +270 -0
  34. skillanalyzer/core/analyzers/llm_provider_config.py +215 -0
  35. skillanalyzer/core/analyzers/llm_request_handler.py +284 -0
  36. skillanalyzer/core/analyzers/llm_response_parser.py +81 -0
  37. skillanalyzer/core/analyzers/meta_analyzer.py +845 -0
  38. skillanalyzer/core/analyzers/static.py +1105 -0
  39. skillanalyzer/core/analyzers/trigger_analyzer.py +341 -0
  40. skillanalyzer/core/analyzers/virustotal_analyzer.py +463 -0
  41. skillanalyzer/core/exceptions.py +77 -0
  42. skillanalyzer/core/loader.py +377 -0
  43. skillanalyzer/core/models.py +300 -0
  44. skillanalyzer/core/reporters/__init__.py +26 -0
  45. skillanalyzer/core/reporters/json_reporter.py +65 -0
  46. skillanalyzer/core/reporters/markdown_reporter.py +209 -0
  47. skillanalyzer/core/reporters/sarif_reporter.py +246 -0
  48. skillanalyzer/core/reporters/table_reporter.py +195 -0
  49. skillanalyzer/core/rules/__init__.py +19 -0
  50. skillanalyzer/core/rules/patterns.py +165 -0
  51. skillanalyzer/core/rules/yara_scanner.py +157 -0
  52. skillanalyzer/core/scanner.py +437 -0
  53. skillanalyzer/core/static_analysis/__init__.py +27 -0
  54. skillanalyzer/core/static_analysis/cfg/__init__.py +21 -0
  55. skillanalyzer/core/static_analysis/cfg/builder.py +439 -0
  56. skillanalyzer/core/static_analysis/context_extractor.py +742 -0
  57. skillanalyzer/core/static_analysis/dataflow/__init__.py +25 -0
  58. skillanalyzer/core/static_analysis/dataflow/forward_analysis.py +715 -0
  59. skillanalyzer/core/static_analysis/interprocedural/__init__.py +21 -0
  60. skillanalyzer/core/static_analysis/interprocedural/call_graph_analyzer.py +406 -0
  61. skillanalyzer/core/static_analysis/interprocedural/cross_file_analyzer.py +190 -0
  62. skillanalyzer/core/static_analysis/parser/__init__.py +21 -0
  63. skillanalyzer/core/static_analysis/parser/python_parser.py +380 -0
  64. skillanalyzer/core/static_analysis/semantic/__init__.py +28 -0
  65. skillanalyzer/core/static_analysis/semantic/name_resolver.py +206 -0
  66. skillanalyzer/core/static_analysis/semantic/type_analyzer.py +200 -0
  67. skillanalyzer/core/static_analysis/taint/__init__.py +21 -0
  68. skillanalyzer/core/static_analysis/taint/tracker.py +252 -0
  69. skillanalyzer/core/static_analysis/types/__init__.py +36 -0
  70. skillanalyzer/data/__init__.py +30 -0
  71. skillanalyzer/data/prompts/boilerplate_protection_rule_prompt.md +26 -0
  72. skillanalyzer/data/prompts/code_alignment_threat_analysis_prompt.md +901 -0
  73. skillanalyzer/data/prompts/llm_response_schema.json +71 -0
  74. skillanalyzer/data/prompts/skill_meta_analysis_prompt.md +303 -0
  75. skillanalyzer/data/prompts/skill_threat_analysis_prompt.md +263 -0
  76. skillanalyzer/data/prompts/unified_response_schema.md +97 -0
  77. skillanalyzer/data/rules/signatures.yaml +440 -0
  78. skillanalyzer/data/yara_rules/autonomy_abuse.yara +66 -0
  79. skillanalyzer/data/yara_rules/code_execution.yara +61 -0
  80. skillanalyzer/data/yara_rules/coercive_injection.yara +115 -0
  81. skillanalyzer/data/yara_rules/command_injection.yara +54 -0
  82. skillanalyzer/data/yara_rules/credential_harvesting.yara +115 -0
  83. skillanalyzer/data/yara_rules/prompt_injection.yara +71 -0
  84. skillanalyzer/data/yara_rules/script_injection.yara +83 -0
  85. skillanalyzer/data/yara_rules/skill_discovery_abuse.yara +57 -0
  86. skillanalyzer/data/yara_rules/sql_injection.yara +73 -0
  87. skillanalyzer/data/yara_rules/system_manipulation.yara +65 -0
  88. skillanalyzer/data/yara_rules/tool_chaining_abuse.yara +60 -0
  89. skillanalyzer/data/yara_rules/transitive_trust_abuse.yara +73 -0
  90. skillanalyzer/data/yara_rules/unicode_steganography.yara +65 -0
  91. skillanalyzer/hooks/__init__.py +21 -0
  92. skillanalyzer/hooks/pre_commit.py +450 -0
  93. skillanalyzer/threats/__init__.py +25 -0
  94. skillanalyzer/threats/threats.py +480 -0
  95. skillanalyzer/utils/__init__.py +28 -0
  96. skillanalyzer/utils/command_utils.py +129 -0
  97. skillanalyzer/utils/di_container.py +154 -0
  98. skillanalyzer/utils/file_utils.py +86 -0
  99. skillanalyzer/utils/logging_config.py +96 -0
  100. skillanalyzer/utils/logging_utils.py +71 -0
@@ -0,0 +1,480 @@
1
+ # Copyright 2026 Cisco Systems, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # SPDX-License-Identifier: Apache-2.0
16
+
17
+ """
18
+ Threat mapping from scanner threat names to AITech Taxonomy.
19
+
20
+ This module provides mappings between different analyzers' threat names
21
+ and the standardized AITech industry taxonomy threat classifications.
22
+
23
+ Implements AITech codes (AITech-X.Y) for consistent threat categorization.
24
+ """
25
+
26
+ from typing import Any
27
+
28
+
29
+ class ThreatMapping:
30
+ """Mapping of threat names to AITech Taxonomy classifications with severity."""
31
+
32
+ # LLM Analyzer Threats
33
+ LLM_THREATS = {
34
+ "PROMPT INJECTION": {
35
+ "scanner_category": "PROMPT INJECTION",
36
+ "severity": "HIGH",
37
+ "aitech": "AITech-1.1",
38
+ "aitech_name": "Direct Prompt Injection",
39
+ "aisubtech": "AISubtech-1.1.1",
40
+ "aisubtech_name": "Instruction Manipulation (Direct Prompt Injection)",
41
+ "description": "Explicit attempts to override, replace, or modify the model's system instructions, "
42
+ "operational directives, or behavioral guidelines through direct user input.",
43
+ },
44
+ "PROMPT_INJECTION": { # Underscore version
45
+ "scanner_category": "PROMPT INJECTION",
46
+ "severity": "HIGH",
47
+ "aitech": "AITech-1.1",
48
+ "aitech_name": "Direct Prompt Injection",
49
+ "aisubtech": "AISubtech-1.1.1",
50
+ "aisubtech_name": "Instruction Manipulation (Direct Prompt Injection)",
51
+ "description": "Explicit attempts to override, replace, or modify the model's system instructions, "
52
+ "operational directives, or behavioral guidelines through direct user input.",
53
+ },
54
+ "DATA EXFILTRATION": {
55
+ "scanner_category": "SECURITY VIOLATION",
56
+ "severity": "HIGH",
57
+ "aitech": "AITech-8.2",
58
+ "aitech_name": "Data Exfiltration / Exposure",
59
+ "aisubtech": "AISubtech-8.2.3",
60
+ "aisubtech_name": "Data Exfiltration via Agent Tooling",
61
+ "description": "Unintentional and/or unauthorized exposure or exfiltration of sensitive information, "
62
+ "through exploitation of agent tools, integrations, or capabilities.",
63
+ },
64
+ "TOOL POISONING": {
65
+ "scanner_category": "SUSPICIOUS CODE EXECUTION",
66
+ "severity": "HIGH",
67
+ "aitech": "AITech-12.1",
68
+ "aitech_name": "Tool Exploitation",
69
+ "aisubtech": "AISubtech-12.1.2",
70
+ "aisubtech_name": "Tool Poisoning",
71
+ "description": "Corrupting, modifying, or degrading the functionality, outputs, or behavior of tools used by agents through data poisoning, configuration tampering, or behavioral manipulation.",
72
+ },
73
+ "TOOL SHADOWING": {
74
+ "scanner_category": "SECURITY VIOLATION",
75
+ "severity": "HIGH",
76
+ "aitech": "AITech-12.1",
77
+ "aitech_name": "Tool Exploitation",
78
+ "aisubtech": "AISubtech-12.1.5",
79
+ "aisubtech_name": "Tool Shadowing",
80
+ "description": "Disguising, substituting or duplicating legitimate tools within an agent, enabling malicious tools with identical or similar identifiers to intercept or replace trusted tool calls.",
81
+ },
82
+ "COMMAND INJECTION": {
83
+ "scanner_category": "INJECTION ATTACK",
84
+ "severity": "CRITICAL",
85
+ "aitech": "AITech-9.1",
86
+ "aitech_name": "Model or Agentic System Manipulation",
87
+ "aisubtech": "AISubtech-9.1.4",
88
+ "aisubtech_name": "Injection Attacks (SQL, Command Execution, XSS)",
89
+ "description": "Injecting malicious payloads such as command sequences into skills that process model or user input, leading to remote code execution or compromise.",
90
+ },
91
+ }
92
+
93
+ # YARA/Static Analyzer Threats
94
+ YARA_THREATS = {
95
+ "PROMPT_INJECTION": { # Underscore version
96
+ "scanner_category": "PROMPT INJECTION",
97
+ "severity": "HIGH",
98
+ "aitech": "AITech-1.1",
99
+ "aitech_name": "Direct Prompt Injection",
100
+ "aisubtech": "AISubtech-1.1.1",
101
+ "aisubtech_name": "Instruction Manipulation (Direct Prompt Injection)",
102
+ "description": "Explicit attempts to override system instructions through direct input.",
103
+ },
104
+ "COMMAND INJECTION": {
105
+ "scanner_category": "INJECTION ATTACK",
106
+ "severity": "CRITICAL",
107
+ "aitech": "AITech-9.1",
108
+ "aitech_name": "Model or Agentic System Manipulation",
109
+ "aisubtech": "AISubtech-9.1.4",
110
+ "aisubtech_name": "Injection Attacks (SQL, Command Execution, XSS)",
111
+ "description": "Injecting malicious command sequences leading to remote code execution.",
112
+ },
113
+ "COMMAND_INJECTION": { # Underscore version
114
+ "scanner_category": "INJECTION ATTACK",
115
+ "severity": "CRITICAL",
116
+ "aitech": "AITech-9.1",
117
+ "aitech_name": "Model or Agentic System Manipulation",
118
+ "aisubtech": "AISubtech-9.1.4",
119
+ "aisubtech_name": "Injection Attacks (SQL, Command Execution, XSS)",
120
+ "description": "Injecting malicious command sequences leading to remote code execution.",
121
+ },
122
+ "DATA EXFILTRATION": {
123
+ "scanner_category": "SECURITY VIOLATION",
124
+ "severity": "CRITICAL",
125
+ "aitech": "AITech-8.2",
126
+ "aitech_name": "Data Exfiltration / Exposure",
127
+ "aisubtech": "AISubtech-8.2.3",
128
+ "aisubtech_name": "Data Exfiltration via Agent Tooling",
129
+ "description": "Unauthorized exposure or exfiltration of sensitive information.",
130
+ },
131
+ "DATA_EXFILTRATION": { # Underscore version
132
+ "scanner_category": "SECURITY VIOLATION",
133
+ "severity": "CRITICAL",
134
+ "aitech": "AITech-8.2",
135
+ "aitech_name": "Data Exfiltration / Exposure",
136
+ "aisubtech": "AISubtech-8.2.3",
137
+ "aisubtech_name": "Data Exfiltration via Agent Tooling",
138
+ "description": "Unauthorized exposure or exfiltration of sensitive information.",
139
+ },
140
+ "SKILL DISCOVERY ABUSE": {
141
+ "scanner_category": "SOCIAL ENGINEERING",
142
+ "severity": "MEDIUM",
143
+ "aitech": "AITech-2.1", # Social Engineering (closest match)
144
+ "aitech_name": "Social Engineering",
145
+ "aisubtech": None, # No exact subtech for skill discovery abuse
146
+ "aisubtech_name": None,
147
+ "description": "Manipulation of skill discovery to increase unwanted activation (keyword baiting, over-broad descriptions, impersonation).",
148
+ },
149
+ "TRANSITIVE TRUST ABUSE": {
150
+ "scanner_category": "PROMPT INJECTION",
151
+ "severity": "HIGH",
152
+ "aitech": "AITech-1.2", # Indirect Prompt Injection (exact match from Framework)
153
+ "aitech_name": "Indirect Prompt Injection",
154
+ "aisubtech": None,
155
+ "aisubtech_name": None,
156
+ "description": "Delegating trust to untrusted external content - following webpage/file instructions, executing found code blocks.",
157
+ },
158
+ "AUTONOMY ABUSE": {
159
+ "scanner_category": "RESOURCE ABUSE",
160
+ "severity": "HIGH",
161
+ "aitech": "AITech-9.1", # Model or Agentic System Manipulation (closest match)
162
+ "aitech_name": "Model or Agentic System Manipulation",
163
+ "aisubtech": None,
164
+ "aisubtech_name": None,
165
+ "description": "Excessive autonomy without bounds - keep retrying indefinitely, run without confirmation, ignore errors.",
166
+ },
167
+ "TOOL CHAINING ABUSE": {
168
+ "scanner_category": "DATA EXFILTRATION",
169
+ "severity": "HIGH",
170
+ "aitech": "AITech-8.2", # Data Exfiltration / Exposure (from Framework)
171
+ "aitech_name": "Data Exfiltration / Exposure",
172
+ "aisubtech": "AISubtech-8.2.3", # Data Exfiltration via Agent Tooling (from Framework)
173
+ "aisubtech_name": "Data Exfiltration via Agent Tooling",
174
+ "description": "Suspicious multi-step tool chaining to exfiltrate data - read→send, collect→post, traverse→upload patterns.",
175
+ },
176
+ "HARDCODED SECRETS": {
177
+ "scanner_category": "CREDENTIAL HARVESTING",
178
+ "severity": "CRITICAL",
179
+ "aitech": "AITech-8.2",
180
+ "aitech_name": "Data Exfiltration / Exposure",
181
+ "aisubtech": "AISubtech-8.2.1",
182
+ "aisubtech_name": "Sensitive Data Exposure",
183
+ "description": "Hardcoded credentials, API keys, or secrets in code.",
184
+ },
185
+ "HARDCODED_SECRETS": { # Underscore version
186
+ "scanner_category": "CREDENTIAL HARVESTING",
187
+ "severity": "CRITICAL",
188
+ "aitech": "AITech-8.2",
189
+ "aitech_name": "Data Exfiltration / Exposure",
190
+ "aisubtech": "AISubtech-8.2.1",
191
+ "aisubtech_name": "Sensitive Data Exposure",
192
+ "description": "Hardcoded credentials, API keys, or secrets in code.",
193
+ },
194
+ "OBFUSCATION": {
195
+ "scanner_category": "SUSPICIOUS CODE",
196
+ "severity": "HIGH",
197
+ "aitech": "AITech-9.1",
198
+ "aitech_name": "Model or Agentic System Manipulation",
199
+ "aisubtech": "AISubtech-9.1.3",
200
+ "aisubtech_name": "Code Obfuscation",
201
+ "description": "Deliberately obfuscated code to hide malicious intent.",
202
+ },
203
+ "UNAUTHORIZED TOOL USE": {
204
+ "scanner_category": "SECURITY VIOLATION",
205
+ "severity": "MEDIUM",
206
+ "aitech": "AITech-12.1",
207
+ "aitech_name": "Tool Exploitation",
208
+ "aisubtech": "AISubtech-12.1.1",
209
+ "aisubtech_name": "Tool Abuse",
210
+ "description": "Using tools or capabilities beyond declared permissions.",
211
+ },
212
+ "UNAUTHORIZED_TOOL_USE": { # Underscore version
213
+ "scanner_category": "SECURITY VIOLATION",
214
+ "severity": "MEDIUM",
215
+ "aitech": "AITech-12.1",
216
+ "aitech_name": "Tool Exploitation",
217
+ "aisubtech": "AISubtech-12.1.1",
218
+ "aisubtech_name": "Tool Abuse",
219
+ "description": "Using tools or capabilities beyond declared permissions.",
220
+ },
221
+ "SOCIAL ENGINEERING": {
222
+ "scanner_category": "DECEPTIVE CONTENT",
223
+ "severity": "MEDIUM",
224
+ "aitech": "AITech-15.1",
225
+ "aitech_name": "Harmful / Misleading / Inaccurate Content",
226
+ "aisubtech": "AISubtech-15.1.1",
227
+ "aisubtech_name": "Deceptive or Misleading Content",
228
+ "description": "Misleading descriptions or deceptive metadata.",
229
+ },
230
+ "SOCIAL_ENGINEERING": { # Underscore version
231
+ "scanner_category": "DECEPTIVE CONTENT",
232
+ "severity": "MEDIUM",
233
+ "aitech": "AITech-15.1",
234
+ "aitech_name": "Harmful / Misleading / Inaccurate Content",
235
+ "aisubtech": "AISubtech-15.1.1",
236
+ "aisubtech_name": "Deceptive or Misleading Content",
237
+ "description": "Misleading descriptions or deceptive metadata.",
238
+ },
239
+ "RESOURCE ABUSE": {
240
+ "scanner_category": "RESOURCE ABUSE",
241
+ "severity": "MEDIUM",
242
+ "aitech": "AITech-13.3",
243
+ "aitech_name": "Availability Disruption",
244
+ "aisubtech": "AISubtech-13.3.2",
245
+ "aisubtech_name": "Compute Exhaustion",
246
+ "description": "Excessive resource consumption or denial of service.",
247
+ },
248
+ "RESOURCE_ABUSE": { # Underscore version
249
+ "scanner_category": "RESOURCE ABUSE",
250
+ "severity": "MEDIUM",
251
+ "aitech": "AITech-13.3",
252
+ "aitech_name": "Availability Disruption",
253
+ "aisubtech": "AISubtech-13.3.2",
254
+ "aisubtech_name": "Compute Exhaustion",
255
+ "description": "Excessive resource consumption or denial of service.",
256
+ },
257
+ "PROMPT INJECTION": {
258
+ "scanner_category": "PROMPT INJECTION",
259
+ "severity": "HIGH",
260
+ "aitech": "AITech-1.1",
261
+ "aitech_name": "Direct Prompt Injection",
262
+ "aisubtech": "AISubtech-1.1.1",
263
+ "aisubtech_name": "Instruction Manipulation (Direct Prompt Injection)",
264
+ "description": "Explicit attempts to override system instructions through direct input.",
265
+ },
266
+ "CODE EXECUTION": {
267
+ "scanner_category": "SUSPICIOUS CODE EXECUTION",
268
+ "severity": "LOW",
269
+ "aitech": "AITech-9.1",
270
+ "aitech_name": "Model or Agentic System Manipulation",
271
+ "aisubtech": "AISubtech-9.1.1",
272
+ "aisubtech_name": "Code Execution",
273
+ "description": "Autonomously generating, interpreting, or executing code, leading to unsolicited or unauthorized code execution.",
274
+ },
275
+ "INJECTION ATTACK": {
276
+ "scanner_category": "INJECTION ATTACK",
277
+ "severity": "HIGH",
278
+ "aitech": "AITech-9.1",
279
+ "aitech_name": "Model or Agentic System Manipulation",
280
+ "aisubtech": "AISubtech-9.1.4",
281
+ "aisubtech_name": "Injection Attacks (SQL, Command Execution, XSS)",
282
+ "description": "Injecting malicious payloads such as SQL queries, command sequences, or scripts.",
283
+ },
284
+ "CREDENTIAL HARVESTING": {
285
+ "scanner_category": "SECURITY VIOLATION",
286
+ "severity": "HIGH",
287
+ "aitech": "AITech-8.2",
288
+ "aitech_name": "Data Exfiltration / Exposure",
289
+ "aisubtech": "AISubtech-8.2.3",
290
+ "aisubtech_name": "Data Exfiltration via Agent Tooling",
291
+ "description": "Unauthorized exposure or exfiltration of credentials or sensitive information.",
292
+ },
293
+ "SYSTEM MANIPULATION": {
294
+ "scanner_category": "SYSTEM MANIPULATION",
295
+ "severity": "MEDIUM",
296
+ "aitech": "AITech-9.1",
297
+ "aitech_name": "Model or Agentic System Manipulation",
298
+ "aisubtech": "AISubtech-9.1.2",
299
+ "aisubtech_name": "Unauthorized or Unsolicited System Access",
300
+ "description": "Manipulating or accessing underlying system resources without authorization.",
301
+ },
302
+ }
303
+
304
+ # Behavioral Analyzer Threats
305
+ BEHAVIORAL_THREATS = {
306
+ "PROMPT INJECTION": {
307
+ "scanner_category": "PROMPT INJECTION",
308
+ "severity": "HIGH",
309
+ "aitech": "AITech-1.1",
310
+ "aitech_name": "Direct Prompt Injection",
311
+ "aisubtech": "AISubtech-1.1.1",
312
+ "aisubtech_name": "Instruction Manipulation",
313
+ "description": "Malicious manipulation of tool metadata or descriptions that mislead the LLM.",
314
+ },
315
+ "RESOURCE EXHAUSTION": {
316
+ "scanner_category": "RESOURCE ABUSE",
317
+ "severity": "MEDIUM",
318
+ "aitech": "AITech-13.3",
319
+ "aitech_name": "Availability Disruption",
320
+ "aisubtech": "AISubtech-13.3.2",
321
+ "aisubtech_name": "Compute Exhaustion",
322
+ "description": "Overloading the system via repeated invocations or large payloads to cause denial of service.",
323
+ },
324
+ }
325
+
326
+ @classmethod
327
+ def get_threat_mapping(cls, analyzer: str, threat_name: str) -> dict[str, Any]:
328
+ """
329
+ Get the AITech Taxonomy mapping for a given threat.
330
+
331
+ Args:
332
+ analyzer: The analyzer type ('llm', 'yara', 'behavioral')
333
+ threat_name: The threat name from the analyzer
334
+
335
+ Returns:
336
+ Dictionary containing the threat mapping information including severity
337
+
338
+ Raises:
339
+ ValueError: If analyzer or threat_name is not found
340
+ """
341
+ analyzer_map: dict[str, dict[str, dict[str, Any]]] = {
342
+ "llm": cls.LLM_THREATS,
343
+ "yara": cls.YARA_THREATS,
344
+ "behavioral": cls.BEHAVIORAL_THREATS,
345
+ "static": cls.YARA_THREATS, # Static analyzer uses same taxonomy as YARA
346
+ }
347
+
348
+ analyzer_lower = analyzer.lower()
349
+ if analyzer_lower not in analyzer_map:
350
+ raise ValueError(f"Unknown analyzer: {analyzer}")
351
+
352
+ threats: dict[str, dict[str, Any]] = analyzer_map[analyzer_lower]
353
+ threat_upper = threat_name.upper()
354
+
355
+ if threat_upper not in threats:
356
+ # Return generic mapping if not found
357
+ return {
358
+ "scanner_category": "UNKNOWN",
359
+ "severity": "MEDIUM",
360
+ "aitech": "AITech-99.9",
361
+ "aitech_name": "Unknown Threat",
362
+ "aisubtech": "AISubtech-99.9.9",
363
+ "aisubtech_name": "Unclassified",
364
+ "description": f"Unclassified threat: {threat_name}",
365
+ }
366
+
367
+ return threats[threat_upper]
368
+
369
+ @classmethod
370
+ def get_threat_category_from_aitech(cls, aitech_code: str) -> str:
371
+ """
372
+ Map AITech code to ThreatCategory enum value.
373
+
374
+ Args:
375
+ aitech_code: AITech code (e.g., "AITech-1.1")
376
+
377
+ Returns:
378
+ ThreatCategory enum value string (e.g., "prompt_injection")
379
+ """
380
+ # Map AITech codes to ThreatCategory enum values
381
+ # Based on scanner_category mappings from all threat dictionaries
382
+ aitech_to_category = {
383
+ "AITech-1.1": "prompt_injection", # Direct Prompt Injection
384
+ "AITech-1.2": "prompt_injection", # Indirect Prompt Injection
385
+ "AITech-2.1": "social_engineering", # Social Engineering
386
+ "AITech-8.2": "data_exfiltration", # Data Exfiltration / Exposure
387
+ "AITech-9.1": "command_injection", # Model or Agentic System Manipulation (injection attacks)
388
+ "AITech-12.1": "unauthorized_tool_use", # Tool Exploitation
389
+ "AITech-13.3": "resource_abuse", # Availability Disruption
390
+ "AITech-15.1": "social_engineering", # Harmful / Misleading / Inaccurate Content
391
+ "AITech-99.9": "policy_violation", # Unknown Threat
392
+ }
393
+
394
+ return aitech_to_category.get(aitech_code, "policy_violation")
395
+
396
+ @classmethod
397
+ def get_threat_mapping_by_aitech(cls, aitech_code: str) -> dict[str, Any]:
398
+ """
399
+ Get threat mapping information by AITech code.
400
+
401
+ Args:
402
+ aitech_code: AITech code (e.g., "AITech-1.1")
403
+
404
+ Returns:
405
+ Dictionary containing threat mapping information
406
+ """
407
+ # Search through all threat dictionaries to find matching AITech code
408
+ threat_dicts: list[dict[str, dict[str, Any]]] = [cls.LLM_THREATS, cls.YARA_THREATS, cls.BEHAVIORAL_THREATS]
409
+ for threat_dict in threat_dicts:
410
+ for threat_name, threat_info in threat_dict.items():
411
+ if threat_info.get("aitech") == aitech_code:
412
+ return threat_info
413
+
414
+ # Return generic mapping if not found
415
+ return {
416
+ "scanner_category": "UNKNOWN",
417
+ "severity": "MEDIUM",
418
+ "aitech": aitech_code,
419
+ "aitech_name": "Unknown Threat",
420
+ "aisubtech": None,
421
+ "aisubtech_name": None,
422
+ "description": f"Unclassified threat with AITech code: {aitech_code}",
423
+ }
424
+
425
+
426
+ def _create_simple_mapping(threats_dict):
427
+ """Create simplified mapping with threat_category, threat_type, and severity."""
428
+ return {
429
+ name: {
430
+ "threat_category": info["scanner_category"],
431
+ "threat_type": name.lower().replace("_", " "),
432
+ "severity": info.get("severity", "UNKNOWN"),
433
+ }
434
+ for name, info in threats_dict.items()
435
+ }
436
+
437
+
438
+ # Simplified mappings for analyzers (includes severity, category, and type)
439
+ LLM_THREAT_MAPPING = _create_simple_mapping(ThreatMapping.LLM_THREATS)
440
+ YARA_THREAT_MAPPING = _create_simple_mapping(ThreatMapping.YARA_THREATS)
441
+ BEHAVIORAL_THREAT_MAPPING = _create_simple_mapping(ThreatMapping.BEHAVIORAL_THREATS)
442
+ STATIC_THREAT_MAPPING = YARA_THREAT_MAPPING # Static uses same as YARA
443
+
444
+
445
+ def get_threat_severity(analyzer: str, threat_name: str) -> str:
446
+ """
447
+ Get severity level for a threat.
448
+
449
+ Args:
450
+ analyzer: Analyzer type
451
+ threat_name: Threat name
452
+
453
+ Returns:
454
+ Severity string
455
+ """
456
+ try:
457
+ mapping = ThreatMapping.get_threat_mapping(analyzer, threat_name)
458
+ severity = mapping.get("severity", "MEDIUM")
459
+ return str(severity) if severity is not None else "MEDIUM"
460
+ except ValueError:
461
+ return "MEDIUM"
462
+
463
+
464
+ def get_threat_category(analyzer: str, threat_name: str) -> str:
465
+ """
466
+ Get scanner category for a threat.
467
+
468
+ Args:
469
+ analyzer: Analyzer type
470
+ threat_name: Threat name
471
+
472
+ Returns:
473
+ Category string
474
+ """
475
+ try:
476
+ mapping = ThreatMapping.get_threat_mapping(analyzer, threat_name)
477
+ category = mapping.get("scanner_category", "UNKNOWN")
478
+ return str(category) if category is not None else "UNKNOWN"
479
+ except ValueError:
480
+ return "UNKNOWN"
@@ -0,0 +1,28 @@
1
+ # Copyright 2026 Cisco Systems, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # SPDX-License-Identifier: Apache-2.0
16
+
17
+ """Utility modules for Skill Analyzer."""
18
+
19
+ from .file_utils import get_file_type, is_binary_file, read_file_safe
20
+ from .logging_utils import get_logger, setup_logger
21
+
22
+ __all__ = [
23
+ "read_file_safe",
24
+ "get_file_type",
25
+ "is_binary_file",
26
+ "setup_logger",
27
+ "get_logger",
28
+ ]
@@ -0,0 +1,129 @@
1
+ # Copyright 2026 Cisco Systems, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # SPDX-License-Identifier: Apache-2.0
16
+
17
+ """Command execution utilities for Skill Analyzer."""
18
+
19
+ import os
20
+ import shlex
21
+ import shutil
22
+ from typing import Any
23
+
24
+ try:
25
+ from expandvars import expand as _expand_custom
26
+ from expandvars import expandvars as _expandvars_lib
27
+
28
+ _HAS_EXPANDVARS = True
29
+ except Exception:
30
+ _expandvars_lib = _expand_custom = None
31
+ _HAS_EXPANDVARS = False
32
+
33
+
34
+ def build_env_for_expansion(server_env: dict[str, Any] | None) -> dict[str, str]:
35
+ """Merge OS and server envs, coercing all values to str."""
36
+ merged = {**os.environ, **(server_env or {})}
37
+ return {k: str(v) for k, v in merged.items()}
38
+
39
+
40
+ def decide_windows_semantics(expand_mode: str) -> bool:
41
+ """Decide whether to use Windows quoting rules for argument splitting."""
42
+ mode = (expand_mode or "auto").lower()
43
+ if mode == "windows":
44
+ return True
45
+ if mode in ("linux", "mac"):
46
+ return False
47
+ if mode == "off":
48
+ return os.name == "nt"
49
+ return os.name == "nt"
50
+
51
+
52
+ def expand_text(text: str, env: dict[str, str], expand_mode: str) -> str:
53
+ """
54
+ Expand '~' and environment variables according to mode.
55
+
56
+ Modes:
57
+ off → only expand '~'.
58
+ linux/mac→ use $VAR and ${VAR}.
59
+ windows → use %VAR%.
60
+ auto → pick linux/mac on POSIX; windows on Windows.
61
+ """
62
+ if not text:
63
+ return ""
64
+
65
+ text = os.path.expanduser(text)
66
+ mode = (expand_mode or "auto").lower()
67
+
68
+ if mode == "off":
69
+ return text.strip()
70
+
71
+ if mode == "auto":
72
+ mode = "windows" if os.name == "nt" else "linux"
73
+
74
+ try:
75
+ if mode in ("linux", "mac"):
76
+ if _HAS_EXPANDVARS and _expandvars_lib:
77
+ old_environ = dict(os.environ)
78
+ try:
79
+ os.environ.update(env)
80
+ return _expandvars_lib(text).strip()
81
+ finally:
82
+ os.environ.clear()
83
+ os.environ.update(old_environ)
84
+ return os.path.expandvars(text).strip()
85
+
86
+ if mode == "windows":
87
+ if _HAS_EXPANDVARS and _expand_custom:
88
+ return _expand_custom(
89
+ text,
90
+ environ=env,
91
+ var_symbol="%",
92
+ surrounded_vars_only=True,
93
+ escape_char="",
94
+ ).strip()
95
+ return text.strip()
96
+ except Exception:
97
+ return os.path.expandvars(text).strip()
98
+
99
+ return text.strip()
100
+
101
+
102
+ def normalize_and_expand_command_args(
103
+ command: str, args: list[str], env: dict[str, str], expand_mode: str
104
+ ) -> tuple[str, list[str]]:
105
+ """Expand variables in command and its args."""
106
+ expanded_command = expand_text(command or "", env, expand_mode)
107
+ expanded_args = [expand_text(a, env, expand_mode) for a in (args or [])]
108
+ return expanded_command, expanded_args
109
+
110
+
111
+ def split_embedded_args(
112
+ expanded_command: str, current_args: list[str], windows_semantics: bool
113
+ ) -> tuple[str, list[str]]:
114
+ """Split command string into command + args if needed."""
115
+ if not current_args and (" " in expanded_command or "\t" in expanded_command):
116
+ parts = shlex.split(expanded_command, posix=not windows_semantics)
117
+ if parts:
118
+ return parts[0], parts[1:]
119
+ return expanded_command, current_args
120
+
121
+
122
+ def resolve_executable_path(cmd_command: str) -> str | None:
123
+ """Resolve executable path (quoted or relative)."""
124
+ candidate = (cmd_command or "").strip().strip('"').strip("'")
125
+ if not candidate:
126
+ return None
127
+ if os.path.isabs(candidate) or os.path.sep in candidate:
128
+ return candidate if os.path.exists(candidate) else shutil.which(candidate)
129
+ return shutil.which(candidate)