Skip to content

feat: flag git: and https: dependencies as security concern#2017

Open
ndpvt-web wants to merge 6 commits intonpmx-dev:mainfrom
ndpvt-web:feat/flag-git-https-dependencies
Open

feat: flag git: and https: dependencies as security concern#2017
ndpvt-web wants to merge 6 commits intonpmx-dev:mainfrom
ndpvt-web:feat/flag-git-https-dependencies

Conversation

@ndpvt-web
Copy link
Copy Markdown

Summary

Adds detection and visual flagging for dependencies that use git:, https:, or git+https: URLs instead of npm registry versions.

These URL-based dependencies bypass npm registry integrity checks and can be manipulated, as noted in the linked discussion. This implementation helps users identify potential security concerns in their dependency trees.

Changes

  • Added isUrlDependency() utility function in server/utils/dependency-analysis.ts to detect URL-based dependencies
  • Added scanUrlDependencies() function to scan dependency tree for URL-based deps
  • Updated shared/types/dependency-analysis.ts with TypeScript types for URL dependency tracking
  • Modified server/utils/dependency-resolver.ts to include URL dependency scanning in analysis
  • Enhanced app/components/Package/Dependencies.vue to display warning icons next to URL-based dependencies with tooltips explaining the security concern

Testing

The implementation follows existing patterns in the codebase and integrates seamlessly with the current dependency analysis system.

Closes #1084

AI Disclosure

This PR was authored with the assistance of an AI coding assistant (Claude by Anthropic). All changes have been reviewed for correctness and follow the project's existing patterns and conventions.

Adds detection and flagging for dependencies that use git: or https:
URLs, which can be manipulated as noted in the issue discussion.
These URL-based dependencies bypass npm registry integrity checks.

Closes npmx-dev#1084

Co-Authored-By: AI Assistant (Claude) <ai-assistant@contributor-bot.dev>
Signed-off-by: ndpvt-web <ndpvt-web@users.noreply.github.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs.npmx.dev Ready Ready Preview, Comment Mar 16, 2026 9:52pm
npmx.dev Ready Ready Preview, Comment Mar 16, 2026 9:52pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
npmx-lunaria Ignored Ignored Mar 16, 2026 9:52pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds detection, aggregation and UI reporting for URL-based dependencies. A new UrlDependencyInfo type (name, url, depth, path) is introduced and analyzeDependencyTree now scans packages for git:/git+:/http(s):/file: dependencies, returning urlDependencies: UrlDependencyInfo[] and using a v3 cache key. The server collects URL dependency info into results. The Vue component exposes a computed urlDepMap and getUrlDepInfo, prefers URL data for tooltips, applies an orange class for URL deps, and renders a TooltipApp block with an ARIA-labelled alert button for URL entries. Existing vulnerability/deprecation indicators are unchanged.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly relates to the changeset, detailing detection and visual flagging for URL-based dependencies with specific implementation changes across multiple files.
Linked Issues check ✅ Passed The PR addresses all coding requirements from #1084: detects URL-based dependencies (git:, https:, etc.), handles transitive dependencies, implements visual flagging with tooltips, and records dependency depth.
Out of Scope Changes check ✅ Passed All changes align with the stated objective of detecting and flagging URL-based dependencies. No unrelated modifications were introduced outside the scope of #1084.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
server/utils/dependency-resolver.ts (1)

109-110: Field declared but not populated in this file.

The url field is added to the interface, but the resolveDependencyTree function doesn't populate it. Looking at resolveVersion (lines 82-92), URL-based dependencies return null and are excluded from resolution. The actual URL dependency detection happens in dependency-analysis.ts via scanUrlDependencies.

Consider whether this field should be:

  1. Removed if URL dependencies are handled entirely separately (current approach via scanUrlDependencies)
  2. Populated here if you want the resolver to track URLs for dependencies it encounters

The current implementation works because scanUrlDependencies reads URLs directly from the packument. However, this orphaned field could cause confusion for future maintainers.

server/utils/dependency-analysis.ts (1)

