feat: Add UUID validation for organization IDs in backend API endpoints and frontend logic.

This commit is contained in:
Ashwin 2026-02-20 21:14:24 +05:30
parent e564501c05
commit 8a6acc911e
4 changed files with 33 additions and 3 deletions

View file

@ -1,6 +1,7 @@
import json
import logging
import secrets
import uuid
import requests
from django.conf import settings
@ -293,6 +294,13 @@ class LoginView(APIView):
default_org = None
if org_id:
try:
uuid.UUID(str(org_id))
except (ValueError, AttributeError):
return Response(
{"error": "org_id must be a valid UUID"},
status=status.HTTP_400_BAD_REQUEST,
)
# Use specified org if user has access
try:
profile = profiles.get(org_id=org_id)
@ -547,6 +555,14 @@ class OrgSwitchView(APIView):
{"error": "org_id is required"}, status=status.HTTP_400_BAD_REQUEST
)
try:
uuid.UUID(str(org_id))
except (ValueError, AttributeError):
return Response(
{"error": "org_id must be a valid UUID"},
status=status.HTTP_400_BAD_REQUEST,
)
# Get current org for audit logging
from_org = getattr(request, "org", None)

View file

@ -15,6 +15,7 @@ import axios from 'axios';
import { env } from '$env/dynamic/public';
const API_BASE_URL = `${env.PUBLIC_DJANGO_API_URL}/api`;
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
/**
* @typedef {{ default_currency?: string, currency_symbol?: string, default_country?: string|null }} OrgSettingsPayload
@ -169,7 +170,10 @@ export const handle = sequence(Sentry.sentryHandle(), async function _handle({ e
};
// Check if org cookie is set and token has org context
if (orgId) {
if (orgId && !UUID_RE.test(orgId)) {
// Invalid org cookie value, clear it
event.cookies.delete('org', { path: '/' });
} else if (orgId) {
const token = /** @type {string} */ (accessToken);
if (tokenHasOrgContext(token, orgId)) {

View file

@ -10,6 +10,8 @@
import { env } from '$env/dynamic/public';
import { goto } from '$app/navigation';
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
// API Base URL from environment variables
// Note: VITE_ prefix is required for client-side env vars
const API_BASE_URL = env.PUBLIC_DJANGO_API_URL
@ -231,6 +233,9 @@ export const auth = {
/** @type {Record<string, string>} */
const body = { email, password };
if (orgId) {
if (!UUID_RE.test(orgId)) {
throw new Error('Invalid organization ID format');
}
body.org_id = orgId;
}
@ -276,6 +281,9 @@ export const auth = {
* @returns {Promise<any>} New tokens and org data
*/
async switchOrg(orgId) {
if (!orgId || !UUID_RE.test(orgId)) {
throw new Error('Invalid organization ID format');
}
/** @type {any} */
const data = await apiRequest('/auth/switch-org/', {
method: 'POST',

View file

@ -13,6 +13,8 @@ import { env as publicEnv } from '$env/dynamic/public';
import { redirect, fail } from '@sveltejs/kit';
import axios from 'axios';
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
/** @type {import('./$types').PageServerLoad} */
export async function load({ cookies, locals }) {
const user = locals.user;
@ -65,8 +67,8 @@ export const actions = {
const orgId = formData.get('org_id')?.toString();
const orgName = formData.get('org_name')?.toString();
if (!orgId) {
return fail(400, { error: 'Organization ID is required' });
if (!orgId || !UUID_RE.test(orgId)) {
return fail(400, { error: 'Invalid Organization ID' });
}
const jwtAccess = cookies.get('jwt_access');