Yogesh
initial deploy
cd8bd0a
Raw
History Blame Contribute Delete
5.48 kB
import { NextResponse } from "next/server";
import { getSettings } from "@/lib/localDb";
import { isAuthRequired, isDashboardSessionAuthenticated } from "@/shared/utils/apiAuth";
import { createErrorResponse } from "@/lib/api/errorResponse";
import { extractApiKey, isValidApiKey } from "@/sse/services/auth";
import {
LOCAL_ONLY_API_PREFIXES,
ALWAYS_PROTECTED_API_PATHS,
LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES,
SPAWN_CAPABLE_PREFIXES,
} from "@/server/authz/routeGuard";
/**
* Static MANAGEMENT-tier example prefixes. Render-only — never consulted by
* the runtime policy. The actual MANAGEMENT classification is "any /api/*
* that is not LOCAL_ONLY, not v1/client, not on the public allowlist", so the
* inventory shows representative entries rather than a generated enumeration.
*/
const MANAGEMENT_TIER_PREFIXES: ReadonlyArray<string> = [
"/api/settings",
"/api/providers/",
"/api/api-keys",
];
const CLIENT_API_TIER_PREFIXES: ReadonlyArray<string> = ["/v1/", "/api/v1/"];
const PUBLIC_TIER_PREFIXES: ReadonlyArray<string> = ["/api/health", "/api/version", "/_next/"];
type TierName = "LOCAL_ONLY" | "ALWAYS_PROTECTED" | "MANAGEMENT" | "CLIENT_API" | "PUBLIC";
interface TierEntry {
name: TierName;
prefixes: string[];
description: string;
bypassable: boolean;
}
/**
* OQ-5: viewing the inventory requires authentication (dashboard session OR
* any valid API key, regardless of scope). The inventory leaks route-prefix
* taxonomy + current bypass state (reconnaissance value), so we never expose
* it anonymously — but a non-manage key holder may still inspect it.
*
* Compare with `requireManagementAuth` which would refuse anything below the
* manage scope; this endpoint is intentionally read-only and one rung lower.
*/
async function requireInventoryReadAuth(request: Request): Promise<Response | null> {
if (!(await isAuthRequired(request))) {
return null;
}
if (await isDashboardSessionAuthenticated(request)) {
return null;
}
const apiKey = extractApiKey(request);
if (apiKey) {
try {
if (await isValidApiKey(apiKey)) {
return null;
}
} catch {
return createErrorResponse({
status: 503,
message: "Service temporarily unavailable",
type: "server_error",
});
}
return createErrorResponse({
status: 403,
message: "Invalid API key",
type: "invalid_request",
});
}
return createErrorResponse({
status: 401,
message: "Authentication required",
type: "invalid_request",
});
}
function isBypassableConstant(prefix: string): boolean {
// A LOCAL_ONLY prefix is bypassable iff it appears in the compile-time
// bypass constant AND is not a SPAWN_CAPABLE prefix. Runtime DB state is
// surfaced separately via `bypassEnabled` / `bypassPrefixes`.
if (SPAWN_CAPABLE_PREFIXES.some((p) => p === prefix || prefix.startsWith(p))) {
return false;
}
return LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES.some((p) => p === prefix);
}
export async function GET(request: Request) {
const authError = await requireInventoryReadAuth(request);
if (authError) return authError;
try {
const settings = await getSettings();
const tiers: TierEntry[] = [
{
name: "LOCAL_ONLY",
prefixes: [...LOCAL_ONLY_API_PREFIXES],
description:
"Loopback-only routes. Spawn child processes; exposing them to non-local traffic is a known CVE class (GHSA-fhh6-4qxv-rpqj). Some entries are opt-in bypassable via the manage scope (kill-switch gated).",
bypassable: LOCAL_ONLY_API_PREFIXES.some(isBypassableConstant),
},
{
name: "ALWAYS_PROTECTED",
prefixes: [...ALWAYS_PROTECTED_API_PATHS],
description:
"Auth required unconditionally, even when requireLogin=false. Covers destructive / irreversible operations (shutdown, database settings).",
bypassable: false,
},
{
name: "MANAGEMENT",
prefixes: [...MANAGEMENT_TIER_PREFIXES],
description:
"Default tier for /api/* admin endpoints. Auth required unless requireLogin=false. PATCHes touching security-impacting keys require currentPassword re-auth.",
bypassable: false,
},
{
name: "CLIENT_API",
prefixes: [...CLIENT_API_TIER_PREFIXES],
description:
"Client-facing inference APIs. Accept Bearer API keys; not gated by dashboard sessions.",
bypassable: false,
},
{
name: "PUBLIC",
prefixes: [...PUBLIC_TIER_PREFIXES],
description: "Unauthenticated routes: health probes, public assets, onboarding bootstrap.",
bypassable: false,
},
];
const bypassEnabled =
typeof settings.localOnlyManageScopeBypassEnabled === "boolean"
? settings.localOnlyManageScopeBypassEnabled
: true;
const bypassPrefixesRaw = settings.localOnlyManageScopeBypassPrefixes;
const bypassPrefixes = Array.isArray(bypassPrefixesRaw)
? bypassPrefixesRaw.filter((p): p is string => typeof p === "string")
: [...LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES];
return NextResponse.json({
tiers,
bypassEnabled,
bypassPrefixes,
spawnCapablePrefixes: [...SPAWN_CAPABLE_PREFIXES],
});
} catch (error) {
console.log("Error loading authz inventory:", error);
return NextResponse.json({ error: "Failed to load authz inventory" }, { status: 500 });
}
}