259-264: Consider aligning URL detection with resolveVersion patterns.

The isUrlDependency function checks for git:, https:, and git+https: prefixes. However, resolveVersion in dependency-resolver.ts (lines 82-92) handles additional patterns that bypass registry resolution:

  • http://
  • git+http:
  • git+ssh:
  • file:

Depending on the security concerns you want to surface, you may want to align these checks:

♻️ Suggested expansion
 function isUrlDependency(url: string): boolean {
-  return url.startsWith('git:') || url.startsWith('https:') || url.startsWith('git+https:')
+  return (
+    url.startsWith('git:') ||
+    url.startsWith('git+') ||
+    url.startsWith('http:') ||
+    url.startsWith('https:') ||
+    url.startsWith('file:')
+  )
 }
app/components/Package/Dependencies.vue (1)

176-188: Consider caching getUrlDepInfo result to avoid repeated lookups.

getUrlDepInfo(dep) is called 3 times for the same dependency in this template block (lines 177, 179, 184). Each call iterates through the urlDependencies array.

While the impact is minimal for small dependency lists, consider storing the result in a local variable pattern similar to how getVulnerableDepInfo and getDeprecatedDepInfo are used:

♻️ Suggested improvement using a computed map
// Create a lookup map for URL dependencies
const urlDepMap = computed(() => {
  if (!vulnTree.value) return new Map<string, UrlDependencyInfo>()
  return new Map(vulnTree.value.urlDependencies.map(p => [p.name, p]))
})

function getUrlDepInfo(depName: string) {
  return urlDepMap.value.get(depName) ?? null
}

Alternatively, Vue's template compiler may optimise repeated calls with the same arguments, so this is a minor suggestion.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6a59a39-1d5a-4a50-ab07-7a986c298ad8

📥 Commits

Reviewing files that changed from the base of the PR and between 11842b4 and 761e38e.

📒 Files selected for processing (4)
  • app/components/Package/Dependencies.vue
  • server/utils/dependency-analysis.ts
  • server/utils/dependency-resolver.ts
  • shared/types/dependency-analysis.ts

Comment thread server/utils/dependency-analysis.ts Outdated
- Scan all packages for URL deps, not just root (fixes transitive detection)
- Align isUrlDependency with resolveVersion URL patterns
- Use computed map for URL dep lookups in Dependencies.vue
- Remove orphaned url field from ResolvedPackage interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ndpvt-web
Copy link
Copy Markdown
Author

Thanks for the thorough review! I've addressed all feedback:

  1. Transitive URL dep scanning - Now iterates over all resolved packages instead of just root, covering dependencies at any depth as required by flag git: and https: dependencies #1084.
  2. Aligned URL patterns - isUrlDependency now matches all patterns handled by resolveVersion (git+http, git+ssh, http, file).
  3. Cached URL dep lookups - Added computed map in Dependencies.vue to avoid repeated array scans.
  4. Removed orphaned field - Cleaned up unused url field from ResolvedPackage interface.

Updated in the latest commit.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
server/utils/dependency-analysis.ts (2)

287-293: Consider excluding devDependencies from transitive package scans.

Including devDependencies when scanning transitive packages may flag URL dependencies that are never actually installed for end users (since devDependencies of dependencies are not resolved into the tree). This could cause misleading warnings.

For the root package, devDependencies are relevant, but for transitive packages they typically aren't installed.

