Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3051348
fix: recognize current ngrok tunnel domains and add bore.pub/serveo.n…
federicoroncallo-hub Jul 2, 2026
0b01f29
feat(static): flag unpinned dependencies in scanned skill packages
federicoroncallo-hub Jul 2, 2026
7b24c8a
feat(static): classify URLs in config files via shared url_classifier
federicoroncallo-hub Jul 2, 2026
4ebaaf1
refactor(static): scan config files via raw URL extraction
federicoroncallo-hub Jul 2, 2026
d7d4790
feat(static): scan more manifest formats for unpinned dependencies
federicoroncallo-hub Jul 2, 2026
e70193f
fix(llm): allow Vertex AI to use ambient Application Default Credentials
gyrospectre Jul 24, 2026
cbf9fce
Bumps to resolve security findings
gyrospectre Jul 25, 2026
adfbc89
fix(llm): don't leak Vertex ADC credential path into GEMINI_API_KEY
gyrospectre Aug 3, 2026
48ba248
revert unrelated cli-command-reference.md regeneration
gyrospectre Aug 3, 2026
ecc0720
fix(static): classify suspicious URLs by hostname
vineethsai7 Aug 3, 2026
93ba6f0
fix(static): tolerate malformed setup.py input
vineethsai7 Aug 3, 2026
6994a42
fix(static): harden URL classification and reporting
vineethsai7 Aug 3, 2026
a419477
Merge remote-tracking branch 'origin/main' into HEAD
vineethsai7 Aug 3, 2026
4c5c18c
Merge commit 'a4194779' into HEAD
vineethsai7 Aug 3, 2026
8b50b49
Merge commit 'a4194779' into HEAD
vineethsai7 Aug 3, 2026
a71e8d8
Merge commit '4c5c18cc' into HEAD
vineethsai7 Aug 3, 2026
961588c
fix(static): validate suspicious URL schemes
vineethsai7 Aug 3, 2026
d4c7654
docs(vertex): clarify conditional ADC configuration
vineethsai7 Aug 3, 2026
6973751
Merge commit '961588c1' into HEAD
vineethsai7 Aug 3, 2026
62c10be
Merge commit '6973751c' into HEAD
vineethsai7 Aug 3, 2026
ea9eb7e
Merge main into feat/pr2-unpinned-deps
vineethsai7 Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/architecture/analyzers/llm-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ analyzer = LLMAnalyzer(model="gemini-2.0-flash-exp", api_key=key)
# Using LiteLLM format
analyzer = LLMAnalyzer(model="gemini/gemini-2.0-flash-exp", api_key=key)

# Vertex AI
analyzer = LLMAnalyzer(model="vertex_ai/gemini-1.5-pro") # uses GOOGLE_APPLICATION_CREDENTIALS
# Vertex AI -- uses GOOGLE_APPLICATION_CREDENTIALS if set, otherwise falls
# back to ambient Application Default Credentials (e.g. Workload Identity)
analyzer = LLMAnalyzer(model="vertex_ai/gemini-1.5-pro")
```

### Azure OpenAI
Expand Down
5 changes: 4 additions & 1 deletion docs/architecture/analyzers/static-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ flowchart TD
A["Manifest validation"] --> B["Instruction body scanning"]
B --> C["Script/code scanning"]
C --> D["Consistency checks"]
D --> E["Referenced file scanning"]
D --> D2["Dependency pinning checks"]
D2 --> E["Referenced file scanning"]
E --> F["Binary file checks"]
F --> G["Hidden file checks"]
G --> H["File inventory analysis"]
Expand All @@ -41,6 +42,7 @@ Each pass targets a different aspect of the skill package:
| Instruction body | `_scan_instruction_body()` | SKILL.md content against signature rules |
| Script scanning | `_scan_scripts()` | Python/bash/other scripts against signatures |
| Consistency | `_check_consistency()` | Mismatch between manifest claims and actual behavior |
| Dependency pinning | `_check_dependency_pinning()` | Unpinned dependencies in `requirements*.txt`, `pyproject.toml`, `setup.cfg`, `setup.py`, `Pipfile`, and manifest metadata |
| Referenced files | `_scan_referenced_files()` | Files mentioned in SKILL.md instructions |
| Binary files | `_check_binary_files()` | Extension/magic mismatch, archive detection, unknown binaries |
| Hidden files | `_check_hidden_files()` | Dotfiles, `__pycache__`, policy-allowed exceptions |
Expand Down Expand Up @@ -97,6 +99,7 @@ The pack manifest registers all rule sources and metadata for the core detection
- Hardcoded credentials and secrets
- Archive/binary risks
- Tool mismatch and manifest consistency
- Supply-chain risk from unpinned dependencies
- Hidden file and dotfile risks
- Document-embedded threats (PDF, Office macros)
- Unicode homoglyph attacks
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/threat-taxonomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Skill Scanner currently uses a subset of those codes for agent-skill risk catego
| Code Execution | `AITech-9.1` | `AISubtech-9.1.1` | Unsafe execution primitives |
| Obfuscation | `AITech-9.2` | `AISubtech-9.2.1` | Detection-evasion obfuscation patterns |
| ASCII Smuggling | `AITech-9.2` | `AISubtech-9.2.1` | Unicode Tag Block (U+E0000–U+E007F) used to hide prompt-injection payloads inside skill files; invisible in editors but decoded by LLMs |
| Supply Chain Attack | `AITech-9.3` | `AISubtech-9.3.1` | Malicious package/tool injection |
| Supply Chain Attack | `AITech-9.3` | `AISubtech-9.3.1` | Malicious package/tool injection; unpinned dependency versions |
| Unauthorized Tool Use | `AITech-12.1` | `AISubtech-12.1.3` | Unsafe/undeclared tool execution |
| Tool Poisoning | `AITech-12.1` | `AISubtech-12.1.2` | Tampering with tool behavior/data |
| Tool Shadowing | `AITech-12.1` | `AISubtech-12.1.4` | Malicious lookalike/replacement tools |
Expand Down
2 changes: 1 addition & 1 deletion docs/features/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ The LLM and Meta analyzers work with multiple LLM providers. Select a provider v

```bash
pip install cisco-ai-skill-scanner[bedrock] # AWS Bedrock (IAM credentials)
pip install cisco-ai-skill-scanner[vertex] # Google Vertex AI
pip install cisco-ai-skill-scanner[vertex] # Google Vertex AI (Workload Identity or service account)
pip install cisco-ai-skill-scanner[azure] # Azure OpenAI (managed identity)
pip install cisco-ai-skill-scanner[all] # All cloud providers
```
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Primary settings for the LLM semantic analyzer.

| Variable | Description | Example |
|---|---|---|
| `SKILL_SCANNER_LLM_API_KEY` | Primary API key for LLM analyzer and meta fallback. **(required)** | `sk-ant-...` |
| `SKILL_SCANNER_LLM_API_KEY` | Primary API key for LLM analyzer and meta fallback. Required for API-key-based providers; not required for Bedrock (IAM), Ollama (local), or Vertex AI (ambient Application Default Credentials). | `sk-ant-...` |
| `SKILL_SCANNER_LLM_MODEL` | Primary model identifier for semantic analysis. | `anthropic/claude-sonnet-4-20250514` |
| `SKILL_SCANNER_LLM_PROVIDER` | Optional provider override, including OpenAI-compatible custom endpoint routing. | `openai` |
| `SKILL_SCANNER_LLM_BASE_URL` | Optional custom endpoint base URL for provider routing. | `https://api.openai.com/v1` |
Expand Down Expand Up @@ -58,7 +58,7 @@ Credentials for Vertex AI and Google AI Studio.

| Variable | Description | Example |
|---|---|---|
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to GCP service account credentials. | `/path/to/sa-key.json` |
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to GCP service account credentials. Optional -- if unset, `vertex_ai/*` models fall back to ambient Application Default Credentials (e.g. a GCE/Cloud Run attached service account or Workload Identity), same as Bedrock's IAM role fallback. | `/path/to/sa-key.json` |
| `GEMINI_API_KEY` | Google AI Studio key; auto-set from `SKILL_SCANNER_LLM_API_KEY` when using Gemini via LiteLLM. | `(auto-set from LLM_API_KEY)` |

