mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-06 18:27:14 +00:00
fix(deps): migrate off abandoned react-router-dom, fix eslint's own brace-expansion
Two real, forward-compatible fixes for npm audit's high-severity advisories (not the downgrades npm audit fix --force offers): - react-router (GHSA-qwww-vcr4-c8h2, RSC CSRF bypass): react-router-dom is frozen at 7.18.1, pinning the vulnerable react-router@7.18.1 -- no newer react-router-dom release exists pointing at the fixed line. react-router itself has shipped the real fix at 8.3.0. Migrated the 9 files importing from react-router-dom (all using plain createBrowserRouter/RouterProvider/useLocation/useNavigate/Outlet, no RSC anywhere) to import from react-router directly instead. - brace-expansion/minimatch (GHSA-mh99-v99m-4gvg): fixed for eslint's own dependency chain (minimatch@10.2.5, used by eslint itself, storybook, typescript-eslint, swagger-client) via a scoped "minimatch@^10" override forcing brace-expansion to the now-published 5.0.8 patch -- within the range minimatch@10.2.5 already declares wanting (^5.0.5), so this isn't a version-pin workaround, just unblocking a patch release npm's resolver hadn't picked up. One advisory remains genuinely unfixable from our side: eslint-plugin-jsx-a11y pins minimatch@^3.1.2 (old major, never patched); forcing it to the 10.x line via override breaks npm's own dependency-tree validation (a real incompatibility, not just an npm quirk), so this needs an eslint-plugin-jsx-a11y release bumping its own minimatch. Lint-time only, no untrusted input reaches it -- ci.yml's Audit step comment updated to reflect the new, smaller remaining scope. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+20
-11
@@ -180,17 +180,26 @@ jobs:
|
||||
- name: Build Storybook
|
||||
run: npm run build-storybook
|
||||
working-directory: frontend
|
||||
# Known-accepted as of 2026-07-26: 2 high advisories (react-router via
|
||||
# react-router-dom, GHSA-qwww-vcr4-c8h2; brace-expansion/minimatch via
|
||||
# eslint-plugin-jsx-a11y, GHSA-mh99-v99m-4gvg). Neither is exploitable
|
||||
# here -- the react-router CVE only affects the unstable RSC APIs
|
||||
# (this app uses plain createBrowserRouter, no RSC anywhere), and the
|
||||
# jsx-a11y chain only runs against this repo's own hardcoded lint globs,
|
||||
# never untrusted input. Both "fixes" npm offers are downgrades (no
|
||||
# patched release exists yet in the currently-depended-upon package
|
||||
# line), so left as-is rather than trading a real regression for a
|
||||
# vulnerability that doesn't apply. Re-check on a future bump in case
|
||||
# upstream ships an actual forward fix.
|
||||
# Known-accepted as of 2026-07-26: 1 high advisory,
|
||||
# brace-expansion/minimatch via eslint-plugin-jsx-a11y's own pinned
|
||||
# minimatch@^3.1.2 (GHSA-mh99-v99m-4gvg). Not exploitable here -- this
|
||||
# chain only runs against this repo's own hardcoded lint globs, never
|
||||
# untrusted input. The eslint-core minimatch@10.x instance of the same
|
||||
# advisory is already fixed via the "minimatch@^10" override below
|
||||
# (brace-expansion 5.0.8); jsx-a11y's own minimatch is pinned to an old
|
||||
# major with no patched release in that line, and forcing it to 10.x
|
||||
# via override breaks npm's own dependency-tree validation (a genuine
|
||||
# incompatibility, not just an npm quirk) -- so this one can only be
|
||||
# fixed by an eslint-plugin-jsx-a11y release bumping its own minimatch.
|
||||
# `npm audit fix --force`'s suggestion is a downgrade to before jsx-a11y
|
||||
# adopted the vulnerable chain at all; left as-is rather than trading a
|
||||
# real regression for a vulnerability that doesn't apply.
|
||||
# The other advisory this comment used to cover (react-router RSC CSRF
|
||||
# bypass, GHSA-qwww-vcr4-c8h2) is actually fixed now: migrated off the
|
||||
# abandoned react-router-dom (frozen at 7.18.1, pinning the vulnerable
|
||||
# react-router 7.18.1) onto react-router 8.3.0 directly, which has the
|
||||
# real forward fix -- not a downgrade. Re-check on a future bump in
|
||||
# case upstream ships a real fix for the remaining advisory too.
|
||||
- name: Audit
|
||||
run: npm audit --audit-level=high
|
||||
working-directory: frontend
|
||||
|
||||
Generated
+19
-35
@@ -25,7 +25,7 @@
|
||||
"react-dom": "^19.2.8",
|
||||
"react-hook-form": "^7.82.0",
|
||||
"react-i18next": "^17.0.10",
|
||||
"react-router-dom": "^7.18.1",
|
||||
"react-router": "^8.3.0",
|
||||
"swagger-ui-react": "^5.32.11",
|
||||
"uplot": "^1.6.32",
|
||||
"zod": "^4.4.3"
|
||||
@@ -5777,15 +5777,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
@@ -6187,6 +6187,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
|
||||
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -6196,6 +6197,12 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-es": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz",
|
||||
"integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/copy-to-clipboard": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
||||
@@ -10176,20 +10183,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.18.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz",
|
||||
"integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-8.3.0.tgz",
|
||||
"integrity": "sha512-qyPMvW83jGIct3yiieisxdk9M745anqhpIMKN5m1t6yBMfgVPpt77aHOqs5fUlEJRMCGffg9BaQLH9oPVOL7xQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
"set-cookie-parser": "^2.6.0"
|
||||
"cookie-es": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
"node": ">=22.22.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
"react": ">=19.2.7",
|
||||
"react-dom": ">=19.2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
@@ -10197,28 +10203,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "7.18.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz",
|
||||
"integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-router": "7.18.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router/node_modules/set-cookie-parser": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
|
||||
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-syntax-highlighter": {
|
||||
"version": "16.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz",
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"react-dom": "^19.2.8",
|
||||
"react-hook-form": "^7.82.0",
|
||||
"react-i18next": "^17.0.10",
|
||||
"react-router-dom": "^7.18.1",
|
||||
"react-router": "^8.3.0",
|
||||
"swagger-ui-react": "^5.32.11",
|
||||
"uplot": "^1.6.32",
|
||||
"zod": "^4.4.3"
|
||||
@@ -61,6 +61,7 @@
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/swagger-ui-react": "^5.18.0",
|
||||
"@vitejs/plugin-react": "^6.0.4",
|
||||
"@vitest/browser-playwright": "4.1.10",
|
||||
"@vitest/coverage-v8": "^4.1.10",
|
||||
"eslint": "^10.7.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
@@ -70,18 +71,20 @@
|
||||
"jsdom": "^29.1.1",
|
||||
"lint-staged": "^17.1.1",
|
||||
"msw": "^2.15.0",
|
||||
"playwright": "^1.61.1",
|
||||
"storybook": "^10.5.3",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.65.0",
|
||||
"vite": "8.1.5",
|
||||
"vitest": "^4.1.10",
|
||||
"@vitest/browser-playwright": "4.1.10",
|
||||
"playwright": "^1.61.1"
|
||||
"vitest": "^4.1.10"
|
||||
},
|
||||
"overrides": {
|
||||
"eslint-plugin-jsx-a11y": {
|
||||
"eslint": "$eslint"
|
||||
},
|
||||
"minimatch@^10": {
|
||||
"brace-expansion": "^5.0.8"
|
||||
},
|
||||
"dompurify": "^3.4.11",
|
||||
"react-copy-to-clipboard": "^5.1.1",
|
||||
"react-inspector": "^9.0.0",
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.14.7'
|
||||
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
||||
const PACKAGE_VERSION = '2.15.0'
|
||||
const INTEGRITY_CHECKSUM = '03cb67ac84128e63d7cd722a6e5b7f1e'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
@@ -137,8 +137,18 @@ async function handleRequest(event, requestId, requestInterceptedAt) {
|
||||
if (client && activeClientIds.has(client.id)) {
|
||||
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
||||
|
||||
// Omit the body of server-sent event stream responses.
|
||||
// Cloning such responses would prevent client-side stream cancelations
|
||||
// from reaching the original stream (a teed stream only cancels its
|
||||
// source once both of its branches cancel) and would buffer the
|
||||
// entire stream into the unconsumed clone indefinitely.
|
||||
const isEventStreamResponse = response.headers
|
||||
.get('content-type')
|
||||
?.toLowerCase()
|
||||
.startsWith('text/event-stream')
|
||||
|
||||
// Clone the response so both the client and the library could consume it.
|
||||
const responseClone = response.clone()
|
||||
const responseClone = isEventStreamResponse ? null : response.clone()
|
||||
|
||||
sendToClient(
|
||||
client,
|
||||
@@ -151,15 +161,17 @@ async function handleRequest(event, requestId, requestInterceptedAt) {
|
||||
...serializedRequest,
|
||||
},
|
||||
response: {
|
||||
type: responseClone.type,
|
||||
status: responseClone.status,
|
||||
statusText: responseClone.statusText,
|
||||
headers: Object.fromEntries(responseClone.headers.entries()),
|
||||
body: responseClone.body,
|
||||
type: response.type,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: Object.fromEntries(response.headers.entries()),
|
||||
body: responseClone ? responseClone.body : null,
|
||||
},
|
||||
},
|
||||
},
|
||||
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
||||
responseClone && responseClone.body
|
||||
? [serializedRequest.body, responseClone.body]
|
||||
: [],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useLocation } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const TITLE_KEYS: Record<string, string> = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import type { ComponentType } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Drawer, Layout, Menu } from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { Outlet } from 'react-router';
|
||||
|
||||
import { useWebSocketBridge } from '@/api/websocketBridge';
|
||||
import { usePageTitle } from '@/hooks/usePageTitle';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import { RouterProvider } from 'react-router';
|
||||
import { message } from 'antd';
|
||||
import 'antd/dist/reset.css';
|
||||
import '@/styles/utils.css';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useLocation } from 'react-router';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Alert, Button, Input, InputNumber, Switch, Tabs } from 'antd';
|
||||
import { BranchesOutlined, CompassOutlined, IdcardOutlined, InfoCircleOutlined, NodeIndexOutlined, SafetyCertificateOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router';
|
||||
import type { AllSetting } from '@/models/setting';
|
||||
import { SettingListItem } from '@/components/ui';
|
||||
import { RemarkTemplateField } from '@/components/form';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { createBrowserRouter, type RouteObject } from 'react-router-dom';
|
||||
import { createBrowserRouter, type RouteObject } from 'react-router';
|
||||
|
||||
import PanelLayout from '@/layouts/PanelLayout';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import { MemoryRouter, useLocation } from 'react-router';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { AllSetting } from '@/models/setting';
|
||||
|
||||
Reference in New Issue
Block a user