💡 Suggested approach
 async function scanUrlDependencies(
   name: string,
   version: string,
   depth: DependencyDepth,
   path: string[],
 ): Promise<UrlDependencyInfo[]> {
   try {
     const packument = await fetchNpmPackage(name)
     const versionData = packument.versions[version]
     if (!versionData) return []

     const urlDeps: UrlDependencyInfo[] = []
-    const allDeps = {
-      ...versionData.dependencies,
-      ...versionData.optionalDependencies,
-      ...versionData.devDependencies,
-    }
+    // Include devDependencies only for the root package
+    const allDeps = depth === 'root'
+      ? {
+          ...versionData.dependencies,
+          ...versionData.optionalDependencies,
+          ...versionData.devDependencies,
+        }
+      : {
+          ...versionData.dependencies,
+          ...versionData.optionalDependencies,
+        }

345-350: Consider parallelising URL dependency scanning for consistency.

The scan currently processes packages sequentially, whereas OSV vulnerability queries use mapWithConcurrency. Since fetchNpmPackage likely caches packument data from the earlier resolveDependencyTree call, the impact is minimal, but parallel processing would be more consistent with the rest of the codebase.

♻️ Suggested refactor using mapWithConcurrency
     // Scan for git: and https: URL dependencies in all packages
-    const urlDependencies: UrlDependencyInfo[] = []
-    for (const pkg of packages) {
-      const pkgUrlDeps = await scanUrlDependencies(pkg.name, pkg.version, pkg.depth, pkg.path)
-      urlDependencies.push(...pkgUrlDeps)
-    }
+    const urlDepResults = await mapWithConcurrency(
+      packages,
+      pkg => scanUrlDependencies(pkg.name, pkg.version, pkg.depth, pkg.path),
+      OSV_DETAIL_CONCURRENCY,
+    )
+    const urlDependencies = urlDepResults.flat()

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96a2e5db-6be6-484f-8c91-d08d43f1c012

📥 Commits

Reviewing files that changed from the base of the PR and between 761e38e and 2840313.

📒 Files selected for processing (2)
  • app/components/Package/Dependencies.vue
  • server/utils/dependency-analysis.ts

- Only include devDependencies when scanning the root package
- Use mapWithConcurrency for consistent parallel processing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ndpvt-web
Copy link
Copy Markdown
Author

Addressed the additional feedback:

  1. devDependencies scoping - Now only includes devDependencies for the root package, avoiding misleading warnings for transitive packages.
  2. Parallel scanning - Replaced sequential loop with mapWithConcurrency for consistency with OSV queries.

Updated in the latest commit.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 09702971-64e5-4e98-a9ea-41e853e5d7da

📥 Commits

Reviewing files that changed from the base of the PR and between 2840313 and b10c55f.

📒 Files selected for processing (1)
  • server/utils/dependency-analysis.ts

Comment thread server/utils/dependency-analysis.ts
Comment thread server/utils/dependency-analysis.ts Outdated
- Record child dependency depth instead of parent depth
- Surface scan failures instead of silently returning empty array

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ndpvt-web
Copy link
Copy Markdown
Author

Addressed both issues:

  1. Depth correction - URL dependencies now correctly record their child depth (root deps -> direct, direct deps -> transitive) instead of using the parent package's depth.
  2. Error handling - Removed silent error swallowing; scan failures now propagate consistently with OSV error handling patterns.

Updated in the latest commit.

@serhalp serhalp added the needs review This PR is waiting for a review from a maintainer label Mar 15, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
server/utils/dependency-analysis.ts (1)

316-320: ⚠️ Potential issue | 🟠 Major

Don’t convert URL-scan failures into clean “no findings”.

Returning [] here masks scan failures as absence of risky URL dependencies, creating false negatives in a security signal. Please propagate the failure (or surface an explicit scan-failed status in the result) instead of silently returning an empty list.

Suggested fix
   } catch (error) {
     // oxlint-disable-next-line no-console -- log URL dependency scan failures for debugging
     console.warn(`[dep-analysis] URL dependency scan failed for ${name}@${version}:`, error)
-    return []
+    throw error
   }
 }

As per coding guidelines, "Use error handling patterns consistently".

🧹 Nitpick comments (1)
server/utils/dependency-analysis.ts (1)

259-275: Update JSDoc to match actual behaviour and return type.

The comments still describe only git:/https: checks and say “returns a map”, but the implementation also matches git+, http:, file: and returns UrlDependencyInfo[].


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f05a63de-efe3-41d2-ba11-fb42c7f42ffb

📥 Commits

Reviewing files that changed from the base of the PR and between 5d5cecc and 9e7c02a.