## VirusTotal
Expand Down Expand Up @@ -115,7 +115,7 @@ Paths, allowlists, and other advanced settings.
| `ENABLE_LLM_ANALYZER` | `skill_scanner/config/config.py` |
| `ENABLE_STATIC_ANALYZER` | `skill_scanner/config/config.py` |
| `GEMINI_API_KEY` | `skill_scanner/core/analyzers/llm_provider_config.py` |
| `GOOGLE_APPLICATION_CREDENTIALS` | `.env.example`, `skill_scanner/core/analyzers/llm_provider_config.py` |
| `GOOGLE_APPLICATION_CREDENTIALS` | `.env.example` |
| `SKILL_SCANNER_ALLOWED_ROOTS` | `skill_scanner/api/router.py` |
| `SKILL_SCANNER_LLM_API_KEY` | `.env.example`, `skill_scanner/cli/cli.py`, `skill_scanner/config/config.py`, `skill_scanner/core/analyzer_factory.py`, `skill_scanner/core/analyzers/behavioral_analyzer.py`, `skill_scanner/core/analyzers/llm_analyzer.py`, `skill_scanner/core/analyzers/llm_provider_config.py`, `skill_scanner/core/analyzers/meta_analyzer.py` |
| `SKILL_SCANNER_LLM_API_VERSION` | `.env.example`, `skill_scanner/cli/cli.py`, `skill_scanner/core/analyzer_factory.py`, `skill_scanner/core/analyzers/meta_analyzer.py` |
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/dependencies-and-llm-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ For OpenAI and OpenAI-compatible custom endpoints, `SKILL_SCANNER_LLM_USER` can
| OpenAI-compatible custom endpoint | API key + endpoint | `SKILL_SCANNER_LLM_API_KEY`, `SKILL_SCANNER_LLM_PROVIDER=openai`, `SKILL_SCANNER_LLM_BASE_URL` |
| AWS Bedrock (API key) | API key | `SKILL_SCANNER_LLM_API_KEY` |
| AWS Bedrock (IAM) | AWS credentials | `AWS_REGION`, `AWS_PROFILE` (optional: `AWS_SESSION_TOKEN`) |
| Google Vertex AI | Service account | `GOOGLE_APPLICATION_CREDENTIALS` |
| Google Vertex AI (service account) | Service account key file | `GOOGLE_APPLICATION_CREDENTIALS` |
| Google Vertex AI (ambient ADC) | Workload Identity / attached service account | none -- falls back automatically, like Bedrock IAM |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| Google AI Studio | API key | `SKILL_SCANNER_LLM_API_KEY` (auto-sets `GEMINI_API_KEY`) |
| Azure OpenAI | API key + endpoint | `SKILL_SCANNER_LLM_API_KEY`, `SKILL_SCANNER_LLM_BASE_URL`, `SKILL_SCANNER_LLM_API_VERSION` |
| Ollama | None | — |
Expand Down
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,18 @@ dependencies = [
# Floor pin: avoid compromised PyPI lines in 1.80.16 / 1.82.3 / 1.82.8 (BerriAI/litellm#24512)
# SSRF fix in 1.83.7 (GHSA-xqmj-j6mv-4862), and CVE-2026-49468 fix in 1.84.0.
"litellm>=1.84.0,<2",
# Floor pin: PYSEC-2026-2132 fixed in 8.3.3. Transitive via litellm/magika/uvicorn/typer.
"click>=8.3.3",
]

