CVE-2024-24566 (TypeScript, published 2024-01-31T17:15Z)
Description: value: Lobe Chat is a chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. When the application is password-protected (deployed with the `ACCESS_CODE` option), it is possible to access plugins without proper authorization (without password). This vulnerability is patched in 0.122.4.
Severity: nan (CVSS v3 base score: 5.3)
Weakness: NVD-CWE-Other \u2014 Other
NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
Repository: https://github.com/lobehub/lobe-chat
Files changed:
src/app/api/openai/chat/createChatCompletion.ts
src/app/api/openai/createBizOpenAI/index.ts
src/app/api/plugin/gateway/route.ts
src/app/chat/features/ChatHeader/SettingButton.tsx
src/app/chat/settings/(desktop)/index.tsx
src/services/chat.ts
Commit message: 🚑️ fix: fix plugin gateway auth (#1195)

* 🚑️ fix: fix plugin auth

* 💚 fix: fix build

* 🐛 fix: fix query of chat settings

* 🐛 fix: fix auth

Vulnerable code:
import { createErrorResponse } from '../errorResponse';
import { createErrorResponse } from '../errorResponse';
import { parserPluginSettings } from '@/app/api/plugin/gateway/settings';
export const POST = createGatewayOnEdgeRuntime({
  defaultPluginSettings,
  pluginsIndexUrl,
});
          router.push(pathString('/chat/settings', { hash: location.hash }));
  <ResponsiveIndex Mobile={Mobile}>
    <Layout>
      <EditPage />
    </Layout>
  </ResponsiveIndex>
      headers: createHeadersWithPluginSettings(settings),
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`settingsSelectors > CUSTOM_MODELS > custom deletion, addition, and renaming of models 1`] = `
[
  {
    "displayName": "llama",
    "name": "llama",
  },
  {
    "displayName": "claude-2",
    "name": "claude-2",
  },
  {
    "displayName": "gpt-4-32k",
    "name": "gpt-4-1106-preview",
  },
]
`;

exports[`settingsSelectors > CUSTOM_MODELS > duplicate naming model 1`] = `
[
  {
    "displayName": "gpt-3.5-turbo",
    "name": "gpt-3.5-turbo",
  },
  {
    "displayName": "gpt-3.5-turbo-1106",
    "name": "gpt-3.5-turbo-1106",
  },
  {
    "displayName": "gpt-3.5-turbo-16k",
    "name": "gpt-3.5-turbo-16k",
  },
  {
    "displayName": "gpt-4",
    "name": "gpt-4",
  },
  {
    "displayName": "gpt-4-32k",
    "name": "gpt-4-32k",
  },
  {
    "displayName": "gpt-4-vision-preview",
    "name": "gpt-4-vision-preview",
  },
  {
    "displayName": "gpt-4-32k",
    "name": "gpt-4-1106-preview",
  },
]
`;

exports[`settingsSelectors > CUSTOM_MODELS > only add the model 1`] = `
[
  {
    "displayName": "gpt-3.5-turbo",
    "name": "gpt-3.5-turbo",
  },
  {
    "displayName": "gpt-3.5-turbo-1106",
    "name": "gpt-3.5-turbo-1106",
  },
  {
    "displayName": "gpt-3.5-turbo-16k",
    "name": "gpt-3.5-turbo-16k",
  },
  {
    "displayName": "gpt-4",
    "name": "gpt-4",
  },
  {
    "displayName": "gpt-4-32k",
    "name": "gpt-4-32k",
  },
  {
    "displayName": "gpt-4-1106-preview",
    "name": "gpt-4-1106-preview",
  },
  {
    "displayName": "gpt-4-vision-preview",
    "name": "gpt-4-vision-preview",
  },
  {
    "displayName": "model1",
    "name": "model1",
  },
  {
    "displayName": "model2",
    "name": "model2",
  },
  {
    "displayName": "model3",
    "name": "model3",
  },
  {
    "displayName": "model4",
    "name": "model4",
  },
]
`;

