@gpzhang2001/sharpkit-skills 0.2.1

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 (84) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +12 -0
  3. package/THIRD_PARTY_NOTICES.md +48 -0
  4. package/lib/index.d.ts +2027 -0
  5. package/lib/index.d.ts.map +1 -0
  6. package/lib/index.js +70 -0
  7. package/lib/index.js.map +1 -0
  8. package/package.json +46 -0
  9. package/skills/analysis/counterevidence.md +185 -0
  10. package/skills/analysis/fix_verification.md +129 -0
  11. package/skills/analysis/severity_calibration.md +130 -0
  12. package/skills/analysis/source_aware_discovery.md +211 -0
  13. package/skills/cloud/aws.md +231 -0
  14. package/skills/cloud/azure.md +262 -0
  15. package/skills/cloud/gcp.md +194 -0
  16. package/skills/cloud/kubernetes.md +223 -0
  17. package/skills/coordination/root_agent.md +105 -0
  18. package/skills/coordination/source_aware_whitebox.md +47 -0
  19. package/skills/custom/api_spec_testing.md +61 -0
  20. package/skills/custom/dependency_cve_scanning.md +341 -0
  21. package/skills/custom/npx_confusion.md +233 -0
  22. package/skills/custom/source_aware_sast.md +192 -0
  23. package/skills/frameworks/django.md +214 -0
  24. package/skills/frameworks/fastapi.md +191 -0
  25. package/skills/frameworks/nestjs.md +225 -0
  26. package/skills/frameworks/nextjs.md +228 -0
  27. package/skills/protocols/graphql.md +276 -0
  28. package/skills/protocols/oauth.md +185 -0
  29. package/skills/reconnaissance/asset_discovery.md +150 -0
  30. package/skills/reconnaissance/infrastructure_lifecycle.md +226 -0
  31. package/skills/scan_modes/deep.md +164 -0
  32. package/skills/scan_modes/diff.md +86 -0
  33. package/skills/scan_modes/quick.md +68 -0
  34. package/skills/scan_modes/standard.md +99 -0
  35. package/skills/technologies/active_directory.md +233 -0
  36. package/skills/technologies/auth0.md +188 -0
  37. package/skills/technologies/electron_desktop_apps.md +181 -0
  38. package/skills/technologies/firebase.md +263 -0
  39. package/skills/technologies/grafana_prometheus.md +189 -0
  40. package/skills/technologies/llm_applications.md +257 -0
  41. package/skills/technologies/supabase.md +268 -0
  42. package/skills/tooling/agent_browser.md +551 -0
  43. package/skills/tooling/ffuf.md +72 -0
  44. package/skills/tooling/httpx.md +82 -0
  45. package/skills/tooling/hurl.md +99 -0
  46. package/skills/tooling/hypothesis.md +100 -0
  47. package/skills/tooling/katana.md +102 -0
  48. package/skills/tooling/naabu.md +68 -0
  49. package/skills/tooling/nmap.md +66 -0
  50. package/skills/tooling/nuclei.md +67 -0
  51. package/skills/tooling/python.md +109 -0
  52. package/skills/tooling/semgrep.md +72 -0
  53. package/skills/tooling/sqlmap.md +67 -0
  54. package/skills/tooling/subfinder.md +66 -0
  55. package/skills/vulnerabilities/agentic_system_security.md +207 -0
  56. package/skills/vulnerabilities/argument_injection.md +157 -0
  57. package/skills/vulnerabilities/authentication_jwt.md +166 -0
  58. package/skills/vulnerabilities/broken_function_level_authorization.md +154 -0
  59. package/skills/vulnerabilities/browser_security.md +192 -0
  60. package/skills/vulnerabilities/business_logic.md +178 -0
  61. package/skills/vulnerabilities/csrf.md +198 -0
  62. package/skills/vulnerabilities/header_injection.md +216 -0
  63. package/skills/vulnerabilities/http_request_smuggling.md +255 -0
  64. package/skills/vulnerabilities/idor.md +217 -0
  65. package/skills/vulnerabilities/information_disclosure.md +187 -0
  66. package/skills/vulnerabilities/insecure_deserialization.md +210 -0
  67. package/skills/vulnerabilities/insecure_file_uploads.md +194 -0
  68. package/skills/vulnerabilities/llm_prompt_injection.md +187 -0
  69. package/skills/vulnerabilities/mass_assignment.md +153 -0
  70. package/skills/vulnerabilities/nosql_injection.md +288 -0
  71. package/skills/vulnerabilities/open_redirect.md +165 -0
  72. package/skills/vulnerabilities/path_traversal_lfi_rfi.md +218 -0
  73. package/skills/vulnerabilities/prototype_pollution.md +142 -0
  74. package/skills/vulnerabilities/race_conditions.md +181 -0
  75. package/skills/vulnerabilities/rce.md +250 -0
  76. package/skills/vulnerabilities/semantic_confusion.md +189 -0
  77. package/skills/vulnerabilities/sql_injection.md +190 -0
  78. package/skills/vulnerabilities/ssrf.md +186 -0
  79. package/skills/vulnerabilities/ssti.md +270 -0
  80. package/skills/vulnerabilities/subdomain_takeover.md +167 -0
  81. package/skills/vulnerabilities/weak_password_detection.md +200 -0
  82. package/skills/vulnerabilities/xss.md +206 -0
  83. package/skills/vulnerabilities/xxe.md +223 -0
  84. package/src/index.ts +89 -0
