Skip to content

Commit 199fa44

Browse files
federicoroncallo-hubcursoragentgyrospectrevineethsai7
authored
feat: add OSV.dev dependency vulnerability analyzer (--use-osv) (#135)
* fix: recognize current ngrok tunnel domains and add bore.pub/serveo.net/localtunnel.me ngrok migrated off ngrok.io to ngrok-free.dev / ngrok.app, so exfil to a current ngrok endpoint slipped past the suspicious-domain checks. Add the modern ngrok domains plus bore.pub, serveo.net and localtunnel.me to: - ContextExtractor.SUSPICIOUS_DOMAINS (Python string-literal URL classification) - tool_chaining_abuse_generic.yara / command_injection_generic.yara exfil dests - cross_skill_scanner exfil pattern list Add regression tests for the new domains (YARA true-positives + ContextExtractor). Co-authored-by: Cursor <cursoragent@cursor.com> * feat(static): flag unpinned dependencies in scanned skill packages Skill packages are end-user applications, so unpinned dependencies (requests>=2 or a bare requests) let a later, potentially compromised release be pulled in at install time -- a supply-chain risk. Only the LLM prompt mentioned this previously; there was no deterministic check. Add StaticAnalyzer._check_dependency_pinning() which parses requirements*.txt files and a manifest metadata "dependencies" list, emitting SUPPLY_CHAIN_UNPINNED_DEPENDENCY (MEDIUM for open ranges/bare names, LOW for ==x.* wildcard pins). Skips when a lockfile (uv.lock/poetry.lock/...) is present, since versions are already resolved -- this is why the scanner's own library pinning policy (ranges in pyproject.toml) is not affected. Register the rule in the core pack.yaml, document the new pass in the static-analyzer docs and threat taxonomy, and add unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(static): classify URLs in config files via shared url_classifier The suspicious/legitimate domain lists and matching logic previously lived inside ContextExtractor and only ran over Python AST string literals, so a tunnel/proxy endpoint hidden in a config value (e.g. base_url in config.yaml) was never classified. Refactor (behavior-preserving): extract SUSPICIOUS_DOMAINS/LEGITIMATE_DOMAINS and the classification into skill_scanner/core/static_analysis/url_classifier.py (classify_url + extract_urls). ContextExtractor imports from it and keeps the lists as class attributes for backward compatibility; existing suspicious-URL behavior is unchanged (covered by refactor-safety tests). New pass: StaticAnalyzer._scan_config_files() parses config.yaml/.yml/.json, settings.*, and *.toml (regex fallback on parse failure), runs each URL through the shared classifier, and emits CONFIG_SUSPICIOUS_URL (HIGH). Registered in the core pack.yaml. Docs updated (static-analyzer, behavioral-analyzer). Adds unit tests plus a labeled config-routed exfil eval sample; benchmark stays at 100% P/R. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(static): scan config files via raw URL extraction Collapse the structured YAML/JSON/TOML parse-and-walk in config URL scanning down to a single raw-text extract_urls() pass. The raw scan is simpler, drops the json/yaml/tomllib imports and the _iter_string_values recursion, and additionally catches suspicious URLs hidden in config comments. classify_url() still only flags known tunnel/exfil domains, so false-positive risk stays low. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(static): scan more manifest formats for unpinned dependencies Extend the unpinned-dependency check beyond requirements*.txt and manifest metadata to also read pyproject.toml ([project] dependencies and optional-dependencies), setup.cfg ([options] install_requires / extras), setup.py (install_requires literals via AST), and Pipfile ([packages]/[dev-packages]). All sources are normalized to requirement strings and run through the existing classifier, so a lockfile still suppresses findings and pinned specs stay clean. Co-authored-by: Cursor <cursoragent@cursor.com> * feat: add OSV dependency vulnerability analyzer (--use-osv) Add an opt-in external analyzer that checks a skill's pinned Python dependencies against the free, open OSV.dev vulnerability database. Follows the established external-analyzer pattern (like VirusTotal): no API key, and it fails open so a network error never breaks a scan. - OSVAnalyzer parses requirements*.txt and manifest metadata dependencies, queries only exact (==) pins via OSV querybatch, and emits SUPPLY_CHAIN_KNOWN_VULNERABILITY (HIGH) with advisory IDs/links. - Wire use_osv through analyzer_factory, the CLI (--use-osv), the API router (use_osv on JSON + upload endpoints), and the setup wizard. - Uses httpx (already a dependency): zero new runtime dependencies. Tests mock all OSV HTTP calls (no live network). Adds a deep-dive doc and updates the analyzer selection guide, analyzer index, CLI/API references, and configuration/dependency references. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(osv): collect pinned dependencies from more manifest formats Extend OSV pin collection beyond requirements*.txt and manifest metadata to also read pyproject.toml, setup.cfg, setup.py (install_requires via AST), and Pipfile. Only exact == pins are queried against OSV; ranges are left to the static unpinned-dependency check. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(llm): allow Vertex AI to use ambient Application Default Credentials ProviderConfig.validate() required a truthy credential for every provider except Bedrock and Ollama, and the only credential source it checked for Vertex was GOOGLE_APPLICATION_CREDENTIALS. This blocked ambient auth via a GCE/Cloud Run attached service account or Workload Identity, even though LiteLLM/google-auth already fall back to it automatically when no explicit credential is passed -- the same pattern already supported for Bedrock's IAM role. Excludes is_vertex from the check, mirroring the Bedrock/Ollama precedent, and documents the fallback. * Bumps to resolve security findings * fix(llm): don't leak Vertex ADC credential path into GEMINI_API_KEY - _resolve_api_key() now returns None for Vertex instead of the GOOGLE_APPLICATION_CREDENTIALS path, since vertex_ai/gemini-* models set both is_vertex and is_gemini, which was causing the file path to be written into GEMINI_API_KEY. - Regenerated configuration-reference.md via generate_reference_docs.py instead of hand-editing, and updated the underlying descriptions so the doc doesn't drift on next regeneration. Addresses CodeRabbit review feedbak on #144. * revert unrelated cli-command-reference.md regeneration * fix(static): classify suspicious URLs by hostname * fix(static): tolerate malformed setup.py input * docs(static): correct analyzer pass count * fix(static): validate suspicious URL schemes * docs(vertex): clarify conditional ADC configuration --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: gyrospectre <7224858+gyrospectre@users.noreply.github.com> Co-authored-by: Vineeth Sai Narajala <vnarajal@cisco.com>
1 parent 0f44aff commit 199fa44

15 files changed

Lines changed: 697 additions & 3 deletions

docs/architecture/analyzers/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Analyzers implement independent detection strategies and return normalized `Find
1717
| Behavioral | Static AST/dataflow | No | 1 | Python source behavior |
1818
| VirusTotal | External intel | Yes | 1 | Binary hash/file reputation |
1919
| AI Defense | External service | Yes | 1 | Prompt/content/code threat signal |
20+
| OSV | External service | No | 1 | Known-vulnerable pinned dependencies |
2021
| Trigger | Heuristic | No | 1 | Vague or risky trigger descriptions |
2122
| LLM | Semantic | Usually (not required for Bedrock IAM mode) | 2 | Intent-level threat reasoning |
2223
| Meta | Semantic post-pass | Usually (not required for Bedrock IAM mode) | 2 | FP filtering and prioritization |
@@ -44,5 +45,6 @@ See [Scanning Pipeline](../scanning-pipeline.md) for the full execution flow.
4445
- [LLM Analyzer](llm-analyzer.md)
4546
- [Meta-Analyzer](meta-analyzer.md)
4647
- [AI Defense Analyzer](aidefense-analyzer.md)
48+
- [OSV Analyzer](osv-analyzer.md)
4749
- [Analyzer Selection Guide](meta-and-external-analyzers.md)
4850
- [Writing Custom Rules](writing-custom-rules.md)

docs/architecture/analyzers/meta-and-external-analyzers.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This page helps you choose which optional analyzers to enable for your use case.
1717
| Cloud-based threat classification | AI Defense | `--use-aidefense` | Requires Cisco AI Defense API access |
1818
| Catch vague/risky skill descriptions | Trigger | `--use-trigger` | Lightweight; no external dependencies |
1919
| Python dataflow and cross-file analysis | Behavioral | `--use-behavioral` | CPU-intensive for large codebases |
20+
| Known-vulnerable dependency detection | OSV | `--use-osv` | Requires network; queries PyPI pins only (no API key) |
2021

2122
## When to Use Each Analyzer
2223

@@ -99,15 +100,28 @@ Skip when: the skill contains no Python source, or you need the fastest possible
99100

100101
See [Behavioral Analyzer deep dive](behavioral-analyzer.md) for detection patterns.
101102

103+
### OSV Analyzer
104+
105+
Best for: skills that declare pinned Python dependencies you want checked against known CVEs/advisories. Queries the free, open [OSV.dev](https://osv.dev) database — no API key required.
106+
107+
```bash
108+
skill-scanner scan ./my-skill --use-osv
109+
```
110+
111+
Skip when: scanning air-gapped/offline (it requires network access), or the skill declares no exactly pinned dependencies. It fails open — a network error logs a warning and yields no findings.
112+
113+
See [OSV Analyzer deep dive](osv-analyzer.md) for details.
114+
102115
## Recommended Combinations
103116

104117
| Scenario | Flags |
105118
|---|---|
106119
| Quick CI gate | (defaults -- core analyzers only) |
107120
| Thorough single-skill review | `--use-llm --use-behavioral --use-trigger --enable-meta` |
108121
| Binary-heavy skill | `--use-virustotal` |
122+
| Dependency-heavy skill | `--use-osv` |
109123
| Enterprise with AI Defense | `--use-aidefense --use-llm --enable-meta` |
110-
| Maximum coverage | `--use-llm --use-behavioral --use-trigger --use-virustotal --enable-meta` |
124+
| Maximum coverage | `--use-llm --use-behavioral --use-trigger --use-virustotal --use-osv --enable-meta` |
111125

112126
## Bytecode Analyzer
113127

@@ -128,4 +142,5 @@ skill-scanner scan-all ./skills-dir --check-overlap
128142
- [LLM Analyzer](llm-analyzer.md)
129143
- [Meta Analyzer](meta-analyzer.md)
130144
- [AI Defense Analyzer](aidefense-analyzer.md)
145+
- [OSV Analyzer](osv-analyzer.md)
131146
- [Binary Handling](../binary-handling.md)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# OSV Analyzer
2+
3+
## Overview
4+
5+
The OSV Analyzer checks a skill's declared Python dependencies against the
6+
[OSV.dev](https://osv.dev) vulnerability database — a free, open aggregator of
7+
security advisories (GHSA, PYSEC, CVE, and more). It is an **opt-in external
8+
analyzer** (like VirusTotal): it requires network access, needs **no API key**,
9+
and **fails open** so a network problem never breaks a scan.
10+
11+
## What It Detects
12+
13+
- **Known-vulnerable dependency versions** — a pinned dependency
14+
(`package==1.2.3`) that has one or more advisories in OSV is flagged as
15+
`SUPPLY_CHAIN_KNOWN_VULNERABILITY` (HIGH), with the advisory IDs and links.
16+
17+
Only dependencies pinned to an **exact** version are queried. An open range
18+
(`package>=1`) has no single version to look up; that risk is already surfaced
19+
by the static [unpinned-dependency check](static-analyzer.md).
20+
21+
## Sources Scanned
22+
23+
| Source | Notes |
24+
|--------|-------|
25+
| `requirements*.txt` | `requirements.txt`, `requirements-dev.txt`, etc. |
26+
| `pyproject.toml` | `[project]` dependencies and optional-dependencies (PEP 621) |
27+
| `setup.cfg` | `[options] install_requires` and `[options.extras_require]` |
28+
| `setup.py` | String literals inside `install_requires=[...]` (parsed via AST, not executed) |
29+
| `Pipfile` | `[packages]` and `[dev-packages]` sections |
30+
| Manifest `metadata.dependencies` | Optional list of requirement strings in SKILL.md frontmatter |
31+
32+
Ecosystem defaults to `PyPI`.
33+
34+
## Usage
35+
36+
### Command Line
37+
38+
```bash
39+
# Enable OSV dependency scanning (no API key needed)
40+
skill-scanner scan /path/to/skill --use-osv
41+
42+
# Combine with other analyzers
43+
skill-scanner scan /path/to/skill --use-osv --use-behavioral
44+
```
45+
46+
### Python API
47+
48+
```python
49+
from skill_scanner.core.analyzers.osv_analyzer import OSVAnalyzer
50+
from skill_scanner.core.loader import SkillLoader
51+
52+
analyzer = OSVAnalyzer(enabled=True)
53+
skill = SkillLoader().load_skill("/path/to/skill")
54+
findings = analyzer.analyze(skill)
55+
```
56+
57+
### API
58+
59+
Set `use_osv: true` on the scan request (see the
60+
[API Endpoint Reference](../../reference/api-endpoint-reference.md)).
61+
62+
## How It Works
63+
64+
1. **Collect pins** — parse every supported dependency source (see
65+
[Sources Scanned](#sources-scanned)), keeping only exact `==` pins as
66+
`(name, version)` pairs.
67+
2. **Batch query** — POST all pins to `https://api.osv.dev/v1/querybatch`
68+
(`{"package": {"ecosystem": "PyPI", "name": ...}, "version": ...}`).
69+
3. **Generate findings** — for each package that returns advisories, emit a
70+
`SUPPLY_CHAIN_KNOWN_VULNERABILITY` finding listing the advisory IDs.
71+
72+
## Error Handling
73+
74+
The analyzer fails open. On any network/HTTP error it logs a warning and
75+
returns no findings, so an offline or air-gapped environment simply skips the
76+
check rather than failing the scan.
77+
78+
## Dependencies
79+
80+
Uses `httpx`, which is already a scanner dependency — enabling OSV adds **no new
81+
runtime dependency** and no API key.
82+
83+
## Related Pages
84+
85+
- [Analyzer Selection Guide](meta-and-external-analyzers.md) — when to enable `--use-osv`
86+
- [Static Analyzer](static-analyzer.md) — the complementary unpinned-dependency check

docs/architecture/analyzers/static-analyzer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
> [!TIP]
44
> **TL;DR**
55
>
6-
> The static analyzer runs 14 detection passes per skill covering YAML signatures, YARA rules, Python checks, binary inspection, document analysis, homoglyph detection, and allowed-tools enforcement. It is always-on (core analyzer) and requires no external services.
6+
> The static analyzer runs 15 detection passes per skill covering YAML signatures, YARA rules, Python checks, binary inspection, document analysis, homoglyph detection, dependency pinning, and allowed-tools enforcement. It is always-on (core analyzer) and requires no external services.
77
88
The static analyzer is the primary deterministic detection engine. It combines YAML signature matching, YARA-X rule scanning, Python-based checks, and file inventory analysis to detect security threats without requiring external services.
99

docs/reference/api-endpoint-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ curl -X POST http://localhost:8000/scan-upload \
9090
| `use_aidefense` | `bool` |
9191
| `aidefense_api_url` | `str | None` |
9292
| `use_trigger` | `bool` |
93+
| `use_osv` | `bool` |
9394
| `enable_meta` | `bool` |
9495
| `llm_consensus_runs` | `int` |
9596

@@ -131,6 +132,7 @@ curl -X POST http://localhost:8000/scan-upload \
131132
| `use_aidefense` | `bool` |
132133
| `aidefense_api_url` | `str | None` |
133134
| `use_trigger` | `bool` |
135+
| `use_osv` | `bool` |
134136
| `enable_meta` | `bool` |
135137
| `llm_consensus_runs` | `int` |
136138

docs/reference/cli-command-reference.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Flags shared by `scan` and `scan-all`:
3333
| `--use-behavioral` | off | Enable the behavioral analyzer |
3434
| `--use-virustotal` | off | Enable VirusTotal hash lookups |
3535
| `--use-aidefense` | off | Enable Cisco AI Defense analyzer |
36+
| `--use-osv` | off | Enable OSV.dev dependency vulnerability scanning (no API key; requires network) |
3637
| `--enable-meta` | off | Enable the meta (cross-correlation) analyzer |
3738
| `--fail-on-findings` | off | Exit non-zero if critical or high findings are reported; equivalent to `--fail-on-severity high` (CI gate) |
3839
| `--fail-on-severity LEVEL` | off | Exit non-zero if findings at or above LEVEL exist (critical, high, medium, low, info) |
@@ -156,6 +157,8 @@ options:
156157
--use-aidefense Enable AI Defense analyzer (requires API key)
157158
--aidefense-api-key AIDEFENSE_API_KEY
158159
AI Defense API key (or set AI_DEFENSE_API_KEY)
160+
--use-osv Enable OSV.dev dependency vulnerability scanning (no
161+
API key; requires network)
159162
--aidefense-api-url AIDEFENSE_API_URL
160163
AI Defense API URL (optional, defaults to US region)
161164
--llm-provider {anthropic,openai}
@@ -268,6 +271,8 @@ options:
268271
--use-aidefense Enable AI Defense analyzer (requires API key)
269272
--aidefense-api-key AIDEFENSE_API_KEY
270273
AI Defense API key (or set AI_DEFENSE_API_KEY)
274+
--use-osv Enable OSV.dev dependency vulnerability scanning (no
275+
API key; requires network)
271276
--aidefense-api-url AIDEFENSE_API_URL
272277
AI Defense API URL (optional, defaults to US region)
273278
--llm-provider {anthropic,openai}
@@ -384,6 +389,8 @@ options:
384389
--use-aidefense Enable AI Defense analyzer (requires API key)
385390
--aidefense-api-key AIDEFENSE_API_KEY
386391
AI Defense API key (or set AI_DEFENSE_API_KEY)
392+
--use-osv Enable OSV.dev dependency vulnerability scanning (no
393+
API key; requires network)
387394
--aidefense-api-url AIDEFENSE_API_URL
388395
AI Defense API URL (optional, defaults to US region)
389396
--llm-provider {anthropic,openai}

docs/reference/configuration-reference.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ Enable the Cisco AI Defense cloud analyzer.
7979
| `AI_DEFENSE_API_KEY` | Cisco AI Defense analyzer API key. | `(your AI Defense key)` |
8080
| `AI_DEFENSE_API_URL` | Cisco AI Defense endpoint override. | `https://us.api.inspect.aidefense.security.cisco.com/api/v1` |
8181
82+
## OSV Dependency Scanning
83+
84+
The OSV analyzer queries [OSV.dev](https://osv.dev) for known-vulnerable pinned dependencies. It is an external service that requires **no API key**, only outbound network access to `api.osv.dev`. Enable it with `--use-osv` (or `use_osv` on the API). Skip it in air-gapped environments — with no network it fails open and reports nothing.
85+
8286
## Feature Toggles
8387
8488
Override default analyzer enablement via environment. Values: `true`/`1` or `false`/`0`.

docs/reference/dependencies-and-llm-providers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ All versions from [`pyproject.toml`](https://github.com/cisco-ai-defense/skill-s
1212
| `uvicorn[standard]` | >= 0.29.0 | ASGI server |
1313
| `pydantic` | >= 2.6.0 | Data validation and serialization |
1414
| `python-multipart` | >= 0.0.6 | File upload handling |
15-
| `httpx` | >= 0.28.1 | Async HTTP client |
15+
| `httpx` | >= 0.28.1 | HTTP client (also used by the VirusTotal, AI Defense, and OSV external analyzers) |
1616

1717
### CLI and TUI
1818

skill_scanner/api/router.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ class ScanRequest(BaseModel):
206206
use_aidefense: bool = Field(False, description="Enable AI Defense analyzer")
207207
aidefense_api_url: str | None = Field(None, description="AI Defense API URL")
208208
use_trigger: bool = Field(False, description="Enable trigger specificity analysis")
209+
use_osv: bool = Field(False, description="Enable OSV.dev dependency vulnerability scanning")
209210
enable_meta: bool = Field(False, description="Enable meta-analysis for false positive filtering")
210211
llm_consensus_runs: int = Field(1, description="Number of LLM consensus runs (majority vote)")
211212

@@ -251,6 +252,7 @@ class BatchScanRequest(BaseModel):
251252
use_aidefense: bool = False
252253
aidefense_api_url: str | None = None
253254
use_trigger: bool = False
255+
use_osv: bool = False
254256
enable_meta: bool = Field(False, description="Enable meta-analysis")
255257
llm_consensus_runs: int = Field(1, description="Number of LLM consensus runs (majority vote)")
256258

@@ -297,6 +299,7 @@ def _build_analyzers(
297299
aidefense_api_key: str | None = None,
298300
aidefense_api_url: str | None = None,
299301
use_trigger: bool = False,
302+
use_osv: bool = False,
300303
llm_consensus_runs: int = 1,
301304
):
302305
"""Build the analyzer list — delegates to the centralized factory."""
@@ -313,6 +316,7 @@ def _build_analyzers(
313316
aidefense_api_key=aidefense_api_key,
314317
aidefense_api_url=aidefense_api_url,
315318
use_trigger=use_trigger,
319+
use_osv=use_osv,
316320
llm_consensus_runs=llm_consensus_runs,
317321
)
318322

@@ -425,6 +429,7 @@ def run_scan():
425429
aidefense_api_key=aidefense_api_key,
426430
aidefense_api_url=request.aidefense_api_url,
427431
use_trigger=request.use_trigger,
432+
use_osv=request.use_osv,
428433
llm_consensus_runs=request.llm_consensus_runs,
429434
)
430435
scanner = SkillScanner(analyzers=analyzers, policy=policy)
@@ -505,6 +510,7 @@ async def scan_uploaded_skill(
505510
aidefense_api_key: str | None = Header(None, alias="X-AIDefense-Key"),
506511
aidefense_api_url: str | None = Form(None, description="AI Defense API URL"),
507512
use_trigger: bool = Form(False, description="Enable trigger specificity analysis"),
513+
use_osv: bool = Form(False, description="Enable OSV.dev dependency vulnerability scanning"),
508514
enable_meta: bool = Form(False, description="Enable meta-analysis for FP filtering"),
509515
llm_consensus_runs: int = Form(1, description="Number of LLM consensus runs"),
510516
):
@@ -598,6 +604,7 @@ async def scan_uploaded_skill(
598604
use_aidefense=use_aidefense,
599605
aidefense_api_url=aidefense_api_url,
600606
use_trigger=use_trigger,
607+
use_osv=use_osv,
601608
enable_meta=enable_meta,
602609
llm_consensus_runs=llm_consensus_runs,
603610
)
@@ -684,6 +691,7 @@ def run_batch_scan(
684691
aidefense_api_key=aidefense_api_key,
685692
aidefense_api_url=request.aidefense_api_url,
686693
use_trigger=request.use_trigger,
694+
use_osv=request.use_osv,
687695
llm_consensus_runs=request.llm_consensus_runs,
688696
)
689697

skill_scanner/cli/cli.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ def _build_analyzers(policy: ScanPolicy, args: argparse.Namespace, status: Calla
134134
aidefense_api_key=getattr(args, "aidefense_api_key", None),
135135
aidefense_api_url=getattr(args, "aidefense_api_url", None),
136136
use_trigger=getattr(args, "use_trigger", False),
137+
use_osv=getattr(args, "use_osv", False),
137138
llm_provider=getattr(args, "llm_provider", None),
138139
llm_consensus_runs=getattr(args, "llm_consensus_runs", 1),
139140
llm_max_tokens=getattr(args, "llm_max_tokens", None),
@@ -153,6 +154,8 @@ def _build_analyzers(policy: ScanPolicy, args: argparse.Namespace, status: Calla
153154
status("Using AI Defense analyzer")
154155
elif name == "trigger":
155156
status("Using Trigger analyzer (description specificity analysis)")
157+
elif name == "osv_analyzer":
158+
status("Using OSV dependency vulnerability analyzer")
156159

157160
return analyzers
158161

@@ -946,6 +949,11 @@ def _add_common_scan_flags(parser: argparse.ArgumentParser) -> None:
946949
parser.add_argument("--use-aidefense", action="store_true", help="Enable AI Defense analyzer (requires API key)")
947950
parser.add_argument("--aidefense-api-key", help="AI Defense API key (or set AI_DEFENSE_API_KEY)")
948951
parser.add_argument("--aidefense-api-url", help="AI Defense API URL (optional, defaults to US region)")
952+
parser.add_argument(
953+
"--use-osv",
954+
action="store_true",
955+
help="Enable OSV.dev dependency vulnerability scanning (no API key; requires network)",
956+
)
949957
parser.add_argument(
950958
"--llm-provider",
951959
choices=["anthropic", "openai", "openai-compatible"],

0 commit comments

Comments
 (0)