📒 Files selected for processing (1)
  • server/utils/dependency-analysis.ts

Copy link
Copy Markdown
Member

@serhalp serhalp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙌🏼 Thanks for the PR!

Comment on lines +208 to +209
/** All dependencies using git: or https: URLs */
urlDependencies: UrlDependencyInfo[]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two fields above use the term "package", so it would be nice to have consistency here to avoid giving a false sense of difference:

Suggested change
/** All dependencies using git: or https: URLs */
urlDependencies: UrlDependencyInfo[]
/** All packages using unsafe `git:`, `https:` (etc.) URLs in the tree */
packagesWithUrlVersion: UrlDependencyInfo[]

name: string
/** The git: or https: URL */
url: string
/** Depth in dependency tree: root (0), direct (1), transitive (2+) */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 how could root ever happen? this is only a possibility in the dependencies array of another package, isn't it?

<button
type="button"
class="p-2 -m-2"
:aria-label="`git/https dependency: ${getUrlDepInfo(dep)!.url}`"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hardcoded English string needs to be internationalized. Check out guidance in CONTRIBUTING.md if you aren't familiar!

Comment on lines +264 to +268
url.startsWith('git:') ||
url.startsWith('git+') ||
url.startsWith('http:') ||
url.startsWith('https:') ||
url.startsWith('file:')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per https://docs.npmjs.com/cli/v11/configuring-npm/package-json#git-urls-as-dependencies, these are also valid and insecure:

  • github:foo/bar
  • gist:foo
  • bitbucket:foo/bar
  • gitlab:foo/bar
  • foo/bar (this means GitHub)

🤔 We can't just assume any protocol is insecure, as npm: is fine.

Maybe something like this works?

Suggested change
url.startsWith('git:') ||
url.startsWith('git+') ||
url.startsWith('http:') ||
url.startsWith('https:') ||
url.startsWith('file:')
// starts with any `foo:` except `npm:`
url.match(/^(?!npm:)[^:]+:/) ||
// foo/bar
url.match(/^[^/]+/[^/]+$/)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oooh wait a sec, I think we can just do...

Suggested change
url.startsWith('git:') ||
url.startsWith('git+') ||
url.startsWith('http:') ||
url.startsWith('https:') ||
url.startsWith('file:')
!semver.valid(url) && !url.startsWith('npm:')

👀 although we'd have to think through "loose" mode and coercion to be sure we're using the same logic as package managers do.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I found this live example of a package with a dependency pointing at github:: https://npmx-r89mhfl1x-npmx.vercel.app/package/react-native-gauge

const urlDepResults = await mapWithConcurrency(
packages,
pkg => scanUrlDependencies(pkg.name, pkg.version, pkg.depth, pkg.path),
OSV_DETAIL_CONCURRENCY,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should create a separate constant for this, as it's unrelated to fetching OSV details

path: string[],
): Promise<UrlDependencyInfo[]> {
try {
const packument = await fetchNpmPackage(name)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be very slow, since it's re-fetching the packument for every package in the tree. This data was already constructed to begin with by fetching all these packuments and the path that "leads" to a given package is returned, so it seems it should be possible to derive what we need directly, no? 🤔

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it helps simplify things, I think it would even be fine to just attach a new field for this in resolveDependencyTree directly.

/**
* Check if a dependency URL is a git: or https: URL that should be flagged.
*/
function isUrlDependency(url: string): boolean {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function is a good candidate for great good unit test coverage. Let's do some table tests covering all the supported formats (see my comment below).

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 50.00000% with 4 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
app/components/Package/Dependencies.vue 50.00% 2 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@serhalp serhalp added stale This has become stale and may be closed soon and removed needs review This PR is waiting for a review from a maintainer labels Apr 10, 2026
@serhalp
Copy link
Copy Markdown
Member

serhalp commented Apr 10, 2026

@ndpvt-web hey there! are you interested in continuing to work on this PR?

@github-actions github-actions bot removed the stale This has become stale and may be closed soon label Apr 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flag git: and https: dependencies

2 participants