diff --git a/app/components/Package/Dependencies.vue b/app/components/Package/Dependencies.vue
index 817aa2458e..6d8778371d 100644
--- a/app/components/Package/Dependencies.vue
+++ b/app/components/Package/Dependencies.vue
@@ -37,6 +37,17 @@ function getDeprecatedDepInfo(depName: string) {
return vulnTree.value.deprecatedPackages.find(p => p.name === depName && p.depth === 'direct')
}
+// Cache URL dependency lookups with computed map
+const urlDepMap = computed(() => {
+ if (!vulnTree.value) return new Map()
+ return new Map(vulnTree.value.urlDependencies.map(dep => [dep.name, dep]))
+})
+
+// Check if a dependency uses git: or https: URL
+function getUrlDepInfo(depName: string) {
+ return urlDepMap.value.get(depName) ?? null
+}
+
// Expanded state for each section
const depsExpanded = shallowRef(false)
const peerDepsExpanded = shallowRef(false)
@@ -73,6 +84,8 @@ const sortedOptionalDependencies = computed(() => {
// Get version tooltip
function getDepVersionTooltip(dep: string, version: string) {
+ const urlDep = getUrlDepInfo(dep)
+ if (urlDep) return urlDep.url
const outdated = outdatedDeps.value[dep]
if (outdated) return getOutdatedTooltip(outdated, t)
if (getVulnerableDepInfo(dep) || getDeprecatedDepInfo(dep)) return version
@@ -82,6 +95,7 @@ function getDepVersionTooltip(dep: string, version: string) {
// Get version class
function getDepVersionClass(dep: string) {
+ if (getUrlDepInfo(dep)) return 'text-orange-700 dark:text-orange-500'
const outdated = outdatedDeps.value[dep]
if (outdated) return getVersionClass(outdated)
if (getVulnerableDepInfo(dep) || getDeprecatedDepInfo(dep)) return getVersionClass(undefined)
@@ -164,6 +178,19 @@ const numberFormatter = useNumberFormatter()
>
{{ $t('package.deprecated.label') }}
+
+
+
{
+ try {
+ const packument = await fetchNpmPackage(name)
+ const versionData = packument.versions[version]
+ if (!versionData) return []
+
+ const urlDeps: UrlDependencyInfo[] = []
+ // Include devDependencies only for the root package
+ const allDeps = depth === 'root'
+ ? {
+ ...versionData.dependencies,
+ ...versionData.optionalDependencies,
+ ...versionData.devDependencies,
+ }
+ : {
+ ...versionData.dependencies,
+ ...versionData.optionalDependencies,
+ }
+
+ // URL dependencies are children of the current package, so their depth is one level deeper
+ const dependencyDepth: DependencyDepth = depth === 'root' ? 'direct' : 'transitive'
+
+ for (const [depName, depUrl] of Object.entries(allDeps || {})) {
+ if (isUrlDependency(depUrl)) {
+ urlDeps.push({
+ name: depName,
+ url: depUrl,
+ depth: dependencyDepth,
+ path: [...path, `${depName}@${depUrl}`],
+ })
+ }
+ }
+
+ return urlDeps
+ } 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 []
+ }
+}
+
/**
* Analyze entire dependency tree for vulnerabilities and deprecated packages.
* Uses OSV batch API for efficient vulnerability discovery, then fetches
@@ -289,6 +353,14 @@ export const analyzeDependencyTree = defineCachedFunction(
return depthOrder[a.depth] - depthOrder[b.depth]
})
+ // Scan for git: and https: URL dependencies in all packages
+ const urlDepResults = await mapWithConcurrency(
+ packages,
+ pkg => scanUrlDependencies(pkg.name, pkg.version, pkg.depth, pkg.path),
+ OSV_DETAIL_CONCURRENCY,
+ )
+ const urlDependencies = urlDepResults.flat()
+
// Step 1: Use batch API to find which packages have vulnerabilities
// This is much faster than individual queries - one request for all packages
const { vulnerableIndices, failed: batchFailed } = await queryOsvBatch(packages)
@@ -347,6 +419,7 @@ export const analyzeDependencyTree = defineCachedFunction(
version,
vulnerablePackages,
deprecatedPackages,
+ urlDependencies,
totalPackages: packages.length,
failedQueries,
totalCounts,
@@ -356,6 +429,6 @@ export const analyzeDependencyTree = defineCachedFunction(
maxAge: 60 * 60,
swr: true,
name: 'dependency-analysis',
- getKey: (name: string, version: string) => `v2:${name}@${version}`,
+ getKey: (name: string, version: string) => `v3:${name}@${version}`,
},
)
diff --git a/shared/types/dependency-analysis.ts b/shared/types/dependency-analysis.ts
index a733c302e5..7c51b63cfb 100644
--- a/shared/types/dependency-analysis.ts
+++ b/shared/types/dependency-analysis.ts
@@ -180,6 +180,19 @@ export interface DeprecatedPackageInfo {
message: string
}
+/**
+ * URL-based dependency info (git:, https:)
+ */
+export interface UrlDependencyInfo {
+ name: string
+ /** The git: or https: URL */
+ url: string
+ /** Depth in dependency tree: root (0), direct (1), transitive (2+) */
+ depth: DependencyDepth
+ /** Dependency path from root package */
+ path: string[]
+}
+
/**
* Result of dependency tree analysis
*/
@@ -192,6 +205,8 @@ export interface VulnerabilityTreeResult {
vulnerablePackages: PackageVulnerabilityInfo[]
/** All deprecated packages in the tree */
deprecatedPackages: DeprecatedPackageInfo[]
+ /** All dependencies using git: or https: URLs */
+ urlDependencies: UrlDependencyInfo[]
/** Total packages analyzed */
totalPackages: number
/** Number of packages that could not be checked (OSV query failed) */