fix(security): restrict local connector endpoints
This commit is contained in:
1 parent
fa19e95f19
commit
84b92c859e
12 files changed
+158
-18
No files matched your search
@@ -25,7 +25,7 @@ export const LOCAL_CONNECTORS: ConnectorDefinition[] = [
|
||||
defaultModel: "llama3.2",
|
||||
auth: { type: "local", defaultBaseUrl: "http://localhost:11434/v1" },
|
||||
notes:
|
||||
"Ollama exposes /v1/chat/completions and /v1/models. The default port is 11434; override if you launched `ollama serve` on a different address.",
|
||||
"Ollama exposes /v1/chat/completions and /v1/models. The default port is 11434; override with a loopback or private-network address.",
|
||||
},
|
||||
{
|
||||
id: "lm-studio",
|
||||
@@ -36,6 +36,6 @@ export const LOCAL_CONNECTORS: ConnectorDefinition[] = [
|
||||
defaultModel: "local-model",
|
||||
auth: { type: "local", defaultBaseUrl: "http://localhost:1234/v1" },
|
||||
notes:
|
||||
"Enable LM Studio's local server from the Developer tab; the default port is 1234. Model ids match whatever you have loaded.",
|
||||
"Enable LM Studio's local server from the Developer tab; the default port is 1234. Use a loopback or private-network address.",
|
||||
},
|
||||
];
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
||||
import { getEnvApiKey } from "@mariozechner/pi-ai";
|
||||
import { isLocalConnectorBaseUrl } from "@shared/contracts/schemas-credentials";
|
||||
import { getConnectorDefinition, isPiKnownProvider } from "@main/connectors";
|
||||
import { UpstreamError } from "../errors/index.js";
|
||||
import { CredentialStore } from "./credential-store";
|
||||
@@ -150,6 +151,13 @@ export class CredentialResolver {
|
||||
if (stored?.kind !== "local") {
|
||||
throw new CredentialError("CREDENTIAL_MISSING", connectorId);
|
||||
}
|
||||
if (!isLocalConnectorBaseUrl(stored.value.baseUrl)) {
|
||||
throw new CredentialError(
|
||||
"CREDENTIAL_INVALID",
|
||||
connectorId,
|
||||
"Local connector baseUrl must use localhost, loopback, or a private IP host.",
|
||||
);
|
||||
}
|
||||
// Ollama and LM Studio ignore the bearer value but the OpenAI SDK
|
||||
// refuses an empty string, so we hand it the connector id as a
|
||||
// stable non-empty placeholder. The actual transport is keyed off
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import log from "electron-log/main.js";
|
||||
import { getModels, type Api, type Model } from "@mariozechner/pi-ai";
|
||||
import { isLocalConnectorBaseUrl } from "@shared/contracts/schemas-credentials";
|
||||
import { isPiKnownProvider } from "./connectors";
|
||||
|
||||
// Fireworks' OpenAI-compatible root. The OpenAI SDK appends
|
||||
@@ -125,6 +126,10 @@ export function resolveModel(
|
||||
log.warn(`[model-resolver] ${provider} requires a baseUrl from credentials; none provided.`);
|
||||
return null;
|
||||
}
|
||||
if (!isLocalConnectorBaseUrl(options.baseUrl)) {
|
||||
log.warn(`[model-resolver] ${provider} rejected non-local baseUrl.`);
|
||||
return null;
|
||||
}
|
||||
return buildLocalModel(provider, modelId, options.baseUrl);
|
||||
}
|
||||
if (!isPiKnownProvider(provider)) {
|
||||
@@ -221,6 +226,10 @@ export function resolveModels(provider: string): Array<{ id: string; name: strin
|
||||
export async function fetchLocalConnectorModels(
|
||||
baseUrl: string,
|
||||
): Promise<Array<{ id: string; name: string }>> {
|
||||
if (!isLocalConnectorBaseUrl(baseUrl)) {
|
||||
log.warn("[model-resolver] rejected non-local model catalog baseUrl.");
|
||||
return [];
|
||||
}
|
||||
const url = `${baseUrl.replace(/\/$/, "")}/models`;
|
||||
try {
|
||||
const response = await fetch(url, { method: "GET" });
|
||||
|
||||
Reference in new issue
Block a user