fix: make goosed mcp paths container safe

This commit is contained in:
john
2026-06-27 21:31:02 +08:00
parent def9b0b5eb
commit fc5b50c936
7 changed files with 157 additions and 30 deletions
+66 -6
View File
@@ -487,14 +487,15 @@ test('launchExecutor rejects openhands headless without instruction', async () =
});
test('launchExecutor returns friendly error when command is missing', async () => {
const encryptedKey = encryptSecret('sk-x', 'unit-test-secret');
const keyRow = {
id: 'key-1',
provider_id: 'custom_deepseek',
provider_kind: 'builtin',
name: 'DeepSeek',
api_key_ciphertext: encryptSecret('sk-x', 'unit-test-secret').ciphertext,
api_key_iv: encryptSecret('sk-x', 'unit-test-secret').iv,
api_key_tag: encryptSecret('sk-x', 'unit-test-secret').tag,
api_key_ciphertext: encryptedKey.ciphertext,
api_key_iv: encryptedKey.iv,
api_key_tag: encryptedKey.tag,
default_model: 'deepseek-chat',
status: 'active',
is_selected: 1,
@@ -513,13 +514,13 @@ test('launchExecutor returns friendly error when command is missing', async () =
};
const pool = {
async query(sql) {
if (sql.includes('SELECT * FROM h5_llm_executor_bindings WHERE executor = ? AND purpose = ? LIMIT 1')) {
return [[bindingRow]];
}
if (sql.includes('SELECT * FROM h5_llm_executor_bindings')) return [[]];
if (sql.includes('SELECT * FROM h5_llm_provider_keys WHERE id = ?')) {
return [[keyRow]];
}
if (sql.includes('SELECT * FROM h5_llm_executor_bindings WHERE executor = ? AND purpose = ? LIMIT 1')) {
return [[bindingRow]];
}
throw new Error(`Unexpected SQL: ${sql}`);
},
};
@@ -597,6 +598,65 @@ test('syncProfileToGoosed writes provider, model and secret keys for builtin', a
);
});
test('syncSelectedToGoosed writes selected provider to every configured target', async () => {
const encrypted = encryptSecret('sk-test', 'unit-test-secret');
const row = {
id: 'key-deepseek',
provider_id: 'custom_deepseek',
provider_kind: 'builtin',
name: 'DeepSeek',
api_url: null,
base_path: null,
models_json: JSON.stringify(['deepseek-chat']),
goosed_provider_id: null,
engine: 'openai',
relay_provider: null,
api_key_ciphertext: encrypted.ciphertext,
api_key_iv: encrypted.iv,
api_key_tag: encrypted.tag,
default_model: 'deepseek-chat',
status: 'active',
is_selected: 1,
created_at: 1,
updated_at: 1,
};
const pool = {
async query(sql) {
if (sql.includes('SELECT * FROM h5_llm_executor_bindings')) return [[]];
if (sql.includes('is_selected = 1')) return [[row]];
throw new Error(`Unexpected SQL: ${sql}`);
},
};
const calls = [];
const mockFetch = async (url, init) => {
calls.push({ url: String(url), body: JSON.parse(init.body) });
if (String(url).includes('/chat/completions')) {
return {
ok: true,
json: async () => ({ choices: [{ message: { content: 'ok' } }] }),
text: async () => JSON.stringify({ choices: [{ message: { content: 'ok' } }] }),
};
}
return { ok: true, text: async () => '' };
};
const service = createLlmProviderService(pool, {
apiTarget: 'https://127.0.0.1:18006',
apiTargets: ['https://127.0.0.1:18006', 'https://127.0.0.1:18007'],
apiSecret: 'secret',
encryptionKey: 'unit-test-secret',
apiFetchImpl: mockFetch,
});
const result = await service.syncSelectedToGoosed();
assert.equal(result.ok, true);
assert.deepEqual(result.targets, ['https://127.0.0.1:18006', 'https://127.0.0.1:18007']);
assert.deepEqual(
calls
.filter((call) => call.url.includes('/config/upsert') && call.body.key === 'GOOSE_PROVIDER')
.map((call) => new URL(call.url).port),
['18006', '18007'],
);
});
test('syncProfileToGoosed tolerates missing /config/upsert endpoint', async () => {
const mockFetch = async (_url, init) => {
if (String(_url).includes('/config/custom-providers')) {