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.
- cisco_ai_skill_scanner-1.0.0.dist-info/METADATA +253 -0
- cisco_ai_skill_scanner-1.0.0.dist-info/RECORD +100 -0
- cisco_ai_skill_scanner-1.0.0.dist-info/WHEEL +4 -0
- cisco_ai_skill_scanner-1.0.0.dist-info/entry_points.txt +4 -0
- cisco_ai_skill_scanner-1.0.0.dist-info/licenses/LICENSE +17 -0
- skillanalyzer/__init__.py +45 -0
- skillanalyzer/_version.py +34 -0
- skillanalyzer/api/__init__.py +25 -0
- skillanalyzer/api/api.py +34 -0
- skillanalyzer/api/api_cli.py +78 -0
- skillanalyzer/api/api_server.py +634 -0
- skillanalyzer/api/router.py +527 -0
- skillanalyzer/cli/__init__.py +25 -0
- skillanalyzer/cli/cli.py +816 -0
- skillanalyzer/config/__init__.py +26 -0
- skillanalyzer/config/config.py +149 -0
- skillanalyzer/config/config_parser.py +122 -0
- skillanalyzer/config/constants.py +85 -0
- skillanalyzer/core/__init__.py +24 -0
- skillanalyzer/core/analyzers/__init__.py +75 -0
- skillanalyzer/core/analyzers/aidefense_analyzer.py +872 -0
- skillanalyzer/core/analyzers/base.py +53 -0
- skillanalyzer/core/analyzers/behavioral/__init__.py +30 -0
- skillanalyzer/core/analyzers/behavioral/alignment/__init__.py +45 -0
- skillanalyzer/core/analyzers/behavioral/alignment/alignment_llm_client.py +240 -0
- skillanalyzer/core/analyzers/behavioral/alignment/alignment_orchestrator.py +216 -0
- skillanalyzer/core/analyzers/behavioral/alignment/alignment_prompt_builder.py +422 -0
- skillanalyzer/core/analyzers/behavioral/alignment/alignment_response_validator.py +136 -0
- skillanalyzer/core/analyzers/behavioral/alignment/threat_vulnerability_classifier.py +198 -0
- skillanalyzer/core/analyzers/behavioral_analyzer.py +453 -0
- skillanalyzer/core/analyzers/cross_skill_analyzer.py +490 -0
- skillanalyzer/core/analyzers/llm_analyzer.py +440 -0
- skillanalyzer/core/analyzers/llm_prompt_builder.py +270 -0
- skillanalyzer/core/analyzers/llm_provider_config.py +215 -0
- skillanalyzer/core/analyzers/llm_request_handler.py +284 -0
- skillanalyzer/core/analyzers/llm_response_parser.py +81 -0
- skillanalyzer/core/analyzers/meta_analyzer.py +845 -0
- skillanalyzer/core/analyzers/static.py +1105 -0
- skillanalyzer/core/analyzers/trigger_analyzer.py +341 -0
- skillanalyzer/core/analyzers/virustotal_analyzer.py +463 -0
- skillanalyzer/core/exceptions.py +77 -0
- skillanalyzer/core/loader.py +377 -0
- skillanalyzer/core/models.py +300 -0
- skillanalyzer/core/reporters/__init__.py +26 -0
- skillanalyzer/core/reporters/json_reporter.py +65 -0
- skillanalyzer/core/reporters/markdown_reporter.py +209 -0
- skillanalyzer/core/reporters/sarif_reporter.py +246 -0
- skillanalyzer/core/reporters/table_reporter.py +195 -0
- skillanalyzer/core/rules/__init__.py +19 -0
- skillanalyzer/core/rules/patterns.py +165 -0
- skillanalyzer/core/rules/yara_scanner.py +157 -0
- skillanalyzer/core/scanner.py +437 -0
- skillanalyzer/core/static_analysis/__init__.py +27 -0
- skillanalyzer/core/static_analysis/cfg/__init__.py +21 -0
- skillanalyzer/core/static_analysis/cfg/builder.py +439 -0
- skillanalyzer/core/static_analysis/context_extractor.py +742 -0
- skillanalyzer/core/static_analysis/dataflow/__init__.py +25 -0
- skillanalyzer/core/static_analysis/dataflow/forward_analysis.py +715 -0
- skillanalyzer/core/static_analysis/interprocedural/__init__.py +21 -0
- skillanalyzer/core/static_analysis/interprocedural/call_graph_analyzer.py +406 -0
- skillanalyzer/core/static_analysis/interprocedural/cross_file_analyzer.py +190 -0
- skillanalyzer/core/static_analysis/parser/__init__.py +21 -0
- skillanalyzer/core/static_analysis/parser/python_parser.py +380 -0
- skillanalyzer/core/static_analysis/semantic/__init__.py +28 -0
- skillanalyzer/core/static_analysis/semantic/name_resolver.py +206 -0
- skillanalyzer/core/static_analysis/semantic/type_analyzer.py +200 -0
- skillanalyzer/core/static_analysis/taint/__init__.py +21 -0
- skillanalyzer/core/static_analysis/taint/tracker.py +252 -0
- skillanalyzer/core/static_analysis/types/__init__.py +36 -0
- skillanalyzer/data/__init__.py +30 -0
- skillanalyzer/data/prompts/boilerplate_protection_rule_prompt.md +26 -0
- skillanalyzer/data/prompts/code_alignment_threat_analysis_prompt.md +901 -0
- skillanalyzer/data/prompts/llm_response_schema.json +71 -0
- skillanalyzer/data/prompts/skill_meta_analysis_prompt.md +303 -0
- skillanalyzer/data/prompts/skill_threat_analysis_prompt.md +263 -0
- skillanalyzer/data/prompts/unified_response_schema.md +97 -0
- skillanalyzer/data/rules/signatures.yaml +440 -0
- skillanalyzer/data/yara_rules/autonomy_abuse.yara +66 -0
- skillanalyzer/data/yara_rules/code_execution.yara +61 -0
- skillanalyzer/data/yara_rules/coercive_injection.yara +115 -0
- skillanalyzer/data/yara_rules/command_injection.yara +54 -0
- skillanalyzer/data/yara_rules/credential_harvesting.yara +115 -0
- skillanalyzer/data/yara_rules/prompt_injection.yara +71 -0
- skillanalyzer/data/yara_rules/script_injection.yara +83 -0
- skillanalyzer/data/yara_rules/skill_discovery_abuse.yara +57 -0
- skillanalyzer/data/yara_rules/sql_injection.yara +73 -0
- skillanalyzer/data/yara_rules/system_manipulation.yara +65 -0
- skillanalyzer/data/yara_rules/tool_chaining_abuse.yara +60 -0
- skillanalyzer/data/yara_rules/transitive_trust_abuse.yara +73 -0
- skillanalyzer/data/yara_rules/unicode_steganography.yara +65 -0
- skillanalyzer/hooks/__init__.py +21 -0
- skillanalyzer/hooks/pre_commit.py +450 -0
- skillanalyzer/threats/__init__.py +25 -0
- skillanalyzer/threats/threats.py +480 -0
- skillanalyzer/utils/__init__.py +28 -0
- skillanalyzer/utils/command_utils.py +129 -0
- skillanalyzer/utils/di_container.py +154 -0
- skillanalyzer/utils/file_utils.py +86 -0
- skillanalyzer/utils/logging_config.py +96 -0
- 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)
|