The Next.js auth middleware (proxy.ts) was never wired in — it exported `proxy` from a file named proxy.ts, but Next.js requires a `middleware` export from middleware.ts. The middleware-manifest.json was empty, leaving all 42 API routes accessible without authentication. Fixes: - Rename web/proxy.ts → web/middleware.ts, export `middleware` not `proxy` - Add defense-in-depth auth-guard to /api/shutdown and /api/update routes - Remove shell: true from update-service spawn (command injection surface) - Update contract tests to verify middleware file name and export Closes #4014 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
2.1 KiB
TypeScript
79 lines
2.1 KiB
TypeScript
import {
|
|
checkForUpdate,
|
|
getUpdateStatus,
|
|
triggerUpdate,
|
|
} from "../../../../src/web/update-service.ts"
|
|
import { verifyAuthToken } from "../../../lib/auth-guard";
|
|
|
|
export const runtime = "nodejs"
|
|
export const dynamic = "force-dynamic"
|
|
|
|
export async function GET(request: Request): Promise<Response> {
|
|
// Defense-in-depth: verify auth token even though middleware should catch it.
|
|
const authError = verifyAuthToken(request);
|
|
if (authError) return authError;
|
|
try {
|
|
const versionInfo = await checkForUpdate()
|
|
const { status, error, targetVersion } = getUpdateStatus()
|
|
|
|
return Response.json(
|
|
{
|
|
currentVersion: versionInfo.currentVersion,
|
|
latestVersion: versionInfo.latestVersion,
|
|
updateAvailable: versionInfo.updateAvailable,
|
|
updateStatus: status,
|
|
...(error ? { error } : {}),
|
|
...(targetVersion ? { targetVersion } : {}),
|
|
},
|
|
{
|
|
headers: { "Cache-Control": "no-store" },
|
|
},
|
|
)
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error)
|
|
return Response.json(
|
|
{ error: message },
|
|
{
|
|
status: 500,
|
|
headers: { "Cache-Control": "no-store" },
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function POST(request: Request): Promise<Response> {
|
|
// Defense-in-depth: verify auth token even though middleware should catch it.
|
|
const authError = verifyAuthToken(request);
|
|
if (authError) return authError;
|
|
try {
|
|
const versionInfo = await checkForUpdate()
|
|
const started = triggerUpdate(versionInfo.latestVersion)
|
|
|
|
if (!started) {
|
|
return Response.json(
|
|
{ error: "Update already in progress" },
|
|
{
|
|
status: 409,
|
|
headers: { "Cache-Control": "no-store" },
|
|
},
|
|
)
|
|
}
|
|
|
|
return Response.json(
|
|
{ triggered: true },
|
|
{
|
|
status: 202,
|
|
headers: { "Cache-Control": "no-store" },
|
|
},
|
|
)
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error)
|
|
return Response.json(
|
|
{ error: message },
|
|
{
|
|
status: 500,
|
|
headers: { "Cache-Control": "no-store" },
|
|
},
|
|
)
|
|
}
|
|
}
|