fix: harden container config DB layer
- config-add/remove-package now rebuild image + restart containers - Deduplicate packages in self-mod install_packages handler - Add runtime whitelist guards for SQL column interpolation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import type { McpServerConfig } from '../../container-config.js';
|
import type { McpServerConfig } from '../../container-config.js';
|
||||||
|
import { buildAgentGroupImage } from '../../container-runner.js';
|
||||||
import { restartAgentGroupContainers } from '../../container-restart.js';
|
import { restartAgentGroupContainers } from '../../container-restart.js';
|
||||||
import {
|
import {
|
||||||
getContainerConfig,
|
getContainerConfig,
|
||||||
@@ -186,6 +187,9 @@ registerResource({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await buildAgentGroupImage(id);
|
||||||
|
restartAgentGroupContainers(id, 'package added via ncl');
|
||||||
|
|
||||||
return { added: { apt: apt || null, npm: npm || null } };
|
return { added: { apt: apt || null, npm: npm || null } };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -214,6 +218,9 @@ registerResource({
|
|||||||
updateContainerConfigJson(id, 'packages_npm', filtered);
|
updateContainerConfigJson(id, 'packages_npm', filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await buildAgentGroupImage(id);
|
||||||
|
restartAgentGroupContainers(id, 'package removed via ncl');
|
||||||
|
|
||||||
return { removed: { apt: apt || null, npm: npm || null } };
|
return { removed: { apt: apt || null, npm: npm || null } };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ export function ensureContainerConfig(agentGroupId: string): void {
|
|||||||
.run(agentGroupId, new Date().toISOString());
|
.run(agentGroupId, new Date().toISOString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SCALAR_COLUMNS = new Set(['provider', 'model', 'effort', 'image_tag', 'assistant_name', 'max_messages_per_prompt']);
|
||||||
|
const JSON_COLUMNS = new Set(['skills', 'mcp_servers', 'packages_apt', 'packages_npm', 'additional_mounts']);
|
||||||
|
|
||||||
/** Update scalar fields on a config row. Only touches fields present in `updates`. */
|
/** Update scalar fields on a config row. Only touches fields present in `updates`. */
|
||||||
export function updateContainerConfigScalars(
|
export function updateContainerConfigScalars(
|
||||||
agentGroupId: string,
|
agentGroupId: string,
|
||||||
@@ -53,6 +56,7 @@ export function updateContainerConfigScalars(
|
|||||||
|
|
||||||
for (const [key, value] of Object.entries(updates)) {
|
for (const [key, value] of Object.entries(updates)) {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
|
if (!SCALAR_COLUMNS.has(key)) throw new Error(`Invalid scalar column: ${key}`);
|
||||||
fields.push(`${key} = @${key}`);
|
fields.push(`${key} = @${key}`);
|
||||||
values[key] = value;
|
values[key] = value;
|
||||||
}
|
}
|
||||||
@@ -73,6 +77,7 @@ export function updateContainerConfigJson(
|
|||||||
column: 'skills' | 'mcp_servers' | 'packages_apt' | 'packages_npm' | 'additional_mounts',
|
column: 'skills' | 'mcp_servers' | 'packages_apt' | 'packages_npm' | 'additional_mounts',
|
||||||
value: unknown,
|
value: unknown,
|
||||||
): void {
|
): void {
|
||||||
|
if (!JSON_COLUMNS.has(column)) throw new Error(`Invalid JSON column: ${column}`);
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
getDb()
|
getDb()
|
||||||
.prepare(`UPDATE container_configs SET ${column} = ?, updated_at = ? WHERE agent_group_id = ?`)
|
.prepare(`UPDATE container_configs SET ${column} = ?, updated_at = ? WHERE agent_group_id = ?`)
|
||||||
|
|||||||
@@ -30,15 +30,19 @@ export const applyInstallPackages: ApprovalHandler = async ({ session, payload,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append new packages to existing lists in the DB
|
// Append new packages to existing lists in the DB (deduplicated)
|
||||||
if (payload.apt) {
|
if (payload.apt) {
|
||||||
const existing = JSON.parse(configRow.packages_apt) as string[];
|
const existing = JSON.parse(configRow.packages_apt) as string[];
|
||||||
existing.push(...(payload.apt as string[]));
|
for (const pkg of payload.apt as string[]) {
|
||||||
|
if (!existing.includes(pkg)) existing.push(pkg);
|
||||||
|
}
|
||||||
updateContainerConfigJson(agentGroup.id, 'packages_apt', existing);
|
updateContainerConfigJson(agentGroup.id, 'packages_apt', existing);
|
||||||
}
|
}
|
||||||
if (payload.npm) {
|
if (payload.npm) {
|
||||||
const existing = JSON.parse(configRow.packages_npm) as string[];
|
const existing = JSON.parse(configRow.packages_npm) as string[];
|
||||||
existing.push(...(payload.npm as string[]));
|
for (const pkg of payload.npm as string[]) {
|
||||||
|
if (!existing.includes(pkg)) existing.push(pkg);
|
||||||
|
}
|
||||||
updateContainerConfigJson(agentGroup.id, 'packages_npm', existing);
|
updateContainerConfigJson(agentGroup.id, 'packages_npm', existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user