mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-23 01:48:11 +00:00
fix: prune Kilo provider models in configure flow
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Config/Kilo Gateway: in `openclaw configure`, prune persisted `models.providers.kilocode.models` to selected Kilo allowlist entries (including empty Kilo selections) only during Kilo auth flow, preserving non-Kilo auth behavior. (#24921) thanks @gumadeiras.
|
||||
- Security/Config writes: block reserved prototype keys in account-id normalization and route account config resolution through own-key lookups, hardening `/allowlist` and account-scoped config paths against prototype-chain pollution.
|
||||
- Security/Exec: harden `safeBins` long-option validation by rejecting unknown/ambiguous GNU long-option abbreviations and denying sort filesystem-dependent flags (`--random-source`, `--temporary-directory`, `-T`), closing safe-bin denylist bypasses. Thanks @jiseoung.
|
||||
- Security/Channels: unify dangerous name-matching policy checks (`dangerouslyAllowNameMatching`) across core and extension channels, share mutable-allowlist detectors between `openclaw doctor` and `openclaw security audit`, and scan all configured accounts (not only the default account) in channel security audit findings.
|
||||
|
||||
@@ -128,4 +128,64 @@ describe("promptAuthConfig", () => {
|
||||
"MiniMax-M2.1",
|
||||
]);
|
||||
});
|
||||
|
||||
it("prunes Kilo provider models to empty when Kilo auth selection has no Kilo models", async () => {
|
||||
mocks.promptAuthChoiceGrouped.mockResolvedValue("kilocode-api-key");
|
||||
mocks.applyAuthChoice.mockResolvedValue({
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "kilocode/anthropic/claude-opus-4.6" },
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
kilocode: {
|
||||
baseUrl: "https://api.kilo.ai/api/gateway/",
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{ id: "anthropic/claude-opus-4.6", name: "Claude Opus 4.6" },
|
||||
{ id: "minimax/minimax-m2.5:free", name: "MiniMax M2.5 (Free)" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
mocks.promptModelAllowlist.mockResolvedValue({
|
||||
models: ["openai/gpt-5.2"],
|
||||
});
|
||||
|
||||
const result = await promptAuthConfig({}, makeRuntime(), noopPrompter);
|
||||
expect(result.models?.providers?.kilocode?.models).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not prune Kilo provider models outside Kilo auth flow", async () => {
|
||||
mocks.promptAuthChoiceGrouped.mockResolvedValue("token");
|
||||
mocks.applyAuthChoice.mockResolvedValue({
|
||||
config: {
|
||||
models: {
|
||||
providers: {
|
||||
kilocode: {
|
||||
baseUrl: "https://api.kilo.ai/api/gateway/",
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{ id: "anthropic/claude-opus-4.6", name: "Claude Opus 4.6" },
|
||||
{ id: "minimax/minimax-m2.5:free", name: "MiniMax M2.5 (Free)" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
mocks.promptModelAllowlist.mockResolvedValue({
|
||||
models: ["openai/gpt-5.2"],
|
||||
});
|
||||
|
||||
const result = await promptAuthConfig({}, makeRuntime(), noopPrompter);
|
||||
expect(result.models?.providers?.kilocode?.models?.map((model) => model.id)).toEqual([
|
||||
"anthropic/claude-opus-4.6",
|
||||
"minimax/minimax-m2.5:free",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -127,7 +127,11 @@ export async function promptAuthConfig(
|
||||
});
|
||||
if (allowlistSelection.models) {
|
||||
next = applyModelAllowlist(next, allowlistSelection.models);
|
||||
next = pruneKilocodeProviderModelsToAllowlist(next, allowlistSelection.models);
|
||||
if (authChoice === "kilocode-api-key") {
|
||||
next = pruneKilocodeProviderModelsToAllowlist(next, allowlistSelection.models, {
|
||||
pruneWhenNoKilocodeSelection: true,
|
||||
});
|
||||
}
|
||||
next = applyModelFallbacksFromSelection(next, allowlistSelection.models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,4 +318,27 @@ describe("pruneKilocodeProviderModelsToAllowlist", () => {
|
||||
"MiniMax-M2.5",
|
||||
]);
|
||||
});
|
||||
|
||||
it("can prune Kilo provider models to empty when configured", () => {
|
||||
const config = {
|
||||
models: {
|
||||
providers: {
|
||||
kilocode: {
|
||||
baseUrl: "https://api.kilo.ai/api/gateway/",
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
makeProviderModel("anthropic/claude-opus-4.6", "Claude Opus 4.6"),
|
||||
makeProviderModel("minimax/minimax-m2.5:free", "MiniMax M2.5 (Free)"),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const next = pruneKilocodeProviderModelsToAllowlist(config, ["openai/gpt-5.2"], {
|
||||
pruneWhenNoKilocodeSelection: true,
|
||||
});
|
||||
|
||||
expect(next.models?.providers?.kilocode?.models).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -552,6 +552,9 @@ export function applyModelAllowlist(cfg: OpenClawConfig, models: string[]): Open
|
||||
export function pruneKilocodeProviderModelsToAllowlist(
|
||||
cfg: OpenClawConfig,
|
||||
selectedModels: string[],
|
||||
opts?: {
|
||||
pruneWhenNoKilocodeSelection?: boolean;
|
||||
},
|
||||
): OpenClawConfig {
|
||||
const normalized = normalizeModelKeys(selectedModels);
|
||||
if (normalized.length === 0) {
|
||||
@@ -564,8 +567,8 @@ export function pruneKilocodeProviderModelsToAllowlist(
|
||||
|
||||
const selectedByProvider = selectedModelIdsByProvider(normalized);
|
||||
// Keep this scoped to Kilo Gateway: do not mutate other providers here.
|
||||
const selectedKilocodeIds = selectedByProvider.get("kilocode");
|
||||
if (!selectedKilocodeIds || selectedKilocodeIds.size === 0) {
|
||||
const selectedKilocodeIds = selectedByProvider.get("kilocode") ?? new Set<string>();
|
||||
if (selectedKilocodeIds.size === 0 && !(opts?.pruneWhenNoKilocodeSelection ?? false)) {
|
||||
return cfg;
|
||||
}
|
||||
let mutated = false;
|
||||
|
||||
Reference in New Issue
Block a user