Skip to content
Open
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
10 changes: 5 additions & 5 deletions src/app/api/crypto/public-keys/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,17 @@
export async function GET(request) {
const url = new URL(request.url);
try {
const userId = url.searchParams.get('user_id')?.trim();
if (!userId) {

Check failure

Code scanning / CodeQL

User-controlled bypass of security check High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
return NextResponse.json({ error: 'Missing user_id parameter' }, { status: 400 });
}

// Authenticate user
const { user, error: authError } = await authenticateUser(request);
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const userId = url.searchParams.get('user_id');
if (!userId) {
return NextResponse.json({ error: 'Missing user_id parameter' }, { status: 400 });
}

// Get the user's auth_user_id from the internal user ID
const { data: userData, error: userError } = await getServiceRoleClient()
.from('users')
Expand Down
36 changes: 35 additions & 1 deletion src/app/api/crypto/public-keys/route.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
const mocks = vi.hoisted(() => ({
authGetUser: vi.fn(),
serviceFrom: vi.fn(),
userEq: vi.fn(),
rpc: vi.fn()
}));

Expand Down Expand Up @@ -32,7 +33,7 @@ function createUsersQuery() {
selected = fields;
return query;
}),
eq: vi.fn(() => query),
eq: mocks.userEq,
single: vi.fn(() => {
if (selected.includes('id, auth_user_id')) {
return Promise.resolve({
Expand All @@ -46,6 +47,7 @@ function createUsersQuery() {
});
})
};
mocks.userEq.mockReturnValue(query);
return query;
}

Expand Down Expand Up @@ -82,4 +84,36 @@ describe('public key cookie authentication', () => {
expect(body).toEqual({ public_key: 'public-key', user_id: 'target-user-id' });
expect(mocks.authGetUser).toHaveBeenCalledWith('access-token');
});

it('trims user_id before public key lookup and response', async () => {
const { GET } = await import('./route.js');
const response = await GET(
new Request('https://qrypt.chat/api/crypto/public-keys?user_id=%20target-user-id%20', {
headers: {
cookie: `sb-xydzwxwsbgmznthiiscl-auth-token=${cookieValue('access-token')}`
}
})
);
const body = await response.json();

expect(response.status).toBe(200);
expect(body).toEqual({ public_key: 'public-key', user_id: 'target-user-id' });
expect(mocks.userEq).toHaveBeenCalledWith('id', 'target-user-id');
});

it('rejects blank user_id before public key lookup', async () => {
const { GET } = await import('./route.js');
const response = await GET(
new Request('https://qrypt.chat/api/crypto/public-keys?user_id=%20%20', {
headers: {
cookie: `sb-xydzwxwsbgmznthiiscl-auth-token=${cookieValue('access-token')}`
}
})
);
const body = await response.json();

expect(response.status).toBe(400);
expect(body).toEqual({ error: 'Missing user_id parameter' });
expect(mocks.serviceFrom).not.toHaveBeenCalled();
});
});
Loading