Skip to content

Commit da7fc94

Browse files
committed
Handle trailing slashes in allow list
1 parent 4cf1569 commit da7fc94

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

src/__tests__/unit/checks/keywords-urls.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,35 @@ describe('urls guardrail', () => {
311311
expect(result.info?.blocked).toHaveLength(3);
312312
expect(result.info?.blocked_reasons).toHaveLength(3);
313313
});
314+
315+
it('handles trailing slashes in allow list paths correctly', async () => {
316+
// Regression test: allow list entries with trailing slashes should match subpaths
317+
// Previously, '/api/' + '/' created '/api//' which wouldn't match '/api/users'
318+
const text = [
319+
'https://example.com/api/users',
320+
'https://example.com/api/v2/data',
321+
'https://example.com/other',
322+
].join(' ');
323+
324+
const result = await urls(
325+
{},
326+
text,
327+
{
328+
url_allow_list: ['https://example.com/api/'],
329+
allowed_schemes: new Set(['https']),
330+
allow_subdomains: false,
331+
block_userinfo: true,
332+
}
333+
);
334+
335+
expect(result.info?.allowed).toEqual(
336+
expect.arrayContaining([
337+
'https://example.com/api/users',
338+
'https://example.com/api/v2/data',
339+
])
340+
);
341+
expect(result.info?.blocked).toContain('https://example.com/other');
342+
});
314343
});
315344

316345
describe('competitors guardrail', () => {

src/checks/urls.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,13 @@ function isUrlAllowed(parsedUrl: URL, allowList: string[], allowSubdomains: bool
452452
}
453453

454454
if (allowedPath && allowedPath !== '/') {
455-
if (urlPath !== allowedPath && !urlPath.startsWith(`${allowedPath}/`)) {
455+
// Normalize trailing slashes to avoid double-slash issues when checking subpaths
456+
// e.g., if allowedPath is "/api/", we normalize to "/api" before adding "/"
457+
// so we check "/api/" not "/api//" when matching "/api/users"
458+
const normalizedAllowedPath = allowedPath.replace(/\/+$/, '');
459+
const normalizedUrlPath = urlPath.replace(/\/+$/, '');
460+
461+
if (normalizedUrlPath !== normalizedAllowedPath && !normalizedUrlPath.startsWith(`${normalizedAllowedPath}/`)) {
456462
continue;
457463
}
458464
}

0 commit comments

Comments
 (0)