Skip to content

Commit ac22b34

Browse files
Merge pull request #269 from auth0/fix/snyk-medium-vulnerabilities
chore(core, react): fix snyk medium vulnerabilities
2 parents 0aea0b1 + e798ef7 commit ac22b34

9 files changed

Lines changed: 470 additions & 101 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ function SettingsPage() {
6969

7070
## Packages
7171

72-
| Package | Description |
73-
| ------------------------------------------------------ | ----------------------------------------------------- |
74-
| [@auth0/universal-components-react](./packages/react/) | React components with `/spa` and `/rwa` entry points |
75-
| [@auth0/universal-components-core](./packages/core/) | Framework-agnostic core utilities, services, and i18n |
72+
| Package | Description |
73+
| --------------------------------------------------------------- | ----------------------------------------------------- |
74+
| [@auth0/universal-components-react](./packages/react/README.md) | React components with `/spa` and `/rwa` entry points |
75+
| [@auth0/universal-components-core](./packages/core/README.md) | Framework-agnostic core utilities, services, and i18n |
7676

7777
## Feedback
7878

docs-site/api/r.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,32 @@ import path from 'path';
33

44
import type { VercelRequest, VercelResponse } from '@vercel/node';
55

6+
const RATE_LIMIT_WINDOW_MS = 60 * 1000; // 1 minute
7+
const RATE_LIMIT_MAX = 60;
8+
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
9+
10+
function pruneExpiredRateLimitEntries(now: number): void {
11+
for (const [ip, entry] of rateLimitMap.entries()) {
12+
if (now > entry.resetTime) {
13+
rateLimitMap.delete(ip);
14+
}
15+
}
16+
}
17+
18+
function isRateLimited(ip: string): boolean {
19+
const now = Date.now();
20+
pruneExpiredRateLimitEntries(now);
21+
const entry = rateLimitMap.get(ip);
22+
23+
if (!entry || now > entry.resetTime) {
24+
rateLimitMap.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW_MS });
25+
return false;
26+
}
27+
28+
entry.count++;
29+
return entry.count > RATE_LIMIT_MAX;
30+
}
31+
632
interface VersionInfo {
733
latest: string;
834
majorVersions?: Record<string, { latest: string }>;
@@ -54,6 +80,11 @@ function sendJson(res: VercelResponse, content: string): void {
5480
}
5581

5682
export default function handler(req: VercelRequest, res: VercelResponse) {
83+
const clientIp = (req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim() || 'unknown';
84+
if (isRateLimited(clientIp)) {
85+
return res.status(429).json({ error: 'Too Many Requests', message: 'Rate limit exceeded' });
86+
}
87+
5788
const { file } = req.query;
5889
const fileName = Array.isArray(file) ? file.join('/') : file || '';
5990

@@ -68,14 +99,15 @@ export default function handler(req: VercelRequest, res: VercelResponse) {
6899

69100
const basePath = getBasePath();
70101
const versionInfo = getVersionInfo(basePath);
71-
const versionParam = req.query.version as string | undefined;
102+
const rawVersionParam = req.query.version;
103+
const versionParam = typeof rawVersionParam === 'string' ? rawVersionParam : undefined;
72104

73105
const rootFilePath = path.join(basePath, normalizedFileName);
74106
if (!versionParam && fs.existsSync(rootFilePath)) {
75107
try {
76108
sendJson(res, fs.readFileSync(rootFilePath, 'utf-8'));
77109
} catch (error) {
78-
console.error(`Failed to read registry file ${rootFilePath}:`, error);
110+
console.error('Failed to read registry file: %s', rootFilePath);
79111
res
80112
.status(500)
81113
.json({ error: 'Internal Server Error', message: 'Failed to read registry file' });
@@ -121,7 +153,7 @@ export default function handler(req: VercelRequest, res: VercelResponse) {
121153
try {
122154
sendJson(res, fs.readFileSync(versionedPath, 'utf-8'));
123155
} catch (error) {
124-
console.error(`Failed to read registry file ${versionedPath}:`, error);
156+
console.error('Failed to read registry file: %s', versionedPath);
125157
res
126158
.status(500)
127159
.json({ error: 'Internal Server Error', message: 'Failed to read registry file' });

docs-site/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"clsx": "^2.1.1",
2626
"lucide-react": "^0.462.0",
2727
"next-themes": "^0.2.1",
28-
"postcss": "^8.5.6",
28+
"postcss": "^8.5.14",
2929
"prismjs": "^1.30.0",
3030
"react": "^19.2.1",
3131
"react-dom": "^19.2.1",
@@ -41,6 +41,6 @@
4141
"@types/react-syntax-highlighter": "^15.5.13",
4242
"globals": "^15.9.0",
4343
"typescript": "^5.9.3",
44-
"vite": "^7.2.4"
44+
"vite": "^7.3.2"
4545
}
4646
}

docs-site/src/api/registry-middleware.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,32 @@ import path from 'path';
33

44
import type { Plugin } from 'vite';
55

6+
const RATE_LIMIT_WINDOW_MS = 60 * 1000;
7+
const RATE_LIMIT_MAX = 60;
8+
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
9+
10+
function pruneExpiredRateLimitEntries(now: number): void {
11+
for (const [ip, entry] of rateLimitMap.entries()) {
12+
if (now > entry.resetTime) {
13+
rateLimitMap.delete(ip);
14+
}
15+
}
16+
}
17+
18+
function isRateLimited(ip: string): boolean {
19+
const now = Date.now();
20+
pruneExpiredRateLimitEntries(now);
21+
const entry = rateLimitMap.get(ip);
22+
23+
if (!entry || now > entry.resetTime) {
24+
rateLimitMap.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW_MS });
25+
return false;
26+
}
27+
28+
entry.count++;
29+
return entry.count > RATE_LIMIT_MAX;
30+
}
31+
632
interface VersionInfo {
733
latest: string;
834
majorVersions?: Record<string, { latest: string }>;
@@ -38,6 +64,17 @@ export function registryMiddleware(): Plugin {
3864
return next();
3965
}
4066

67+
const clientIp =
68+
(req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim() ||
69+
req.socket.remoteAddress ||
70+
'unknown';
71+
if (isRateLimited(clientIp)) {
72+
res.statusCode = 429;
73+
res.setHeader('Content-Type', 'application/json');
74+
res.end(JSON.stringify({ error: 'Too Many Requests' }));
75+
return;
76+
}
77+
4178
const url = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
4279
const fileName = url.pathname.replace(/^\/r\//, '');
4380

docs-site/src/components/ui/sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ function SidebarProvider({
7878
}
7979

8080
// This sets the cookie to keep the sidebar state.
81-
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
81+
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}; secure; samesite=lax`;
8282
},
8383
[setOpenProp, open],
8484
);

examples/next-rwa/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"lint": "next lint"
1111
},
1212
"dependencies": {
13-
"@auth0/nextjs-auth0": "^4.16.0",
13+
"@auth0/nextjs-auth0": "^4.20.0",
1414
"@auth0/universal-components-react": "workspace:*",
1515
"@radix-ui/react-slot": "^1.2.3",
1616
"@tailwindcss/postcss": "^4.1.17",
@@ -21,7 +21,7 @@
2121
"lucide-react": "^0.511.0",
2222
"next": "16.1.5",
2323
"ora": "^8.0.1",
24-
"postcss": "^8.5.5",
24+
"postcss": "^8.5.14",
2525
"radix-ui": "^1.4.3",
2626
"react": "^19.2.1",
2727
"react-dom": "^19.2.1",

packages/react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"@testing-library/user-event": "^14.6.1",
114114
"@types/react": "^19.1.2",
115115
"@types/react-dom": "^19.1.2",
116-
"postcss": "^8.5.6",
116+
"postcss": "^8.5.14",
117117
"postcss-cli": "^11.0.1",
118118
"react-hook-form": "^7.57.0",
119119
"rimraf": "^5.0.0",

packages/react/src/components/ui/image-preview-field.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ function ImagePreviewField({
7777

7878
const hasError = error || (!isValidUrl && imageUrl.trim() !== '') || imageError;
7979
const showPreview = imageUrl.trim() !== '' && isValidUrl && !imageError;
80+
const sanitizedImageUrl = showPreview ? encodeURI(imageUrl) : '';
8081

8182
return (
8283
<div className="space-y-2">
@@ -90,8 +91,8 @@ function ImagePreviewField({
9091
{showPreview ? (
9192
<img
9293
loading="lazy"
93-
srcSet={imageUrl}
94-
src={imageUrl}
94+
srcSet={sanitizedImageUrl}
95+
src={sanitizedImageUrl}
9596
sizes={imgSizes}
9697
decoding="async"
9798
alt="Preview"

0 commit comments

Comments
 (0)