mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-10 19:49:52 +08:00
fix(security): check request sources, regions and endpoints
- Bedrock: a request's AWS region must be a region name. It becomes part of the endpoint's host name, so a value such as "us-east-1.attacker.example/" sent the server's bearer token or signed request to another host. - MCP preview server: only the preview page itself (Origin equal to the Host) or a non-browser client may call it; a page on another localhost port could replace the diagram with a plain text POST. History builds its thumbnails element by element and shows only SVG data images, so a stored value can no longer run script in the preview. - chat, validate-model, validate-diagram, provider-models and parse-url take JSON bodies only, so another website cannot make the user's own server (the desktop app, a local install) run models with their keys; the desktop app also refuses a foreign Host (DNS rebinding). - The model list reads at most 2 MB, also through the Gateway SDK, and answers only with its own error texts: the URL is the caller's and may be an internal address. - An admin panel provider with its own key and no URL no longer inherits the global <P>_BASE_URL, which may be a proxy for another key; OpenAI then gets the official endpoint, as its Test. Azure keeps the server's resource.
This commit is contained in:
@@ -1,3 +1,31 @@
|
||||
/**
|
||||
* Refuse a POST that a page on another website could have sent. A browser
|
||||
* sends a cross-site POST without asking first (CORS preflight) only with a
|
||||
* text or form body, so the routes take JSON only. In the desktop app also
|
||||
* refuse a foreign Host: a site that points its own domain name at
|
||||
* 127.0.0.1 (DNS rebinding) is same-origin with the local server, but its
|
||||
* requests carry that domain. A request the server builds itself has no
|
||||
* Host. Returns the response to send, or null when the request may go on.
|
||||
*/
|
||||
export function rejectCrossSite(req: Request): Response | null {
|
||||
const contentType = req.headers.get("content-type") ?? ""
|
||||
if (!/^\s*application\/json\b/i.test(contentType)) {
|
||||
return Response.json(
|
||||
{ error: "Content-Type must be application/json" },
|
||||
{ status: 415 },
|
||||
)
|
||||
}
|
||||
const host = req.headers.get("host")
|
||||
if (
|
||||
process.env.NEXT_AI_DRAWIO_DESKTOP === "1" &&
|
||||
host &&
|
||||
!/^(127\.0\.0\.1|localhost)(:\d+)?$/i.test(host)
|
||||
) {
|
||||
return Response.json({ error: "Forbidden" }, { status: 403 })
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the x-access-code header against ACCESS_CODE_LIST.
|
||||
* Returns a 401 response to send back when the check fails, or null when the
|
||||
|
||||
Reference in New Issue
Block a user