exports[`settingsSelectors > currentSettings > should merge DEFAULT_SETTINGS and s.settings correctly 1`] = `
{
  "avatar": "avatar.jpg",
  "defaultAgent": {
    "config": {
      "autoCreateTopicThreshold": 2,
      "displayMode": "chat",
      "enableAutoCreateTopic": true,
      "historyCount": 1,
      "model": "gpt-3.5-turbo",
      "params": {
        "frequency_penalty": 0,
        "presence_penalty": 0,
        "temperature": 0.6,
        "top_p": 1,
      },
      "plugins": [],
      "systemRole": "",
      "tts": {
        "showAllLocaleVoice": false,
        "sttLocale": "auto",
        "ttsService": "openai",
        "voice": {
          "openai": "alloy",
        },
      },
    },
    "meta": {
      "avatar": "Default Agent",
      "description": "Default agent for testing",
    },
  },
  "fontSize": 14,
  "language": "en-US",
  "languageModel": {
    "openAI": {
      "OPENAI_API_KEY": "openai-api-key",
      "endpoint": "https://openai-endpoint.com",
      "models": [
        "gpt-3.5-turbo",
      ],
    },
  },
  "neutralColor": "sand",
  "password": "password123",
  "primaryColor": "blue",
  "themeMode": "light",
  "tool": {
    "dalle": {
      "autoGenerate": false,
    },
  },
  "tts": {
    "openAI": {
      "sttModel": "whisper-1",
      "ttsModel": "tts-1",
    },
    "sttAutoStop": true,
    "sttServer": "openai",
  },
}
`;

exports[`settingsSelectors > currentTTS > should merge DEFAULT_TTS_CONFIG and s.settings.tts correctly 1`] = `
{
  "openAI": {
    "sttModel": "whisper-2",
    "ttsModel": "tts-1",
  },
  "sttAutoStop": false,
  "sttServer": "openai",
}
`;

exports[`settingsSelectors > dalleConfig > should return the dalle configuration 1`] = `
{
  "apiKey": "dalle-api-key",
  "autoGenerate": true,
}
`;

exports[`settingsSelectors > defaultAgent > should merge DEFAULT_AGENT and s.settings.defaultAgent correctly 1`] = `
{
  "config": {
    "autoCreateTopicThreshold": 2,
    "displayMode": "chat",
    "enableAutoCreateTopic": true,
    "historyCount": 1,
    "model": "gpt-3.5-turbo",
    "params": {
      "frequency_penalty": 0,
      "presence_penalty": 0,
      "temperature": 0.6,
      "top_p": 1,
    },
    "plugins": [],
    "systemRole": "user",
    "tts": {
      "showAllLocaleVoice": false,
      "sttLocale": "auto",
      "ttsService": "openai",
      "voice": {
        "openai": "alloy",
      },
    },
  },
  "meta": {
    "avatar": "agent-avatar.jpg",
    "description": "Test agent",
  },
}
`;

exports[`settingsSelectors > defaultAgentConfig > should merge DEFAULT_AGENT_CONFIG and defaultAgent(s).config correctly 1`] = `
{
  "autoCreateTopicThreshold": 2,
  "displayMode": "chat",
  "enableAutoCreateTopic": true,
  "historyCount": 1,
  "model": "gpt-3.5-turbo",
  "params": {
    "frequency_penalty": 0,
    "presence_penalty": 0,
    "temperature": 0.7,
    "top_p": 1,
  },
  "plugins": [],
  "systemRole": "user",
  "tts": {
    "showAllLocaleVoice": false,
    "sttLocale": "auto",
    "ttsService": "openai",
    "voice": {
      "openai": "alloy",
    },
  },
}
`;

exports[`settingsSelectors > defaultAgentMeta > should merge DEFAULT_AGENT_META and defaultAgent(s).meta correctly 1`] = `
{
  "avatar": "agent-avatar.jpg",
  "description": "Test agent",
}
`;

Fixed code:
import { createErrorResponse } from '../../errorResponse';
import { createErrorResponse } from '../../errorResponse';
import { createErrorResponse } from '@/app/api/errorResponse';
import { getOpenAIAuthFromRequest } from '@/const/fetch';
import { ChatErrorType, ErrorType } from '@/types/fetch';

import { parserPluginSettings } from './settings';

const checkAuth = (accessCode: string | null) => {
  const { ACCESS_CODES } = getServerConfig();

  // if accessCode doesn't exist
  if (!ACCESS_CODES.length) return { auth: true };

  if (!accessCode || !ACCESS_CODES.includes(accessCode)) {
    return { auth: false, error: ChatErrorType.InvalidAccessCode };
  }

  return { auth: true };
};
const handler = createGatewayOnEdgeRuntime({ defaultPluginSettings, pluginsIndexUrl });

export const POST = async (req: Request) => {
  const { accessCode } = getOpenAIAuthFromRequest(req);

  const result = checkAuth(accessCode);

  if (!result.auth) {
    return createErrorResponse(result.error as ErrorType);
  }

  return handler(req);
};
          router.push(pathString('/chat/settings', { search: location.search }));
import SessionHydration from '@/app/chat/components/SessionHydration';
  <>
    <ResponsiveIndex Mobile={Mobile}>
      <Layout>
        <EditPage />
      </Layout>
    </ResponsiveIndex>
    <SessionHydration />
  </>
      // TODO: we can have a better auth way
      headers: createHeadersWithPluginSettings(settings, createHeaderWithOpenAI()),