Search for packages and assess security risk before adding as dependencies
This skill searches for packages across ecosystems and provides a comprehensive security risk assessment before adding them as dependencies.
Primary output format: Markdown. All reports, tables, summaries, and diffs MUST be presented as formatted markdown text directly — never generate scripts or programs to produce output that can be expressed as markdown.
Visual data — use Mermaid diagrams to display data visually when it aids comprehension. Mermaid renders natively in markdown and requires no external tools. Use it for:
graph TD or graph LRtimelinepie or quadrantChartflowchartExample — vulnerability distribution for a package:
```mermaid
pie title Vulnerability Severity
"Critical" : 1
"High" : 2
"Medium" : 5
"Low" : 3
```
If uv is available, richer visualizations can be generated with Python (matplotlib, plotly) and saved to .vulnetix/:
command -v uv &>/dev/null && uv run --with matplotlib python3 -c '
import matplotlib.pyplot as plt
# ... generate chart ...
plt.savefig(".vulnetix/chart.png", dpi=150, bbox_inches="tight")
'
When Python charts are generated, display them inline and keep the Mermaid version as a text fallback.
Data processing — tooling cascade (strict order):
jq for JSON, yq for YAML. Pipe to head, tail, cut, sed, grep, sort, uniq, wc for shaping.uv first:
command -v uv &>/dev/null && uv run --with pandas,matplotlib python3 -c '...'
uv is unavailable. Use json, csv, collections, statistics modules — no pip dependencies:
command -v python3 &>/dev/null && python3 -c 'import json, sys; ...'
Never assume any runtime is available — always check with command -v before use. If all programmatic tools are unavailable, analyze manually with the Read tool and present results as markdown with Mermaid diagrams.
Version detection commands (pip show, go list -m, cargo pkgid, etc.) are exempt — they query package managers directly and are tried as-available per the version detection priority in Step 1b.
This skill reads the .vulnetix/memory.yaml file in the repository root to surface prior vulnerability history for packages being searched. This file is shared with /vulnetix:fix and /vulnetix:exploits.
At the start of every invocation:
.vulnetix/memory.yaml exists in the repo root.vulnetix/scans/*.cdx.json — if CycloneDX SBOMs exist from prior scans (pre-commit hook or fix skill), cross-reference package names against SBOM component lists for additional vulnerability contextDuring risk assessment (Step 4):
CVE-2021-44228 — Fixed (2024-01-15), CVE-2023-1234 — Risk accepted (2024-03-01), P3 (52.0)pocs exist for a vuln, note: N PoC(s) on file — do not display PoC URLs or paths in package search outputaffected or under_investigation), flag this prominently in the risk assessment. If CWSS priority is P1 or P2, add a warning: "Active exploit intelligence available — run /vulnetix:exploits <vuln-id> for details"After completing the search:
vulnerabilityCount or maxSeverity in the API response) that are NOT already tracked in the memory file, record them as new entries with:
status: under_investigationdiscovery.source: scandiscovery.sbom: path to the relevant .vulnetix/scans/*.cdx.json if one exists for this package's manifestdecision.choice: investigatingdecision.reason: "Discovered via /vulnetix:package-search"history: event: discovered, detail: "Found via package search for <query>"VEX-to-developer-language: When surfacing prior decisions, use developer-friendly language:
not_affected → "Not affected", affected → "Vulnerable", fixed → "Fixed", under_investigation → "Investigating"When gh CLI is available (check with gh auth status 2>/dev/null), query Dependabot alerts for packages in the search results to enrich the risk assessment.
During Step 4 (Risk Assessment):
gh api repos/{owner}/{repo}/dependabot/alerts?state=open --jq '[.[] | select(.dependency.package.name == "'"$PACKAGE_NAME"'")] | length'
"Dependabot PR #N open for <package> upgrade"dependabot section, surface it in the Known History output:
CVE-2021-44228 — Fixed (2024-01-15, Dependabot: merged PR #187)During Step 5 (Propose Dependency Addition):
"Dependabot PR #N already proposes this upgrade — consider merging it instead"This avoids duplicate work and leverages Dependabot's existing CI validation.
When gh CLI is available, check if CodeQL has flagged issues related to packages being searched. The canonical state-to-VEX mapping is defined in /vulnetix:fix.
During Step 4 (Risk Assessment):
gh api repos/{owner}/{repo}/code-scanning/alerts --jq '[.[] | select(.rule.tags[]? | test("CWE-<NUMBER>"; "i"))] | length'
code_scanning section, surface it in Known History:
CVE-2021-44228 — Fixed (2024-01-15, CodeQL: alert #15 fixed)During Step 5 (Propose Dependency Addition):
When gh CLI is available, check for secret scanning alerts relevant to packages handling authentication or credentials.
During Step 4 (Risk Assessment):
jsonwebtoken, bcrypt, passport, oauth2, crypto, keyring), check for open secret scanning alerts:
gh api repos/{owner}/{repo}/secret-scanning/alerts?state=open --jq 'length'
secret_scanning section, surface it in Known HistoryCheck cached manifest data first: If .vulnetix/memory.yaml has a manifests section, use it to identify previously detected ecosystems and their scan dates. This avoids re-globbing for manifests that are already tracked. If the manifests section exists and is recent (< 24h), use the cached ecosystem list as a starting point.
Then verify with Glob to catch any new manifest files:
package.json, package-lock.json, yarn.lock, pnpm-lock.yaml → npmgo.mod, go.sum → goCargo.toml, Cargo.lock → cargorequirements.txt, pyproject.toml, Pipfile, poetry.lock, uv.lock → pypiGemfile, Gemfile.lock → rubygemspom.xml, build.gradle, gradle.lockfile → mavencomposer.json, composer.lock → packagistDetermine which ecosystems this repository uses. If new manifest files are discovered that aren't in the manifests section of .vulnetix/memory.yaml, add them with ecosystem, path, and scan_source: package-search (without sbom_generated: true since this skill doesn't generate SBOMs).
For the package being searched, determine if it is already installed and what version is in use. You MUST resolve the current version using one of these methods (in priority order) and always disclose the source in your output:
package-lock.json or yarn.lock or pnpm-lock.yaml for the resolved versionpoetry.lock, Pipfile.lock, or uv.lockgo.sum for the recorded versionCargo.lock for the resolved versionGemfile.lockgradle.lockfile if presentcomposer.lockpackage.json → dependencies / devDependenciesrequirements.txt (pkg==1.2.3), pyproject.tomlgo.mod (require pkg v1.2.3)Cargo.toml [dependencies]Gemfilepom.xml <version>, build.gradlecomposer.jsonnode_modules/<package>/package.json → version fieldpip show <package> or python -c "import <pkg>; print(<pkg>.__version__)"go list -m <package>cargo pkgid <package>gem list <package> --local<binary> --version or which <binary>If the package is not currently installed (not found in any of the above), explicitly state: "Not currently installed — no existing version detected."
Version Source Label: In all outputs, tag the version with its source, e.g.:
1.2.3 (from lockfile: package-lock.json)^1.2.0 (from manifest: package.json — constraint, not exact)1.2.3 (from node_modules)1.2.3 (user-supplied)Not installedRun the Vulnetix VDB package search command:
vulnetix vdb packages search "$ARGUMENTS" -o json
If you detected a single ecosystem, add the --ecosystem <ecosystem> flag to filter results.
For example:
vulnetix vdb packages search "express" --ecosystem npm -o json
The output is JSON with this structure:
{
"packages": [
{
"name": "express",
"ecosystem": "npm",
"description": "Fast, unopinionated, minimalist web framework",
"latestVersion": "4.18.2",
"vulnerabilityCount": 3,
"maxSeverity": "high",
"safeHarbourScore": 85,
"repository": "https://github.com/expressjs/express"
}
]
}
Version enrichment: After receiving results, enrich each package with the current version detected in Step 1b. The API returns latestVersion — you must pair this with the currentVersion you resolved from the filesystem.
Discard packages from ecosystems not present in the repository. For example, if the repo only has package.json, filter out PyPI and Cargo results.
Present the matching packages in a comparison table with these columns:
| Package | Ecosystem | Current Version | Latest Version | Vulnerabilities | Max Severity | Safe Harbour | Confidence | Repository |
|---|---|---|---|---|---|---|---|---|
| express | npm | 4.17.1 (lockfile) | 4.18.2 | 3 | high | 0.85 | High | [link] |
Column definitions:
4.17.1 (lockfile), ^4.17.0 (manifest), Not installed). This is resolved from Step 1b.85 → display 0.85). This represents a safety confidence percentage where 1.0 = 100% confidence in safety.Below the table, always include a Version Context summary:
Version Context:
- express: 4.17.1 → 4.18.2 (patch upgrade available) — source: package-lock.json
- lodash: Not installed — no existing version detected
This gives the user full transparency on where version information was derived and what upgrade path exists.
For the best candidate (lowest vuln count, highest Safe Harbour value):
currentVersion → latestVersion. If new, show (new) latestVersion.dependencies in package.jsonrequirements.txt or pyproject.tomlgo get command with version[dependencies] in Cargo.toml<dependency> XML with versionGemfile with versioncomposer require command with versionUse the Edit tool to show the proposed change, but DO NOT apply it yet.
Example for npm (new dependency):
{
"dependencies": {
+ "express": "^4.18.2",
"other-package": "1.0.0"
}
}
Example for npm (upgrade):
{
"dependencies": {
- "express": "^4.17.1",
+ "express": "^4.18.2",
"other-package": "1.0.0"
}
}
Always include the specific version in the proposed edit — never use * or latest.
Ask the user:
npm install, pip install, etc.)/vulnetix:exploits <vuln-id> for any critical/high severity vulnerabilities found)If the user requests alternatives, repeat steps 2-6 with the suggested names.
vulnetix vdb packages search fails, inform the user to check vulnetix vdb status