@@ -0,0 +1,341 @@
1
+ ---
2
+ name: dependency-cve-scanning
3
+ description: Supply-chain / SCA playbook — scan repository lockfiles for known dependency CVEs and report them with create_dependency_report (no dynamic PoC required)
4
+ ---
5
+
6
+ # Dependency / Supply-Chain CVE Scanning (SCA)
7
+
8
+ Use this skill on white-box / repository scans to make sure a repository pinning a
9
+ **known-vulnerable dependency** is actually reported as a finding, instead of being
10
+ discovered and then silently dropped because it cannot be dynamically exploited.
11
+
12
+ Known-CVE dependency findings are a first-class deliverable. Report each one with
13
+ the dedicated `create_dependency_report` tool.
14
+
15
+ ## Why this skill exists
16
+
17
+ A vulnerable dependency pinned in a lockfile (e.g. `lodash@4.17.4` with a known
18
+ prototype-pollution CVE) usually cannot be dynamically PoC'd from the outside —
19
+ the vulnerable code path may not even be reachable from a running endpoint. The
20
+ normal "no report without a dynamic PoC" rule would suppress it. For these
21
+ findings the proof is the **lockfile entry + scanner output + published
22
+ advisory**, not an exploit script. This is the one explicit exception to the
23
+ dynamic-validation rule, and it exists only for `create_dependency_report`.
24
+
25
+ ## Scan procedure
26
+
27
+ Run from the repo root and store output in the shared artifact directory used by
28
+ the source-aware pass:
29
+
30
+ ```bash
31
+ ART=/workspace/.source-aware
32
+ mkdir -p "$ART"
33
+
34
+ # Record the vuln DB age so a stale DB is a visible signal, not a silent clean scan.
35
+ trivy version --format json 2>/dev/null | tee "$ART/trivy-version.json"
36
+ # inspect .VulnerabilityDB.UpdatedAt / NextUpdate
37
+
38
+ # Lockfile/manifest -> known-CVE matching. Try a best-effort DB refresh first so a
39
+ # sandbox with egress gets the freshest CVEs; if the update fails, fall back to the
40
+ # cached DB instead of failing the scan. --offline-scan keeps per-package advisory
41
+ # lookups offline.
42
+ # --list-all-pkgs includes the package graph (Relationship + DependsOn) needed
43
+ # to attribute transitive CVEs to the direct dependency that introduces them.
44
+ trivy fs --scanners vuln --timeout 30m --offline-scan --list-all-pkgs \
45
+ --format json --output "$ART/trivy-sca.json" . \
46
+ || trivy fs --scanners vuln --timeout 30m --offline-scan --skip-db-update --list-all-pkgs \
47
+ --format json --output "$ART/trivy-sca.json" . \
48
+ || true
49
+ ```
50
+
51
+ If `.VulnerabilityDB.UpdatedAt` is more than a few weeks old (the sandbox had no
52
+ egress to refresh it), treat it as a scan limitation and note it in the
53
+ `assumptions` of dependency findings — a stale DB that still returns *some* results
54
+ will not trip the "zero results is suspicious" heuristic, so its age is the only
55
+ staleness signal.
56
+
57
+ Trivy reads the lockfiles/manifests it finds, including:
58
+ `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `poetry.lock`,
59
+ `requirements.txt`, `Pipfile.lock`, `go.mod`/`go.sum`, `Gemfile.lock`,
60
+ `pom.xml`/`gradle.lockfile`, `Cargo.lock`, `composer.lock`, etc.
61
+
62
+ If trivy returns zero vulnerabilities on a repo with dependencies, treat it as
63
+ suspicious: confirm the vuln DB is present (`trivy-version.json`) and that
64
+ lockfiles exist.
65
+
66
+ ## Interpreting results
67
+
68
+ For each entry under `.Results[].Vulnerabilities[]` in `trivy-sca.json`, collect:
69
+
70
+ - `VulnerabilityID` — the CVE (or GHSA; prefer the CVE if both are present)
71
+ - `PkgName` and `InstalledVersion` — the affected package + pinned version
72
+ - `FixedVersion` — the version that resolves it
73
+ - `Target` — the lockfile path it came from
74
+ - `.Results[].Type` (e.g. `npm`, `pip`, `gomod`, `pom`, `gemspec`, `cargo`) — the
75
+ package ecosystem; normalize to the registry name lowercased (`npm`, `pypi`,
76
+ `go`, `maven`, `rubygems`, `cargo`, `composer`, `nuget`, ...)
77
+ - `CVSS` — the published advisory base score
78
+ - `PrimaryURL` / references — to verify the advisory
79
+
80
+ Deduplicate by `(CVE, PkgName, Target)` — the same CVE/package observed in two
81
+ different manifests (e.g. two workspaces of a monorepo) is two findings, one
82
+ per manifest. File one `create_dependency_report` per CVE — do not batch
83
+ multiple CVEs into one report.
84
+
85
+ ### Attribute transitive CVEs to the direct dependency
86
+
87
+ With `--list-all-pkgs`, each `.Results[].Packages[]` entry carries `ID`
88
+ (`name@version`), `Relationship` (`direct` / `indirect`) and `DependsOn` (the
89
+ `ID`s it resolves to). For every vulnerable package that is **indirect**, walk
90
+ the `DependsOn` graph backwards to find the `direct` package(s) whose closure
91
+ contains it, then pass to `create_dependency_report`:
92
+
93
+ - `introduced_by` — the direct dependency as `name@version` (e.g.
94
+ `express@4.18.1`). If several direct dependencies pull it in, pick the
95
+ primary one and name the rest in `technical_analysis`.
96
+ - `dependency_path` — the shortest resolution chain from that direct
97
+ dependency to the vulnerable package, joined with ` > ` (e.g.
98
+ `express@4.18.1 > body-parser@1.20.0 > qs@6.10.2`).
99
+ - Omit both when the vulnerable package is itself a direct dependency.
100
+
101
+ If the ecosystem's lockfile gives trivy no graph (`DependsOn` absent), derive
102
+ the chain from the package manager instead (`npm ls <pkg>`, `pnpm why <pkg>`,
103
+ `yarn why <pkg>`, `pipdeptree --reverse -p <pkg>`, `go mod graph`,
104
+ `mvn dependency:tree`, ...) — and if that also fails, leave the fields out
105
+ rather than guessing.
106
+
107
+ For transitive findings, `remediation_steps` must be actionable at the
108
+ **direct-dependency level**: upgrading the vulnerable package directly is
109
+ usually impossible from the app's own manifest. Say which direct dependency to
110
+ bump (a version whose closure resolves the fixed version), or how to force the
111
+ resolution (npm `overrides` / yarn `resolutions` / pnpm `pnpm.overrides` /
112
+ Maven `dependencyManagement` / Gradle resolution strategy / `go mod edit`),
113
+ not just "upgrade <vulnerable pkg> to <fixed>".
114
+
115
+ ### Usage / reachability analysis (required for every dependency CVE)
116
+
117
+ For every CVE you are about to report, run a static usage analysis and record
118
+ the result in the structured `reachability` + `reachability_evidence` fields.
119
+ The level is an **evidence ladder, never an exploitability verdict** — claim
120
+ only what you proved, and cite the proof. It never changes severity (that is
121
+ `advisory_cvss` alone); it exists so the reader can prioritize.
122
+
123
+ **Go — use govulncheck (real call-graph analysis):**
124
+
125
+ ```bash
126
+ # Symbol-level: reports only vulnerabilities whose vulnerable functions are
127
+ # actually reachable from application code. Needs the Go toolchain + module
128
+ # deps; if either is missing, fall back to the checks below rather than
129
+ # claiming a level.
130
+ if command -v govulncheck >/dev/null && go version >/dev/null 2>&1; then
131
+ govulncheck -format json ./... > "$ART/govulncheck.json" || true
132
+ fi
133
+ ```
134
+
135
+ - A finding with a call stack ⇒ `reachability=reachable_call_path`, put the
136
+ call-path excerpt (entrypoint → vulnerable function) in
137
+ `reachability_evidence`.
138
+ - Listed as affecting a required module but with no reachable symbol ⇒ fall
139
+ back to the import/symbol checks below (`imported` / `not_imported`).
140
+
141
+ **All other ecosystems — import check, then symbol match:**
142
+
143
+ 1. **Import check.** Search application code (exclude lockfiles, vendored
144
+ deps, `node_modules`, build output) for imports of the vulnerable package:
145
+ `ast-grep`/`rg` for `import`/`require`/`from X import` of the package (and
146
+ its ecosystem import name, which may differ from the registry name, e.g.
147
+ `PyYAML` → `yaml`). No hits ⇒ `not_imported`, with the search scope stated
148
+ in `reachability_evidence`. For a **transitive** dependency, the check is
149
+ whether application code imports it directly; if not, it is reachable only
150
+ through the direct dependency — check whether the direct dep's usage can
151
+ hit it (if unclear, use `imported` when the direct dep is used at all).
152
+ 2. **Symbol match — per CVE, not per package.** Read each CVE's own advisory
153
+ (GHSA/NVD/OSV `affected[].ecosystem_specific.imports` or the advisory
154
+ text) for the affected functions/classes/APIs. Search application code for
155
+ those symbols (`ast-grep` pattern or `rg -n`). Hits ⇒
156
+ `vulnerable_symbol_used`, with repo-relative `file:line` of each hit (up
157
+ to a handful) in `reachability_evidence`. Imported but no affected-symbol
158
+ usage found (or the advisory names no symbols) ⇒ `imported`.
159
+ Different CVEs on the same package usually affect **different** symbols
160
+ (one hits a parser, another a header check) — never copy one CVE's
161
+ verdict/evidence onto its siblings; run the symbol search against each
162
+ CVE's own affected-symbol list. The import check (step 1) is the only
163
+ part shared across a package's CVEs.
164
+ 3. **Source-to-sink trace — do this whenever step 2 found a symbol hit.** A
165
+ symbol hit alone says the code calls the vulnerable API; it does not say
166
+ who can reach it. Start at the sink (the exact line that calls the
167
+ vulnerable function) and walk backwards hop by hop to the source: the
168
+ entry point that carries untrusted input (HTTP route, CLI argument, queue
169
+ or webhook payload, uploaded file, config value). Read each intermediate
170
+ function; when a hop is a thin wrapper, go one step deeper — never stop at
171
+ the first caller. Record what each hop enforces: authentication, a role
172
+ check, validation, a feature flag, a size or type limit, a default that is
173
+ off in production.
174
+ Write the chain into `reachability_evidence` as
175
+ `entry point -> intermediate call -> package call` with a
176
+ repository-relative `file:line` for every hop, and say who controls the
177
+ input. If no source reaches the sink, say that too — the level stays
178
+ `vulnerable_symbol_used` (the call is real), and the trace is what tells
179
+ the reader it is only reachable from, say, an operator CLI.
180
+ 4. If the analysis was not performed or is inconclusive (obfuscated code,
181
+ dynamic loading, unparsable sources) ⇒ `unknown` and say why in
182
+ `assumptions`.
183
+
184
+ Cheap-first budgeting: the import check is one search per package — always do
185
+ it. Do the per-CVE symbol match for every CVE whose advisory names affected
186
+ symbols (they can be batched into one multi-pattern search per package);
187
+ prioritize `critical`/`high`/KEV when the budget is tight; a CVE whose symbol
188
+ search was skipped may still be reported as `imported` (the import check is
189
+ real evidence), but its `reachability_evidence` must state that the
190
+ affected-symbol check was not performed, so a skipped search is never
191
+ mistaken for a completed one with no hits. Never let this analysis stall
192
+ reporting — `unknown` with a reason beats an unverified claim.
193
+
194
+ Anti-overclaim rules:
195
+
196
+ - `not_imported` still does NOT mean safe (dynamic `import()`/reflection/
197
+ framework wiring evade static search) — never phrase it as "not exploitable".
198
+ - `reachable_call_path` is reserved for call-graph tools (govulncheck); a
199
+ symbol grep hit is `vulnerable_symbol_used`, no matter how convinced you are.
200
+ - The tool rejects any level other than `unknown` without
201
+ `reachability_evidence`.
202
+
203
+ ### Reachability is a confidence modifier, not a gate
204
+
205
+ Do NOT suppress or downgrade a known CVE just because you could not prove the
206
+ vulnerable code path is reachable. Report it, set `advisory_cvss` from the
207
+ advisory, record the usage analysis in `reachability`/`reachability_evidence`,
208
+ and use `assumptions` for anything softer. If you *can* actually trigger the
209
+ vulnerable path or chain it into a dynamic exploit, additionally report that
210
+ as a normal dynamic finding with `create_vulnerability_report` (the standalone
211
+ CVE stays in its own `create_dependency_report`).
212
+
213
+ ## Reporting
214
+
215
+ Report each confirmed known CVE with the dedicated `create_dependency_report`
216
+ tool (NOT `create_vulnerability_report` — that tool is for dynamically validated
217
+ findings and rejects empty PoC fields):
218
+
219
+ - Set `cve` to the verified `CVE-YYYY-NNNNN` id (required). If you only have a
220
+ GHSA, look up the mapped CVE; if there is genuinely no CVE, do not report it
221
+ with this tool.
222
+ - There are no PoC fields — `create_dependency_report` does not take
223
+ `poc_description` / `poc_script_code` / `code_locations`. The proof lives in
224
+ `description` and `technical_analysis` (scanner output + advisory).
225
+ - **Always fill the structured dependency fields** (they power the dedicated
226
+ dependency-report card; do not leave them only in free-text):
227
+ - `package_name` — `PkgName` (required).
228
+ - `installed_version` — `InstalledVersion` (required).
229
+ - `package_ecosystem` — normalized ecosystem from `.Results[].Type` (lowercased,
230
+ e.g. `npm`, `pypi`, `go`, `maven`, `rubygems`, `cargo`) (required).
231
+ - `fixed_version` — `FixedVersion` (leave empty only if no fix is published).
232
+ - `manifest_path` — the repo-relative `Target` lockfile/manifest path
233
+ (required). Strip any scan-workspace or repo checkout directory prefix so
234
+ the path is relative to the repository root (e.g. `package-lock.json`,
235
+ `services/api/pom.xml`); the tool rejects absolute paths and `..` segments.
236
+ This binds the finding to the exact file so remediation can target the
237
+ right repository.
238
+ - Reference the repo-relative `Target` lockfile path in `description` /
239
+ `technical_analysis` (no leading slash) so the finding is traceable.
240
+ - Put the concrete proof in `description` / `technical_analysis`: package name,
241
+ installed/affected version, fixed version, lockfile path, and the relevant
242
+ trivy output excerpt.
243
+ - **Always set `advisory_cvss` to the published advisory base score (0.0–10.0).**
244
+ It is the published reference, and it rates the finding whenever you give no
245
+ contextual breakdown: read it off the advisory (`CVSS` in trivy output, or the
246
+ NVD/GHSA page) and pass the real value. The tool rejects a call that omits it,
247
+ because guessing a score both inflates low CVEs and deflates critical ones.
248
+ - Set `cwe` to the most specific `CWE-NNN` when the advisory names one.
249
+ - Do NOT cap severity at LOW just because there is no dynamic reproduction — use
250
+ the advisory score.
251
+ - Set `reachability` + `reachability_evidence` from the usage analysis above —
252
+ the tool rejects a report with no evidence, so for `unknown` write what you
253
+ searched and why the result is inconclusive;
254
+ use `assumptions` for anything softer (confidence, caveats, analysis limits).
255
+ - **Always set `contextual_cvss_breakdown` + `contextual_cvss_reasoning`.** Every
256
+ dependency finding carries a contextual rating of the CVE in this codebase
257
+ (see below). Start from the published metrics and change only what your
258
+ evidence proves.
259
+ - Set every other field the report accepts when the information exists:
260
+ `package`, `ecosystem`, `installed_version`, `fixed_version`, `manifest_path`,
261
+ `introduced_by` for a transitive package, `dependency_path`, `cwe`,
262
+ `assumptions`, and the remediation instruction. A blank field costs the reader
263
+ a triage step.
264
+
265
+ ### Contextual CVSS
266
+
267
+ The published score rates the CVE in the abstract. `contextual_cvss_breakdown`
268
+ rates it **here**, in this codebase, and every dependency report must carry
269
+ one. It is the same 8-metric CVSS v3.1 object as a
270
+ normal finding's `cvss_breakdown` (`attack_vector`, `attack_complexity`,
271
+ `privileges_required`, `user_interaction`, `scope`, `confidentiality`,
272
+ `integrity`, `availability`). You never pass a score: the contextual score and
273
+ vector are computed from the breakdown, and when you provide one it determines
274
+ the finding's severity. `advisory_cvss` stays the published reference.
275
+
276
+ Start from the advisory's own published metrics and change only what your
277
+ evidence proves is different in this codebase:
278
+
279
+ - `attack_vector` `N`/`A`/`L`/`P` — as deployed. A library reached only by a
280
+ local CLI is `L`, not `N`.
281
+ - `attack_complexity` `L`/`H` — raise to `H` when the vulnerable path needs a
282
+ precondition the code enforces (input validation, a non-default flag, an
283
+ internal-only route).
284
+ - `privileges_required` `N`/`L`/`H`, `user_interaction` `N`/`R` — what this
285
+ deployment requires before the path is reachable.
286
+ - `scope` `U`/`C` — whether exploitation here escapes the component boundary.
287
+ - `confidentiality`/`integrity`/`availability` `N`/`L`/`H` — the impact in this
288
+ codebase. `not_imported` code the build still ships is usually `N` across all
289
+ three.
290
+
291
+ Ground every metric in the **source-to-sink trace** from the usage analysis
292
+ (step 3 above), not in a general impression of the package. Derive the metrics
293
+ from that chain: `attack_vector`, `privileges_required`, and `user_interaction`
294
+ come from what the source requires; `attack_complexity` comes from the
295
+ preconditions the hops enforce; `confidentiality`, `integrity`, and
296
+ `availability` come from the data and privileges available at the sink.
297
+
298
+ When you have no source-to-sink trace, still rate the finding: copy the
299
+ published metrics, change only the metrics the usage level itself proves, and
300
+ say so in the reasoning. For example, for a `not_imported` package that the
301
+ build still ships, keep the published metrics and lower `confidentiality`,
302
+ `integrity`, and `availability` to `N`, because no code path reaches the
303
+ vulnerable symbol. Never invent a hop you did not read.
304
+
305
+ `contextual_cvss_reasoning` is required with the breakdown. Write two to four
306
+ sentences that another engineer can check without opening the repository. Name
307
+ the chain hop by hop as `entry point -> intermediate call -> package call`, with
308
+ a repository-relative `file:line` for each hop, say who controls the input, and
309
+ say what the contextual rating changes. Example: lowering `attack_vector` to
310
+ `L` and `confidentiality` to `L` with "The only caller of `yaml.load` is
311
+ `parse_manifest` in `scripts/import.py:88`, which `cli/commands.py:212` invokes
312
+ for an operator-supplied path behind the `--allow-unsafe-import` flag that
313
+ `deploy/prod.yaml` never sets. No HTTP route reaches that function, so an
314
+ attacker must already hold shell access on the job host, and the parsed data is
315
+ build metadata rather than customer records."
316
+
317
+ When the published rating already fits this codebase, repeat the published
318
+ metrics in the breakdown and say in the reasoning that the deployment matches
319
+ the advisory. A contextual rating is a claim you must be able to defend, and it
320
+ never replaces `advisory_cvss` as the published reference.
321
+
322
+ Verify the CVE with `web_search` when available before reporting. Never guess or
323
+ hallucinate a CVE id.
324
+
325
+ ## Anti-patterns
326
+
327
+ - Do not report a dependency CVE with `create_vulnerability_report`; use
328
+ `create_dependency_report`.
329
+ - Do not report a finding without a verified CVE id.
330
+ - Do not batch multiple CVEs into one report.
331
+ - Do not omit `advisory_cvss` — the tool rejects it, and it rates every finding
332
+ that carries no contextual breakdown.
333
+ - Do not silently drop a known CVE because it lacks a dynamic PoC — that is the
334
+ exact failure this skill prevents.
335
+ - Do not downgrade advisory severity for lack of dynamic reproduction.
336
+ - Do not claim a `reachability` level the evidence does not prove — `unknown`
337
+ with a reason is always acceptable; an overclaimed level never is.
338
+ - Do not send a report without `contextual_cvss_breakdown` and
339
+ `contextual_cvss_reasoning` — the reader rates and ranks the finding with them.
340
+ - Do not use the contextual breakdown to quietly de-rate a CVE you could not
341
+ analyze. State the limit of the analysis in the reasoning instead.
@@ -0,0 +1,233 @@
1
+ ---
2
+ name: npx-confusion
3
+ description: Test package and executable identity confusion in npx, npm exec, and bunx fallback, plus explicit auto-fetch runners such as pnpm/yarn dlx and deno run npm:, with runner-specific resolution analysis, registry-state controls, reporting gates, and false-positive elimination
4
+ ---
5
+
6
+ # npx Confusion
7
+
8
+ Use this skill when a package runner may execute code from a package other than the publisher or package the workflow intended. For `npx`, `npm exec`, and `bunx`, the recurring case is a missing local executable being reinterpreted as a remotely fetched package spec. Explicit auto-fetch runners such as `pnpm dlx`, `yarn dlx`, and `deno run npm:` have different semantics; analyze them as an adjacent package-identity problem rather than pretending they share npm's fallback order.
9
+
10
+ Load `dependency_cve_scanning` for known vulnerable versions, `infrastructure_lifecycle` for abandoned domains or registry resources, `agentic_system_security` for the authority of an MCP/agent process, and `semantic_confusion` for the general lookup-order model.
11
+
12
+ ## Core Condition
13
+
14
+ Choose the branch that matches the runner.
15
+
16
+ For local-first fallback (`npx`, `npm exec`, or `bunx`), require all of the following:
17
+
18
+ 1. A target-controlled workflow invokes a bare executable or ambiguous package token.
19
+ 2. The intended package and its executable name differ, or other evidence establishes the expected publisher/package.
20
+ 3. The executable is not resolved in the workflow's real local, workspace, global, or cache context as applicable to that runner.
21
+ 4. The runner consequently selects an unintended remote package spec from its configured registry.
22
+ 5. The affected workflow reaches that package's executable with security-relevant authority.
23
+
24
+ For explicit auto-fetch runners (`pnpm dlx`/`pnx`/`pnpx`, `yarn dlx`, or `deno run npm:`), do not require or claim a missing-local-binary fallback. Require evidence that the command names or infers a package different from the one the workflow intended, such as a scoped-package/bin mismatch, typo, generated configuration error, or wrong publisher. Then prove the exact fetched package, chosen binary/module, execution path, and inherited authority.
25
+
26
+ A public package merely being outside the target's ownership is not a vulnerability. Third-party packages are normal; the mismatch between intended executable provenance and actual registry resolution is the finding.
27
+
28
+ ## Resolution Model
29
+
30
+ Record the npm version because `npx` has used `npm exec` since npm 7 and resolver behavior changes between releases. For npm, model these decisions:
31
+
32
+ ```text
33
+ bare command
34
+ -> executable in ancestor node_modules/.bin?
35
+ -> executable in global bin?
36
+ -> matching local/global package and usable bin?
37
+ -> matching environment in the npx cache?
38
+ -> treat the command token as a package spec
39
+ -> fetch its manifest from the configured registry
40
+ -> infer one executable from package.json#bin
41
+ -> install into the npx cache and execute
42
+ ```
43
+
44
+ Also record:
45
+
46
+ - working directory and workspace root
47
+ - local dependency tree and generated `node_modules/.bin` links
48
+ - global prefix/bin directory and npx cache
49
+ - `registry`, scope-specific registry rules, proxy and authentication configuration
50
+ - command form, flags, package spec/version, TTY/CI state, and `yes` policy
51
+ - npm's executable-inference result when the package exposes zero, one, or several `bin` entries
52
+
53
+ Do not collapse package-name lookup and bin selection into one step. npm can fetch a manifest yet fail because it cannot infer exactly one executable.
54
+
55
+ ### Runner distinctions
56
+
57
+ Record the exact runner and version. Do not reuse npm's local/global/cache ordering for another implementation.
58
+
59
+ | Runner | Resolution behavior to model | Package binding / fetch control |
60
+ |---|---|---|
61
+ | `npx` / `npm exec` | Local/workspace/global/cache resolution followed by package-spec fallback; executable inference depends on `package.json#bin` | `--package <pkg>` binds the provider; `--no` rejects an install prompt |
62
+ | `bunx` | Checks a locally installed package, then can install from npm into Bun's cache | `--package <pkg>` binds the provider; `--no-install` forbids installation |
63
+ | `yarn dlx` | Downloads the command-named package into a temporary environment by default; this is not a local-bin fallback | `--package <pkg>` selects a different provider package |
64
+ | `pnpm dlx` / `pnx` / `pnpx` | Fetches and hotloads a registry package, then runs its default binary; project trust policies are version-dependent | `--package=<pkg>` selects the provider; prefer declared dependencies plus `pnpm exec` when remote fetch is unintended |
65
+ | `deno run npm:<pkg>` | Uses an explicit npm package spec and cache; a subpath can select a binary | Pin the package/subpath and model lock, cache, lifecycle-script, and Deno permission settings |
66
+
67
+ Treat mutable tags and ranges such as `latest`, `next`, `@2`, caret, and tilde ranges as selectors, not pins. A privileged repeatable workflow needs an exact reviewed version plus lockfile/integrity enforcement where the runner supports it.
68
+
69
+ ## High-Signal Patterns
70
+
71
+ ### Bare executable fallback
72
+
73
+ ```text
74
+ npx internal-tool
75
+ npx -y internal-tool
76
+ npm exec -- internal-tool
77
+ ```
78
+
79
+ The signal is strongest in CI, release scripts, bootstrap commands, developer setup, and tool/agent configuration where the same command is run repeatedly.
80
+
81
+ ### Scoped package versus unscoped bin
82
+
83
+ A scoped package can expose an unscoped executable:
84
+
85
+ ```json
86
+ {
87
+ "name": "@org/tooling",
88
+ "bin": { "org-tool": "./bin/run.js" }
89
+ }
90
+ ```
91
+
92
+ Inside a correctly installed workspace, `npx org-tool` may resolve `node_modules/.bin/org-tool`. Outside that tree, the same command can fall back to the public package named `org-tool`. Treat documentation, MCP configuration, and bootstrap scripts as separate execution contexts rather than assuming the repository-local result applies everywhere.
93
+
94
+ ### Agent and MCP launchers
95
+
96
+ Inspect `.mcp.json`, editor/desktop agent configuration, devcontainers, and generated tool launchers for `command: npx` plus `-y` and a bare package or binary name. Combine this resolver analysis with `agentic_system_security` to determine the credentials, tools, files, and network access inherited by that process.
97
+
98
+ ## Candidate Collection
99
+
100
+ Search executable surfaces and retain file, line, command, and execution context:
101
+
102
+ ```bash
103
+ rg -n --no-heading -g '!node_modules' -g '!**/dist/**' \
104
+ -e '\b(npx|npm\s+exec|bunx|pnx|pnpx|pnpm\s+dlx|yarn\s+dlx)\s+[^[:space:]]+' \
105
+ -e '\bdeno\s+run\b[^\n]*\bnpm:' \
106
+ -e '"command"\s*:\s*"(npx|bunx|pnx|pnpx|pnpm|yarn|deno)"' \
107
+ -e '"args"\s*:\s*\[[^]]*"(dlx|npm:[^"]+|-y)"' \
108
+ .
109
+ ```
110
+
111
+ Search the source/configuration tree rather than a fixed file list: these commands also live in
112
+ `scripts/`, husky/lint-staged hooks, `turbo.json`/`nx.json` task definitions,
113
+ `.circleci/`, composite-action `action.yml`, devcontainer `postCreateCommand`,
114
+ nested workspace `package.json` files, and editor/agent config under
115
+ `.cursor/`, `.vscode/`, and `.mcp.json`. If generated output is itself shipped or executed, search its specific directory separately instead of globally including every `dist/` artifact.
116
+
117
+ Also inspect:
118
+
119
+ - package scripts and lifecycle hooks
120
+ - workspace package `name` and `bin` maps
121
+ - READMEs and generated setup instructions
122
+ - CI composite actions and reusable workflows
123
+ - source maps or bundled package metadata that reveal internal commands
124
+
125
+ Discard paths, shell variables, flags, Node built-ins, and text that is not executed or presented as an executable command.
126
+
127
+ ## Establish the Actual Resolution
128
+
129
+ Prefer inspecting the existing dependency tree, lockfile, workspace packages, and `.bin` links. Do not run `npm ci` merely to decide whether a command is local: it changes the tree and can execute lifecycle scripts.
130
+
131
+ For a version-controlled reproduction environment, record npm's registry lookup without allowing a missing package to be installed:
132
+
133
+ ```bash
134
+ npx --no --loglevel=http <candidate>
135
+ ```
136
+
137
+ Interpret this carefully:
138
+
139
+ - a local executable may run immediately; `--no` only refuses missing-package installation
140
+ - an HTTP registry request shows fallback, not ownership or successful execution
141
+ - a cancellation naming the missing package shows npm's chosen package spec
142
+ - cache, global installs, parent directories, workspaces, and registry configuration can change the result
143
+
144
+ Repeat the resolution analysis in every context that matters: repository root, documented launch directory, CI checkout, generated agent configuration, and bootstrap-before-install flow. Do not substitute a clean empty directory for the target context except to understand npm's generic name mapping.
145
+
146
+ Do not apply `npx --no` as a generic dry-run flag. Use `bunx --no-install` only for Bun's local-resolution question. `dlx` and `deno run npm:` already name a remotely resolvable package, so validate their package spec, registry, cache/lock, selected binary or subpath, and permissions using that runner's own behavior.
147
+
148
+ ## Ownership and Registry State
149
+
150
+ Query the exact registry selected by the target configuration, then distinguish:
151
+
152
+ - intended package owned by the expected publisher
153
+ - unrelated public package with the same name
154
+ - unregistered name (`404` from a functioning registry)
155
+ - private or access-controlled name (`401`/`403`)
156
+ - transient/rate-limited/blocked lookup (`429`, `5xx`, timeout)
157
+ - placeholder, reserved, disputed, or previously unpublished name
158
+
159
+ Before trusting any of those states, check whether the target's lookup path can distinguish a known existing package from a newly generated negative control. Resolve the registry from the same working directory and configuration used by the target:
160
+
161
+ ```bash
162
+ # Public npm example; use a known package from the actual registry when different.
163
+ task_registry="$(npm config get registry)"
164
+ npm view --registry="$task_registry" lodash name --json
165
+ npm view --registry="$task_registry" "$(openssl rand -hex 12)" name --json
166
+ ```
167
+
168
+ Run the pair through the same `.npmrc`, scope routing, authentication, proxy, and egress path as the candidate. Direct `curl` requests to the public registry are a separate observation unless the target runner uses that exact route. A successful pair establishes coarse positive/negative discrimination, not authenticity of every candidate response; verify that returned documents name the requested package and contain plausible registry metadata.
169
+
170
+ If the pair fails or returns indistinguishable responses, mark the target-path registry state `UNKNOWN`. An independently verified public-registry response may characterize public state, but it does not prove what the target runner resolves. Re-confirm candidate absence before relying on it.
171
+
172
+ A `404` proves absence from that registry at that time; it does not by itself prove that registration would be accepted. Registry similarity, trademark, reservation, security-hold, and unpublish rules remain separate facts. Two concrete cases to check rather than infer:
173
+
174
+ - A registry-owned security placeholder occupies the name even when its only version is `0.0.1-security`. Do not identify one from the version alone: inspect the packument, description, dist-tags, top-level and version-level maintainers, and version publisher such as `_npmUser`.
175
+ - npm rejects new unscoped names that collide with an existing package after `.`, `-`, and `_` are removed. Normalize both the candidate and existing names: looking up only the candidate's stripped form catches `some-tool` versus `sometool`, but misses the reverse direction when the existing package contains punctuation. Treat this as registry-policy eligibility evidence, not a guarantee that registration would otherwise succeed.
176
+
177
+ When a candidate name is already registered, distinguish the target's own
178
+ organization from an unrelated party before calling it a clash. Correlate `npm owner ls <name>`, version-level publisher metadata, known target-controlled npm organizations, and independently verified repository provenance. Repository/homepage fields are self-asserted supporting evidence and do not settle ownership alone. If publisher identity remains ambiguous, mark it `UNKNOWN`.
179
+
180
+ ## Validation and Impact
181
+
182
+ Demonstrate the complete resolver statement:
183
+
184
+ ```text
185
+ target-controlled invocation and context
186
+ -> intended executable absent
187
+ -> exact public package spec selected
188
+ -> package ownership/availability state
189
+ -> execution trigger and inherited authority
190
+ ```
191
+
192
+ Do not report an unregistered name without an execution path, or an execution path whose command is satisfied locally in every relevant context. Derive impact from the environment that executes the package: developer workstation, CI job, release pipeline, agent runtime, container build, or documentation-only workflow.
193
+
194
+ ## Reporting
195
+
196
+ There is no CVE and no vulnerable installed version here, so this does not go through `create_dependency_report`; that tool requires an advisory-matched CVE. Use `create_vulnerability_report` only after the applicable core condition is fully verified.
197
+
198
+ A registry lookup or `404` alone is candidate evidence, not a working PoC. The report must preserve the target invocation and execution context, show the exact selected package and binary/module, demonstrate the runner's execution transition in a representative controlled setup without publishing the contested name, and establish the authority inherited by that process. When source is available, include the responsible invocation/configuration and concrete fix in `code_locations`.
199
+
200
+ Do not file documentation/comment-only references, locally satisfied commands, unregisterable names, ambiguous ownership, or chains that stop before package execution. Retain them as investigation notes only when useful.
201
+
202
+ Derive CVSS from the demonstrated path rather than a fixed severity label. Account for required developer/user action, registry and configuration prerequisites, runner permissions, credential availability, and the confidentiality, integrity, and availability actually exposed. A CI, release, container-build, or agent context can be severe, but the context name alone does not establish High or Critical impact.
203
+
204
+ Deduplicate by root cause, affected asset/workflow, and remediation. Combine call sites when the same configuration mistake and fix apply; keep separate findings when the same candidate name affects different products, tenants, runner semantics, authority, or fixes.
205
+
206
+ ## False Positives
207
+
208
+ - The executable is provided by a declared dependency in every real execution context.
209
+ - `npx --package @scope/pkg <bin>` explicitly binds the executable to the intended package.
210
+ - A versioned package spec or scope-specific registry points to the intended publisher.
211
+ - The public package is the deliberately selected third-party tool.
212
+ - npm fetches the manifest but cannot infer or execute a bin.
213
+ - The reference appears only in generated/minified text with no executable call site.
214
+ - A registry/proxy error is misread as an unregistered name, or the target-path control pair is inconclusive.
215
+ - A package is absent but registry policy prevents the contested registration.
216
+ - The command resolves to the deliberately selected ecosystem tool and expected publisher.
217
+ - The already-registered name belongs to the target's own organization.
218
+ - An explicit `dlx` or `npm:` package spec is treated as missing-local fallback without evidence of a package/publisher mismatch.
219
+
220
+ ## Remediation
221
+
222
+ - Install the intended package and invoke its local executable through an npm script.
223
+ - For npm, bind and pin the provider: `npx --package @org/tool@<version> org-tool`; use `--no` when a missing dependency must fail.
224
+ - For Bun, use `bunx --package @org/tool@<version> org-tool` and `--no-install` when remote installation is not intended.
225
+ - Replace `yarn dlx`/`pnpm dlx` in repeatable or privileged workflows with a declared, locked dependency plus the runner's local `exec` command. When ephemeral execution is required, bind and pin the provider package explicitly.
226
+ - For Deno, pin the `npm:` package and binary subpath, retain a reviewed lockfile, use cache-only operation where appropriate, and grant only the permissions the command requires.
227
+ - Route private scopes to the intended registry and prevent public fallback.
228
+ - Pin package versions and lockfiles in privileged workflows.
229
+ - Replace bare `npx -y <name>` agent launchers with reviewed, publisher-qualified, version-pinned package specs.
230
+
231
+ ## Summary
232
+
233
+ Treat package-runner confusion as an identity and execution-context bug. Prove the runner-specific transition, distinguish binary names from package names, verify registry and publisher state without equating absence with eligibility, and report only a complete execution path under the affected workflow's actual authority.