Add npm risky_new_dependency metadata rule#779
Conversation
Flags dependencies newly introduced in a package version (relative to the previous published version) that are themselves high risk. The previous version is derived from the registry 'time' map; any dependency name present in the scanned version but absent from the previous one is scanned as a standalone package via a subprocess invocation of guarddog, so it is sandboxed identically to the parent. The sub-scan runs source-code rules only (all metadata rules excluded, which also excludes this rule, preventing recursion), and the parent is flagged when a new dependency scores at or above the configurable threshold (GUARDDOG_NEW_DEPENDENCY_RISK_THRESHOLD, default 7.0). Also extracts shared npm version-resolution helpers into guarddog/utils/npm.py (reused by the project scanner) and stops the human-readable reporter from emitting an empty location line for metadata findings.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 282b3c84c7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
With the sub-scan running source-code rules only, the 5.0-6.9 (suspicious) band was empty of false positives across 3000 popular npm packages, so the metadata noise that previously justified a 7.0 default is gone. Lower the default to 5.0 to catch genuinely suspicious newly-added dependencies.
…endency Addresses review feedback: - The new-dependency diff and sub-scan now resolve npm aliases to the real package (e.g. "x": "npm:evil@1" scans evil, not x). Because the diff is built from resolved package names, an alias retargeted to a different package under the same key is also detected. - optionalDependencies are now included alongside dependencies (npm installs them by default), closing an install-time coverage gap. devDependencies stay excluded (not installed for consumers). Alias handling (NPM_ALIAS_PATTERN / resolve_npm_alias) is lifted into guarddog/utils/npm.py and reused by the project scanner.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f499dfd32a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| package so the diff and sub-scan target the aliased package, not the local | ||
| alias name (e.g. "x": "npm:evil@1" -> {"evil": "1"}).""" | ||
| resolved: dict = {} | ||
| for section in ("dependencies", "optionalDependencies"): |
There was a problem hiding this comment.
Include installed peer dependencies in the diff
When a package version newly adds a non-optional peerDependencies entry such as "evil": "^1.0.0", this loop ignores it, so new_dependencies stays empty and the risky package is never sub-scanned. npm v7+ installs peer dependencies by default (npm docs), so consumers that do not already have the peer will still install that newly introduced package while this detector misses the install-time path.
Useful? React with 👍 / 👎.
| f"'{resolved_version or 'latest'}'" | ||
| ) | ||
|
|
||
| command = [ |
There was a problem hiding this comment.
Need to double check that's solid enough
There was a problem hiding this comment.
I checked and it should work with poetry run, pip, uv tool run and uvx
Adds an npm metadata rule,
risky_new_dependency, that flags a package whose scanned version introduced a dependency that is itself high risk.How it works
dependenciesplus atimemap. The detector takes the version published just before the scanned one and computes newly-added dependency names (a version bump of an existing dep is not "new").guarddog npm scan <dep>, taking the same CLI path; the parent's sandbox decision is propagated via theGUARDDOG_SUBSCAN_SANDBOXenv var. In-process scanning couldn't match the parent's sandbox (applied once, permanently).GUARDDOG_NEW_DEPENDENCY_RISK_THRESHOLD(default 5.0). It's a normal metadata rule, so it counts toward issues/exit codes and renders through all reporters.Evidence that it doesn't add noise
I scanned the top 1k packages on npm, plus a sample of 2k amongst the top 1-to-20k most popular packages (total 3k packages). In the current state:
@ark-ui/react@5.37.2dependency))Evidence that it would detect real threats
Axios: The payload was delivered through a new dependency
plain-crypto-js, which GuardDog flags with score >= 5 when scanning with source code rules only:Mastra: Backdoored a bunch of packages by adding the
easy-day-jsdependency. This rule would have caught it too, since it flags easy-day-js with a score >= 5:Sample output
Open questions / discussion