From 9bbd06bf9bf2c3e254cfeee07c1da5f5cbcbcdee Mon Sep 17 00:00:00 2001
From: ProfTrader <3li8@proton.me>
Date: Thu, 25 Jun 2026 16:55:20 +0530
Subject: [PATCH] fix: address dependency audit findings (#962)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add npm overrides for vulnerable transitive frontend packages (ws, brace-expansion, ajv, @eslint/plugin-kit, postcss) — npm audit now reports 0 vulnerabilities — refresh uv.lock (langsmith, pydantic-settings, pip), and harden external window.open(..., '_blank') calls with noopener,noreferrer.
Verified: uv lock --check consistent, npm audit 0 vulns, npm build passes.
---
frontend/package-lock.json | 99 +++++++++----------
frontend/package.json | 7 ++
.../components/source/SourceDetailContent.tsx | 42 +++++---
frontend/src/lib/hooks/use-version-check.ts | 2 +-
uv.lock | 19 ++--
5 files changed, 91 insertions(+), 78 deletions(-)
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 66114352..c02d9f22 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1156,16 +1156,30 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz",
- "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==",
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+ "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.1",
+ "@eslint/core": "^1.2.1",
"levn": "^0.4.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+ "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@floating-ui/core": {
@@ -4478,15 +4492,6 @@
"typescript": ">=4.8.4 <5.9.0"
}
},
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
"node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -4531,6 +4536,17 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+ "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
"node_modules/@typescript-eslint/utils": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz",
@@ -5105,10 +5121,11 @@
}
},
"node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -5441,10 +5458,11 @@
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+ "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -9766,33 +9784,6 @@
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
}
},
- "node_modules/next/node_modules/postcss": {
- "version": "8.4.31",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
- "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
"node_modules/node-releases": {
"version": "2.0.47",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz",
@@ -10121,10 +10112,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
- "dev": true,
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
+ "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
"funding": [
{
"type": "opencollective",
@@ -10139,6 +10129,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -12600,9 +12591,9 @@
}
},
"node_modules/ws": {
- "version": "8.19.0",
- "resolved": "https://registry.npmmirror.com/ws/-/ws-8.19.0.tgz",
- "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "version": "8.21.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
+ "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/frontend/package.json b/frontend/package.json
index 5ab7f97f..226fa2c5 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -75,5 +75,12 @@
"tw-animate-css": "^1.3.5",
"typescript": "^5",
"vitest": "^4.1.8"
+ },
+ "overrides": {
+ "ws": "8.21.0",
+ "brace-expansion": "1.1.15",
+ "ajv": "6.15.0",
+ "@eslint/plugin-kit": "0.7.2",
+ "postcss": "8.5.10"
}
}
diff --git a/frontend/src/components/source/SourceDetailContent.tsx b/frontend/src/components/source/SourceDetailContent.tsx
index d3fed308..26d3ad82 100644
--- a/frontend/src/components/source/SourceDetailContent.tsx
+++ b/frontend/src/components/source/SourceDetailContent.tsx
@@ -77,6 +77,17 @@ interface SourceDetailContentProps {
onClose?: () => void
}
+const safeExternalHref = (url: string | null | undefined): string | null => {
+ if (!url) return null
+
+ try {
+ const parsedUrl = new URL(url)
+ return ['http:', 'https:'].includes(parsedUrl.protocol) ? parsedUrl.href : null
+ } catch {
+ return null
+ }
+}
+
export function SourceDetailContent({
sourceId,
showChatButton = false,
@@ -318,6 +329,8 @@ export function SourceDetailContent({
return 'text'
}
+ const externalHref = useMemo(() => safeExternalHref(source?.asset?.url), [source?.asset?.url])
+
const handleCopyUrl = useCallback(() => {
if (source?.asset?.url) {
navigator.clipboard.writeText(source.asset.url)
@@ -328,10 +341,10 @@ export function SourceDetailContent({
}, [source, t])
const handleOpenExternal = useCallback(() => {
- if (source?.asset?.url) {
- window.open(source.asset.url, '_blank')
+ if (externalHref) {
+ window.open(externalHref, '_blank', 'noopener,noreferrer')
}
- }, [source])
+ }, [externalHref])
const getYouTubeVideoId = (url: string): string | null => {
const patterns = [
@@ -347,14 +360,14 @@ export function SourceDetailContent({
}
const isYouTubeUrl = useMemo(() => {
- if (!source?.asset?.url) return false
- return !!(getYouTubeVideoId(source.asset.url))
- }, [source?.asset?.url])
+ if (!externalHref) return false
+ return !!(getYouTubeVideoId(externalHref))
+ }, [externalHref])
const youTubeVideoId = useMemo(() => {
- if (!source?.asset?.url) return null
- return getYouTubeVideoId(source.asset.url)
- }, [source?.asset?.url])
+ if (!externalHref) return null
+ return getYouTubeVideoId(externalHref)
+ }, [externalHref])
const handleDelete = async () => {
if (!source) return
@@ -481,16 +494,16 @@ export function SourceDetailContent({
{isYouTubeUrl &&