mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 14:52:09 +03:00
Fase 8 · Bloco D — injection-guard em todas as rotas LLM + red-team (#3857)
Integrated into release/v3.8.25 — Fase 8 Bloco D (injection-guard em todas as rotas LLM + red-team).
This commit is contained in:
committed by
GitHub
parent
d3146a1751
commit
d728bfbb1e
@@ -1,4 +1,5 @@
|
||||
import { handleAudioSpeech } from "@omniroute/open-sse/handlers/audioSpeech.ts";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import { getProviderCredentials, clearRecoveredProviderState } from "@/sse/services/auth";
|
||||
import {
|
||||
parseSpeechModel,
|
||||
@@ -33,7 +34,7 @@ export async function OPTIONS() {
|
||||
* POST /v1/audio/speech — text-to-speech
|
||||
* OpenAI TTS API compatible. Returns audio stream.
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
let rawBody;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -110,3 +111,5 @@ export async function POST(request) {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -13,6 +13,7 @@ import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
import { getAllCustomModels, getApiKeyMetadata } from "@/lib/localDb";
|
||||
import { createEmbeddingResponse, type EmbeddingHandlerOptions } from "@/lib/embeddings/service";
|
||||
import { extractApiKey, isValidApiKey } from "@/sse/services/auth";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
|
||||
function toProviderScopedModelId(providerId: string, modelId: string): string {
|
||||
return modelId.startsWith(`${providerId}/`) ? modelId : `${providerId}/${modelId}`;
|
||||
@@ -75,7 +76,7 @@ export async function handleValidatedEmbeddingRequestBody(
|
||||
return createEmbeddingResponse(body, options);
|
||||
}
|
||||
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
let rawBody;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -120,3 +121,5 @@ export async function POST(request) {
|
||||
connectionId: null,
|
||||
});
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
handleImageEdit,
|
||||
handleOpenAIImageEdit,
|
||||
} from "@omniroute/open-sse/handlers/imageGeneration.ts";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import { getProviderCredentials, clearRecoveredProviderState } from "@/sse/services/auth";
|
||||
import { parseImageModel, getImageProvider } from "@omniroute/open-sse/config/imageRegistry.ts";
|
||||
import { errorResponse, unavailableResponse } from "@omniroute/open-sse/utils/error.ts";
|
||||
@@ -136,7 +137,7 @@ function jsonResponse(data: unknown, status = 200): Response {
|
||||
});
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
async function postHandler(request: Request, context) {
|
||||
const input = await readEditInput(request);
|
||||
if (!input) {
|
||||
return errorResponse(
|
||||
@@ -283,3 +284,5 @@ export async function POST(request: Request) {
|
||||
(result as any).status
|
||||
);
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { handleImageGeneration } from "@omniroute/open-sse/handlers/imageGeneration.ts";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import {
|
||||
getProviderCredentials,
|
||||
clearRecoveredProviderState,
|
||||
@@ -118,7 +119,7 @@ function publicBaseUrlHeaders(headers: Headers): Record<string, string> {
|
||||
return out;
|
||||
}
|
||||
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
let rawBody;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -277,3 +278,5 @@ export async function POST(request) {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
|
||||
let initialized = false;
|
||||
|
||||
@@ -29,7 +30,9 @@ export async function OPTIONS() {
|
||||
/**
|
||||
* POST /v1/messages - Claude format (auto convert via handleChat)
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
await ensureInitialized();
|
||||
return await handleChat(request);
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { handleModeration } from "@omniroute/open-sse/handlers/moderations.ts";
|
||||
import { getProviderCredentials, clearRecoveredProviderState } from "@/sse/services/auth";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import { parseModerationModel } from "@omniroute/open-sse/config/moderationRegistry.ts";
|
||||
import { errorResponse } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { HTTP_STATUS } from "@omniroute/open-sse/config/constants.ts";
|
||||
@@ -27,7 +28,7 @@ export async function OPTIONS() {
|
||||
* POST /v1/moderations — content moderation
|
||||
* OpenAI Moderations API compatible.
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
let rawBody;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -68,3 +69,5 @@ export async function POST(request) {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { handleMusicGeneration } from "@omniroute/open-sse/handlers/musicGeneration.ts";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import {
|
||||
getProviderCredentials,
|
||||
clearRecoveredProviderState,
|
||||
@@ -59,7 +60,7 @@ export async function GET() {
|
||||
/**
|
||||
* POST /v1/music/generations — generate music
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
let rawBody;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -125,3 +126,5 @@ export async function POST(request) {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { handleRerank } from "@omniroute/open-sse/handlers/rerank.ts";
|
||||
import { getProviderCredentials, clearRecoveredProviderState } from "@/sse/services/auth";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import { parseRerankModel, getRerankProvider } from "@omniroute/open-sse/config/rerankRegistry.ts";
|
||||
import { errorResponse } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { HTTP_STATUS } from "@omniroute/open-sse/config/constants.ts";
|
||||
@@ -48,7 +49,7 @@ function buildDynamicRerankProvider(node: any) {
|
||||
* Supports cloud providers (Cohere, Together, NVIDIA, Fireworks)
|
||||
* and local provider_nodes (oMLX, vLLM, etc.) via dynamic routing.
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
let rawBody;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -182,3 +183,5 @@ export async function POST(request) {
|
||||
`Invalid rerank model: ${body.model}. Use format: provider/model`
|
||||
);
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { withEarlyStreamKeepalive } from "@omniroute/open-sse/utils/earlyStreamKeepalive";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import { resolveResponsesApiModel } from "@/app/api/internal/codex-responses-ws/modelResolution";
|
||||
import { getModelInfo } from "@/sse/services/model";
|
||||
import { getComboByName } from "@/lib/db/combos";
|
||||
@@ -61,7 +62,7 @@ export async function withCodexPreferredModel(request: Request): Promise<Request
|
||||
* POST /v1/responses - OpenAI Responses API format
|
||||
* Handled by the unified chat handler (openai-responses format auto-detected).
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
// Codex CLI (wire_api="responses") consumes this endpoint over SSE and its reqwest
|
||||
// client drops the connection if no bytes arrive within ~5s. Keep the connection
|
||||
// warm with early keepalives while the upstream produces its first token (#2544).
|
||||
@@ -85,3 +86,5 @@ export async function POST(request) {
|
||||
}
|
||||
return await handleChat(resolved);
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
rateLimitedProviderResponse,
|
||||
type RateLimitedCredentials,
|
||||
} from "@/app/api/v1/_shared/rateLimit";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
|
||||
const CORS_HEADERS = {
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
@@ -101,7 +102,7 @@ function buildDomainFilter(filters?: {
|
||||
/**
|
||||
* POST /v1/search — execute a web search
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
async function postHandler(request: Request, context: unknown) {
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -319,3 +320,5 @@ class SearchError extends Error {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { handleVideoGeneration } from "@omniroute/open-sse/handlers/videoGeneration.ts";
|
||||
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
|
||||
import {
|
||||
getProviderCredentials,
|
||||
clearRecoveredProviderState,
|
||||
@@ -59,7 +60,7 @@ export async function GET() {
|
||||
/**
|
||||
* POST /v1/videos/generations — generate videos
|
||||
*/
|
||||
export async function POST(request) {
|
||||
async function postHandler(request, context) {
|
||||
let rawBody;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
@@ -125,3 +126,5 @@ export async function POST(request) {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
export const POST = withInjectionGuard(postHandler);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
type PromptInjectionGuardrailOptions,
|
||||
} from "@/lib/guardrails/promptInjection";
|
||||
import { resolveDisabledGuardrails } from "@/lib/guardrails/registry";
|
||||
import { CORS_HEADERS } from "@/shared/utils/cors";
|
||||
|
||||
/**
|
||||
* Create a prompt injection guard middleware.
|
||||
@@ -71,10 +72,11 @@ export function withInjectionGuard(handler: any, options: any = {}) {
|
||||
error: {
|
||||
message: "Request blocked: potential prompt injection detected",
|
||||
type: "injection_detected",
|
||||
code: "SECURITY_001",
|
||||
detections: result.detections.length,
|
||||
},
|
||||
}),
|
||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
||||
{ status: 400, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,20 @@ function extractMessageContents(body) {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof body.input === "string") contents.push(body.input);
|
||||
if (typeof body.prompt === "string") contents.push(body.prompt);
|
||||
else if (Array.isArray(body.prompt))
|
||||
for (const p of body.prompt) {
|
||||
if (typeof p === "string") contents.push(p);
|
||||
}
|
||||
if (typeof body.instructions === "string") contents.push(body.instructions);
|
||||
if (typeof body.query === "string") contents.push(body.query);
|
||||
if (Array.isArray(body.documents))
|
||||
for (const d of body.documents) {
|
||||
if (typeof d === "string") contents.push(d);
|
||||
else if (d && typeof d.text === "string") contents.push(d.text);
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user