From 43359779e73f2e2dea97a24c28c33f81d725cc61 Mon Sep 17 00:00:00 2001 From: khanon Date: Wed, 14 Jun 2023 04:05:51 +0000 Subject: [PATCH] Implements more robust anti-zoomer functionality (khanon/oai-reverse-proxy!24) --- src/config.ts | 2 +- src/proxy/anthropic.ts | 2 ++ src/proxy/middleware/common.ts | 28 ++++++++++++--- src/proxy/middleware/request/block-zoomers.ts | 34 +++++++++++++++++++ src/proxy/middleware/request/index.ts | 1 + src/proxy/openai.ts | 9 +++-- 6 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 src/proxy/middleware/request/block-zoomers.ts diff --git a/src/config.ts b/src/config.ts index 9755d80..9d168b0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -156,7 +156,7 @@ export const config: Config = { undefined ), queueMode: getEnvWithDefault("QUEUE_MODE", "fair"), - blockedOrigins: getEnvWithDefault("BLOCKED_ORIGINS", "janitorai"), + blockedOrigins: getEnvWithDefault("BLOCKED_ORIGINS", undefined), blockMessage: getEnvWithDefault( "BLOCK_MESSAGE", "You must be over the age of majority in your country to use this service." diff --git a/src/proxy/anthropic.ts b/src/proxy/anthropic.ts index af7cd82..b94eab7 100644 --- a/src/proxy/anthropic.ts +++ b/src/proxy/anthropic.ts @@ -9,6 +9,7 @@ import { handleProxyError } from "./middleware/common"; import { addKey, addAnthropicPreamble, + blockZoomers, createPreprocessorMiddleware, finalizeBody, languageFilter, @@ -71,6 +72,7 @@ const rewriteAnthropicRequest = ( const rewriterPipeline = [ addKey, addAnthropicPreamble, + blockZoomers, languageFilter, limitOutputTokens, finalizeBody, diff --git a/src/proxy/middleware/common.ts b/src/proxy/middleware/common.ts index e4f2421..7c70655 100644 --- a/src/proxy/middleware/common.ts +++ b/src/proxy/middleware/common.ts @@ -2,7 +2,6 @@ import { Request, Response } from "express"; import httpProxy from "http-proxy"; import { ZodError } from "zod"; - const OPENAI_CHAT_COMPLETION_ENDPOINT = "/v1/chat/completions"; const ANTHROPIC_COMPLETION_ENDPOINT = "/v1/complete"; @@ -32,9 +31,14 @@ export function writeErrorResponse( res.headersSent || res.getHeader("content-type") === "text/event-stream" ) { + const errorContent = + statusCode === 403 + ? JSON.stringify(errorPayload) + : JSON.stringify(errorPayload, null, 2); + const msg = buildFakeSseMessage( `${errorSource} error (${statusCode})`, - JSON.stringify(errorPayload, null, 2), + errorContent, req ); res.write(msg); @@ -57,6 +61,7 @@ export const handleInternalError = ( ) => { try { const isZod = err instanceof ZodError; + const isForbidden = err.name === "ForbiddenError"; if (isZod) { writeErrorResponse(req, res, 400, { error: { @@ -67,6 +72,17 @@ export const handleInternalError = ( message: err.message, }, }); + } else if (isForbidden) { + // Spoofs a vaguely threatening OpenAI error message. Only invoked by the + // block-zoomers rewriter to scare off tiktokers. + writeErrorResponse(req, res, 403, { + error: { + type: "organization_account_disabled", + code: "policy_violation", + param: null, + message: err.message, + }, + }); } else { writeErrorResponse(req, res, 500, { error: { @@ -91,10 +107,14 @@ export function buildFakeSseMessage( req: Request ) { let fakeEvent; + const useBackticks = !type.includes("403"); + const msgContent = useBackticks + ? `\`\`\`\n[${type}: ${string}]\n\`\`\`\n` + : `[${type}: ${string}]`; if (req.inboundApi === "anthropic") { fakeEvent = { - completion: `\`\`\`\n[${type}: ${string}]\n\`\`\`\n`, + completion: msgContent, stop_reason: type, truncated: false, // I've never seen this be true stop: null, @@ -109,7 +129,7 @@ export function buildFakeSseMessage( model: req.body?.model, choices: [ { - delta: { content: `\`\`\`\n[${type}: ${string}]\n\`\`\`\n` }, + delta: { content: msgContent }, index: 0, finish_reason: type, }, diff --git a/src/proxy/middleware/request/block-zoomers.ts b/src/proxy/middleware/request/block-zoomers.ts new file mode 100644 index 0000000..17135d9 --- /dev/null +++ b/src/proxy/middleware/request/block-zoomers.ts @@ -0,0 +1,34 @@ +import { isCompletionRequest } from "../common"; +import { ProxyRequestMiddleware } from "."; + +const DISALLOWED_ORIGIN_SUBSTRINGS = "janitorai.com,janitor.ai".split(","); + +class ForbiddenError extends Error { + constructor(message: string) { + super(message); + this.name = "ForbiddenError"; + } +} + +/** + * Blocks requests from Janitor AI users with a fake, scary error message so I + * stop getting emails asking for tech support. + */ +export const blockZoomers: ProxyRequestMiddleware = (_proxyReq, req) => { + if (!isCompletionRequest(req)) { + return; + } + + const origin = req.headers.origin || req.headers.referer; + if (origin && DISALLOWED_ORIGIN_SUBSTRINGS.some((s) => origin.includes(s))) { + // Venus-derivatives send a test prompt to check if the proxy is working. + // We don't want to block that just yet. + if (req.body.messages[0]?.content === "Just say TEST") { + return; + } + + throw new ForbiddenError( + `This OpenAI account has been disabled due to fraud and potential CSAM violations. Your IP address, user agent, and request details have been logged and will be shared with the National Center for Missing and Exploited Children and local law enforcement's cybercrime division to assist in their investigation.` + ); + } +}; diff --git a/src/proxy/middleware/request/index.ts b/src/proxy/middleware/request/index.ts index 11f2c00..d27e356 100644 --- a/src/proxy/middleware/request/index.ts +++ b/src/proxy/middleware/request/index.ts @@ -10,6 +10,7 @@ export { transformOutboundPayload } from "./transform-outbound-payload"; // HPM middleware (runs on onProxyReq, cannot be async) export { addKey } from "./add-key"; export { addAnthropicPreamble } from "./add-anthropic-preamble"; +export { blockZoomers } from "./block-zoomers"; export { finalizeBody } from "./finalize-body"; export { languageFilter } from "./language-filter"; export { limitCompletions } from "./limit-completions"; diff --git a/src/proxy/openai.ts b/src/proxy/openai.ts index 6060416..c68330e 100644 --- a/src/proxy/openai.ts +++ b/src/proxy/openai.ts @@ -9,6 +9,7 @@ import { ipLimiter } from "./rate-limit"; import { handleProxyError } from "./middleware/common"; import { addKey, + blockZoomers, createPreprocessorMiddleware, finalizeBody, languageFilter, @@ -28,15 +29,16 @@ function getModelsResponse() { return modelsCache; } + // https://platform.openai.com/docs/models/overview const gptVariants = [ "gpt-4", "gpt-4-0613", - "gpt-4-0314", + "gpt-4-0314", // EOL 2023-09-13 "gpt-4-32k", "gpt-4-32k-0613", - "gpt-4-32k-0314", + "gpt-4-32k-0314", // EOL 2023-09-13 "gpt-3.5-turbo", - "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0301", // EOL 2023-09-13 "gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613", @@ -89,6 +91,7 @@ const rewriteRequest = ( ) => { const rewriterPipeline = [ addKey, + blockZoomers, languageFilter, limitOutputTokens, limitCompletions,