feat(validate): implement SSRF guard for remote requests and protect sensitive settings
This commit is contained in:
parent
b55cf36d2e
commit
090886ced9
2 changed files with 17 additions and 0 deletions
|
|
@ -1,4 +1,6 @@
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import { assertPublicUrl } from "@/shared/utils/ssrfGuard.js";
|
||||||
|
import { isLocalRequest } from "@/dashboardGuard";
|
||||||
|
|
||||||
// Fetch with timeout wrapper
|
// Fetch with timeout wrapper
|
||||||
const fetchWithTimeout = (url, options, timeout = 10000) => {
|
const fetchWithTimeout = (url, options, timeout = 10000) => {
|
||||||
|
|
@ -64,6 +66,15 @@ export async function POST(request) {
|
||||||
return NextResponse.json({ error: "Invalid URL format" }, { status: 400 });
|
return NextResponse.json({ error: "Invalid URL format" }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSRF guard for remote callers; local host keeps self-hosted nodes (e.g. ollama-local)
|
||||||
|
if (!isLocalRequest(request)) {
|
||||||
|
try {
|
||||||
|
assertPublicUrl(baseUrl);
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ error: "URL not allowed" }, { status: 400 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Custom Embedding Validation - test POST /embeddings directly
|
// Custom Embedding Validation - test POST /embeddings directly
|
||||||
if (type === "custom-embedding") {
|
if (type === "custom-embedding") {
|
||||||
const normalizedBase = baseUrl.trim().replace(/\/$/, "");
|
const normalizedBase = baseUrl.trim().replace(/\/$/, "");
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ const SETTINGS_RESPONSE_HEADERS = {
|
||||||
"Cache-Control": "no-store"
|
"Cache-Control": "no-store"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Secrets must never be mass-assigned from request body (CWE-915)
|
||||||
|
const PROTECTED_SETTING_KEYS = ["password", "mitmSudoEncrypted"];
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
const settings = await getSettings();
|
const settings = await getSettings();
|
||||||
|
|
@ -36,6 +39,9 @@ export async function PATCH(request) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
|
|
||||||
|
// Strip protected secrets before any internal handling sets them
|
||||||
|
for (const key of PROTECTED_SETTING_KEYS) delete body[key];
|
||||||
|
|
||||||
// If updating password, hash it
|
// If updating password, hash it
|
||||||
if (body.newPassword) {
|
if (body.newPassword) {
|
||||||
const settings = await getSettings();
|
const settings = await getSettings();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue