Merge pull request #4079 from cryst-hq/codex/fix-umami-4044

Fix tracker script name route matching
This commit is contained in:
Mike Cao
2026-03-09 23:28:40 -07:00
committed by GitHub
4 changed files with 65 additions and 4 deletions
+6 -4
View File
@@ -1,4 +1,5 @@
import { type NextRequest, NextResponse } from 'next/server';
import { matchesConfiguredPath } from '@/lib/match-configured-path';
export const config = {
matcher: '/:path*',
@@ -7,6 +8,7 @@ export const config = {
const TRACKER_PATH = '/script.js';
const COLLECT_PATH = '/api/send';
const LOGIN_PATH = '/login';
const BASE_PATH = process.env.BASE_PATH || '';
const apiHeaders = {
'Access-Control-Allow-Origin': '*',
@@ -27,7 +29,7 @@ function customCollectEndpoint(request: NextRequest) {
if (collectEndpoint) {
const url = request.nextUrl.clone();
if (url.pathname.endsWith(collectEndpoint)) {
if (matchesConfiguredPath(url.pathname, collectEndpoint, BASE_PATH)) {
url.pathname = COLLECT_PATH;
return NextResponse.rewrite(url, { headers: apiHeaders });
}
@@ -41,7 +43,7 @@ function customScriptName(request: NextRequest) {
const url = request.nextUrl.clone();
const names = scriptName.split(',').map(name => name.trim().replace(/^\/+/, ''));
if (names.find(name => url.pathname.endsWith(name))) {
if (names.find(name => matchesConfiguredPath(url.pathname, name, BASE_PATH))) {
url.pathname = TRACKER_PATH;
return NextResponse.rewrite(url, { headers: trackerHeaders });
}
@@ -51,7 +53,7 @@ function customScriptName(request: NextRequest) {
function customScriptUrl(request: NextRequest) {
const scriptUrl = process.env.TRACKER_SCRIPT_URL;
if (scriptUrl && request.nextUrl.pathname.endsWith(TRACKER_PATH)) {
if (scriptUrl && matchesConfiguredPath(request.nextUrl.pathname, TRACKER_PATH, BASE_PATH)) {
return NextResponse.rewrite(scriptUrl, { headers: trackerHeaders });
}
}
@@ -59,7 +61,7 @@ function customScriptUrl(request: NextRequest) {
function disableLogin(request: NextRequest) {
const loginDisabled = process.env.DISABLE_LOGIN;
if (loginDisabled && request.nextUrl.pathname.endsWith(LOGIN_PATH)) {
if (loginDisabled && matchesConfiguredPath(request.nextUrl.pathname, LOGIN_PATH, BASE_PATH)) {
return new NextResponse('Access denied', { status: 403 });
}
}
+4
View File
@@ -6701,6 +6701,10 @@ packages:
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
engines: {node: '>=10.0.0'}
tar@6.2.1:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
tailwind-merge@3.4.0:
resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
@@ -0,0 +1,17 @@
import { matchesConfiguredPath } from '../match-configured-path';
test('matches the exact configured path', () => {
expect(matchesConfiguredPath('/d.js', 'd.js')).toBe(true);
});
test('does not match unrelated asset paths that only share the suffix', () => {
expect(matchesConfiguredPath('/_next/static/chunks/app/dashboard.js', 'd.js')).toBe(false);
});
test('matches paths under the configured base path', () => {
expect(matchesConfiguredPath('/umami/d.js', 'd.js', '/umami')).toBe(true);
});
test('normalizes leading slashes in configured paths', () => {
expect(matchesConfiguredPath('/script.js', '/script.js')).toBe(true);
});
+38
View File
@@ -0,0 +1,38 @@
function normalizePathname(pathname?: string) {
if (!pathname) {
return '';
}
return `/${pathname.replace(/^\/+/, '')}`;
}
function normalizeBasePath(basePath?: string) {
if (!basePath) {
return '';
}
return `/${basePath.replace(/^\/+|\/+$/g, '')}`;
}
export function matchesConfiguredPath(
pathname: string,
configuredPath?: string,
basePath?: string,
) {
const normalizedPathname = normalizePathname(pathname);
const normalizedConfiguredPath = normalizePathname(configuredPath);
if (!normalizedConfiguredPath) {
return false;
}
if (normalizedPathname === normalizedConfiguredPath) {
return true;
}
const normalizedBasePath = normalizeBasePath(basePath);
return normalizedBasePath
? normalizedPathname === `${normalizedBasePath}${normalizedConfiguredPath}`
: false;
}