[project.optional-dependencies]
# Google AI Studio / Gemini SDKs
google = [
"google-genai>=1.60,<2",
"google-generativeai>=0.8,<1",
# Floor pins for transitive CVEs pulled in via google-generativeai -> google-api-python-client / google-auth
"httplib2>=0.32.0", # PYSEC-2026-3444
"pyasn1>=0.6.4", # PYSEC-2026-3455, PYSEC-2026-3456, PYSEC-2026-3457
]
# AWS Bedrock support (requires boto3 for IAM credentials)
bedrock = [
Expand All @@ -91,6 +96,9 @@ bedrock = [
# Google Vertex AI support
vertex = [
"google-cloud-aiplatform>=1.130,<2",
# Floor pins for transitive CVEs pulled in via google-cloud-aiplatform -> google-api-core / google-auth
"httplib2>=0.32.0", # PYSEC-2026-3444
"pyasn1>=0.6.4", # PYSEC-2026-3455, PYSEC-2026-3456, PYSEC-2026-3457
]
# Azure OpenAI support (for managed identity auth)
azure = [
Expand All @@ -107,6 +115,8 @@ all = [
"boto3>=1.40,<2",
"google-cloud-aiplatform>=1.130,<2",
"azure-identity>=1.20,<2",
"httplib2>=0.32.0", # PYSEC-2026-3444
"pyasn1>=0.6.4", # PYSEC-2026-3455, PYSEC-2026-3456, PYSEC-2026-3457
]

[dependency-groups]
Expand Down
15 changes: 12 additions & 3 deletions scripts/generate_reference_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,11 @@ def add(var: str, source: str) -> None:

def _describe_env_var(var: str) -> str:
descriptions = {
"SKILL_SCANNER_LLM_API_KEY": "Primary API key for LLM analyzer and meta fallback.",
"SKILL_SCANNER_LLM_API_KEY": (
"Primary API key for LLM analyzer and meta fallback. Required for "
"API-key-based providers; not required for Bedrock (IAM), Ollama "
"(local), or Vertex AI (ambient Application Default Credentials)."
),
"SKILL_SCANNER_LLM_MODEL": "Primary model identifier for semantic analysis.",
"SKILL_SCANNER_LLM_PROVIDER": "Optional provider override, including OpenAI-compatible custom endpoint routing.",
"SKILL_SCANNER_LLM_BASE_URL": "Optional custom endpoint base URL for provider routing.",
Expand All @@ -410,7 +414,12 @@ def _describe_env_var(var: str) -> str:
"AWS_REGION": "AWS region for Bedrock-backed flows.",
"AWS_PROFILE": "AWS credential profile for Bedrock IAM auth.",
"AWS_SESSION_TOKEN": "Optional AWS session token.",
"GOOGLE_APPLICATION_CREDENTIALS": "Path to GCP service account credentials.",
"GOOGLE_APPLICATION_CREDENTIALS": (
"Path to GCP service account credentials. Optional -- if unset, "
"`vertex_ai/*` models fall back to ambient Application Default "
"Credentials (e.g. a GCE/Cloud Run attached service account or "
"Workload Identity), same as Bedrock's IAM role fallback."
),
"SKILL_SCANNER_ALLOWED_ROOTS": "Colon-delimited API path allowlist for server-side path access.",
"SKILL_SCANNER_TAXONOMY_PATH": "Path to a custom Cisco AI taxonomy YAML file (overridden by `--taxonomy`).",
"SKILL_SCANNER_THREAT_MAPPING_PATH": "Path to a custom threat mapping YAML file (overridden by `--threat-mapping`).",
Expand Down Expand Up @@ -518,7 +527,7 @@ def _describe_env_var(var: str) -> str:
"SKILL_SCANNER_THREAT_MAPPING_PATH": "/path/to/threats.yaml",
}

_ENV_VAR_REQUIRED: set[str] = {"SKILL_SCANNER_LLM_API_KEY"}
_ENV_VAR_REQUIRED: set[str] = set()


def _render_configuration_reference() -> str:
Expand Down
11 changes: 8 additions & 3 deletions skill_scanner/core/analyzers/llm_provider_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,12 @@ def _resolve_api_key(self, api_key: str | None) -> str | None:
Uses SKILL_SCANNER_LLM_API_KEY consistently for all providers.

Special cases:
- Vertex AI: Uses GOOGLE_APPLICATION_CREDENTIALS (service account)
- Vertex AI: Always returns ``None`` -- LiteLLM/google-auth read
GOOGLE_APPLICATION_CREDENTIALS directly from the environment when
set, or fall back to ambient Application Default Credentials
(like Bedrock's IAM role) -- e.g. a GCE/Cloud Run attached
service account or Workload Identity, with no key file on disk
at all.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- Ollama: No API key needed (local)
- Azure: Falls back to Entra ID (``az login``) when no API key is set
"""
Expand All @@ -182,7 +187,7 @@ def _resolve_api_key(self, api_key: str | None) -> str | None:

# Special cases with different auth mechanisms
if self.is_vertex:
return os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
return None
elif self.is_ollama:
return None

Expand Down Expand Up @@ -269,7 +274,7 @@ def _normalize_gemini_model_name(self, model: str) -> str:

def validate(self) -> None:
"""Validate that configuration is complete."""
if not self.is_bedrock and not self.is_ollama and not self.api_key:
if not self.is_bedrock and not self.is_ollama and not self.is_vertex and not self.api_key:
if self.is_azure:
raise ValueError(
f"No API key or Entra ID credentials found for Azure model {self.model}. "
Expand Down
Loading
Loading