Skip to content

Commit 761e38e

Browse files
ndpvt-webAI Assistant (Claude)
andcommitted
feat: flag git: and https: dependencies as security concern
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 #1084 Co-Authored-By: AI Assistant (Claude) <ai-assistant@contributor-bot.dev> Signed-off-by: ndpvt-web <ndpvt-web@users.noreply.github.com>
1 parent 11842b4 commit 761e38e

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

app/components/Package/Dependencies.vue

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ function getDeprecatedDepInfo(depName: string) {
3737
return vulnTree.value.deprecatedPackages.find(p => p.name === depName && p.depth === 'direct')
3838
}
3939
40+
// Check if a dependency uses git: or https: URL
41+
function getUrlDepInfo(depName: string) {
42+
if (!vulnTree.value) return null
43+
return vulnTree.value.urlDependencies.find(p => p.name === depName)
44+
}
45+
4046
// Expanded state for each section
4147
const depsExpanded = shallowRef(false)
4248
const peerDepsExpanded = shallowRef(false)
@@ -73,6 +79,8 @@ const sortedOptionalDependencies = computed(() => {
7379
7480
// Get version tooltip
7581
function getDepVersionTooltip(dep: string, version: string) {
82+
const urlDep = getUrlDepInfo(dep)
83+
if (urlDep) return urlDep.url
7684
const outdated = outdatedDeps.value[dep]
7785
if (outdated) return getOutdatedTooltip(outdated, t)
7886
if (getVulnerableDepInfo(dep) || getDeprecatedDepInfo(dep)) return version
@@ -82,6 +90,7 @@ function getDepVersionTooltip(dep: string, version: string) {
8290
8391
// Get version class
8492
function getDepVersionClass(dep: string) {
93+
if (getUrlDepInfo(dep)) return 'text-orange-700 dark:text-orange-500'
8594
const outdated = outdatedDeps.value[dep]
8695
if (outdated) return getVersionClass(outdated)
8796
if (getVulnerableDepInfo(dep) || getDeprecatedDepInfo(dep)) return getVersionClass(undefined)
@@ -164,6 +173,19 @@ const numberFormatter = useNumberFormatter()
164173
>
165174
<span class="sr-only">{{ $t('package.deprecated.label') }}</span>
166175
</LinkBase>
176+
<TooltipApp
177+
v-if="getUrlDepInfo(dep)"
178+
class="shrink-0 text-orange-700 dark:text-orange-500"
179+
:text="getUrlDepInfo(dep)!.url"
180+
>
181+
<button
182+
type="button"
183+
class="p-2 -m-2"
184+
:aria-label="`git/https dependency: ${getUrlDepInfo(dep)!.url}`"
185+
>
186+
<span class="i-lucide:triangle-alert w-3 h-3" aria-hidden="true" />
187+
</button>
188+
</TooltipApp>
167189
<LinkBase
168190
:to="packageRoute(dep, version)"
169191
class="block truncate"

server/utils/dependency-analysis.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
PackageVulnerabilityInfo,
99
VulnerabilityTreeResult,
1010
DeprecatedPackageInfo,
11+
UrlDependencyInfo,
1112
OsvAffected,
1213
OsvRange,
1314
} from '#shared/types/dependency-analysis'
@@ -255,6 +256,52 @@ function getSeverityLevel(vuln: OsvVulnerability): OsvSeverityLevel {
255256
return 'unknown'
256257
}
257258

259+
/**
260+
* Check if a dependency URL is a git: or https: URL that should be flagged.
261+
*/
262+
function isUrlDependency(url: string): boolean {
263+
return url.startsWith('git:') || url.startsWith('https:') || url.startsWith('git+https:')
264+
}
265+
266+
/**
267+
* Scan a package's dependencies for git: and https: URLs.
268+
* Returns a map of package names to their URL dependencies.
269+
*/
270+
async function scanUrlDependencies(
271+
name: string,
272+
version: string,
273+
depth: DependencyDepth,
274+
path: string[],
275+
): Promise<UrlDependencyInfo[]> {
276+
try {
277+
const packument = await fetchNpmPackage(name)
278+
const versionData = packument.versions[version]
279+
if (!versionData) return []
280+
281+
const urlDeps: UrlDependencyInfo[] = []
282+
const allDeps = {
283+
...versionData.dependencies,
284+
...versionData.optionalDependencies,
285+
...versionData.devDependencies,
286+
}
287+
288+
for (const [depName, depUrl] of Object.entries(allDeps || {})) {
289+
if (isUrlDependency(depUrl)) {
290+
urlDeps.push({
291+
name: depName,
292+
url: depUrl,
293+
depth,
294+
path: [...path, `${depName}@${depUrl}`],
295+
})
296+
}
297+
}
298+
299+
return urlDeps
300+
} catch {
301+
return []
302+
}
303+
}
304+
258305
/**
259306
* Analyze entire dependency tree for vulnerabilities and deprecated packages.
260307
* Uses OSV batch API for efficient vulnerability discovery, then fetches
@@ -289,6 +336,9 @@ export const analyzeDependencyTree = defineCachedFunction(
289336
return depthOrder[a.depth] - depthOrder[b.depth]
290337
})
291338

339+
// Scan for git: and https: URL dependencies in the root package
340+
const urlDependencies = await scanUrlDependencies(name, version, 'root', [])
341+
292342
// Step 1: Use batch API to find which packages have vulnerabilities
293343
// This is much faster than individual queries - one request for all packages
294344
const { vulnerableIndices, failed: batchFailed } = await queryOsvBatch(packages)
@@ -347,6 +397,7 @@ export const analyzeDependencyTree = defineCachedFunction(
347397
version,
348398
vulnerablePackages,
349399
deprecatedPackages,
400+
urlDependencies,
350401
totalPackages: packages.length,
351402
failedQueries,
352403
totalCounts,
@@ -356,6 +407,6 @@ export const analyzeDependencyTree = defineCachedFunction(
356407
maxAge: 60 * 60,
357408
swr: true,
358409
name: 'dependency-analysis',
359-
getKey: (name: string, version: string) => `v2:${name}@${version}`,
410+
getKey: (name: string, version: string) => `v3:${name}@${version}`,
360411
},
361412
)

server/utils/dependency-resolver.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export interface ResolvedPackage {
106106
path?: string[]
107107
/** Deprecation message if the version is deprecated */
108108
deprecated?: string
109+
/** Original URL if this was a git: or https: dependency */
110+
url?: string
109111
}
110112

111113
/**

shared/types/dependency-analysis.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,19 @@ export interface DeprecatedPackageInfo {
180180
message: string
181181
}
182182

183+
/**
184+
* URL-based dependency info (git:, https:)
185+
*/
186+
export interface UrlDependencyInfo {
187+
name: string
188+
/** The git: or https: URL */
189+
url: string
190+
/** Depth in dependency tree: root (0), direct (1), transitive (2+) */
191+
depth: DependencyDepth
192+
/** Dependency path from root package */
193+
path: string[]
194+
}
195+
183196
/**
184197
* Result of dependency tree analysis
185198
*/
@@ -192,6 +205,8 @@ export interface VulnerabilityTreeResult {
192205
vulnerablePackages: PackageVulnerabilityInfo[]
193206
/** All deprecated packages in the tree */
194207
deprecatedPackages: DeprecatedPackageInfo[]
208+
/** All dependencies using git: or https: URLs */
209+
urlDependencies: UrlDependencyInfo[]
195210
/** Total packages analyzed */
196211
totalPackages: number
197212
/** Number of packages that could not be checked (OSV query failed) */

0 commit comments

Comments
 (0)