Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/api/notify/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,19 @@ import { gitHubUserValidator } from '@/services/github/validate-user';
import { getClientIp } from '@/utils/getClientIp';
import { verifyGitHubOwner } from '@/lib/github-owner-verification';

const makeRequest = (method: string, body?: object, search?: string) => {
const makeRequest = (
method: string,
body?: object,
search?: string,
headers?: Record<string, string>
) => {
const url = `http://localhost:3000/api/notify${search ? '?' + search : ''}`;
return new NextRequest(url, {
method,
headers: {
'x-forwarded-for': '127.0.0.1',
Authorization: 'Bearer test-owner-token',
...headers,
},
body: body ? JSON.stringify(body) : undefined,
});
Expand Down Expand Up @@ -593,7 +599,9 @@ describe('DELETE /api/notify', () => {
vi.mocked(Notification.deleteOne).mockResolvedValue({ deletedCount: 1 } as never);

const res = await DELETE(
makeRequest('DELETE', undefined, `user=testuser&managementToken=${managementToken}`)
makeRequest('DELETE', undefined, 'user=testuser', {
'x-notification-token': managementToken,
})
);
const data = await res.json();

Expand Down
2 changes: 1 addition & 1 deletion app/api/notify/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export async function DELETE(req: NextRequest) {
);
}

const providedManagementToken = getNotificationManagementToken(req, undefined, searchParams);
const providedManagementToken = getNotificationManagementToken(req);
const authorization = await authorizeNotificationMutation(
req,
normalizedUsername,
Expand Down
10 changes: 5 additions & 5 deletions lib/notification-management-token.empty-fallback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,16 @@ describe('notification management token empty / missing inputs', () => {
expect(getNotificationManagementToken(req, body)).toBe('body-token-xyz');
});

it('extracts token from search params managementToken', () => {
it('ignores managementToken in query parameters (security: no URL leakage)', () => {
const req = mockRequest();
const params = new URLSearchParams({ managementToken: 'query-token-123' });
expect(getNotificationManagementToken(req, undefined, params)).toBe('query-token-123');
expect(getNotificationManagementToken(req, undefined)).toBeNull();
});

it('extracts token from search params token fallback', () => {
it('ignores token query parameter (security: no URL leakage)', () => {
const req = mockRequest();
const params = new URLSearchParams({ token: 'fallback-token-456' });
expect(getNotificationManagementToken(req, undefined, params)).toBe('fallback-token-456');
expect(getNotificationManagementToken(req, undefined)).toBeNull();
});

it('prefers header over body when both are present', () => {
Expand All @@ -90,7 +90,7 @@ describe('notification management token empty / missing inputs', () => {
const req = mockRequest();
const body = { managementToken: 'body-value' };
const params = new URLSearchParams({ managementToken: 'query-value' });
expect(getNotificationManagementToken(req, body, params)).toBe('body-value');
expect(getNotificationManagementToken(req, body)).toBe('body-value');
});

it('ignores body managementToken when it is not a string', () => {
Expand Down
7 changes: 2 additions & 5 deletions lib/notification-management-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ export function hashNotificationManagementToken(token: string): string {

export function getNotificationManagementToken(
request: Request,
body?: { managementToken?: unknown },
searchParams?: URLSearchParams
body?: { managementToken?: unknown }
): string | null {
const headerToken = request.headers.get('x-notification-token')?.trim();
if (headerToken) return headerToken;
Expand All @@ -24,9 +23,7 @@ export function getNotificationManagementToken(
return bodyToken.trim();
}

const queryToken =
searchParams?.get('managementToken')?.trim() || searchParams?.get('token')?.trim();
return queryToken || null;
return null;
}

export function verifyNotificationManagementToken(
Expand Down
Loading