mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-06 23:20:53 +01:00
Keep owned Docker database credentials in server-only environment
This commit is contained in:
1 parent
1454f54399
commit
96058c7e2f
6 files changed
+51
-9
No files matched your search
@@ -9,7 +9,9 @@ import { assertRunnerDatabaseIsolation, assertEmbeddedDatabaseIsolation } from "
|
||||
import { campaignDatabaseEvidenceEligible, type CampaignAttempt } from "./chat-campaign.js";
|
||||
import { assertChatCatalogFingerprintLane, fingerprintChatDependencyClosure } from "./chat-catalog-fingerprint.js";
|
||||
import { validateRetainedRunnerResult } from "./result-validation.js";
|
||||
import { buildPaperclipServerEnvironment } from "./harness-env.js";
|
||||
import { assertIsolatedServerEnvironment, buildPaperclipServerEnvironment } from "./harness-env.js";
|
||||
import { findSecretLeakInDirectory } from "./redaction.js";
|
||||
import { createSanitizedCodexEnvironment } from "../../packages/paperclip-runner/src/drivers/codex/app-server-transport.js";
|
||||
const roots: string[] = [];
|
||||
beforeEach(() => { vi.stubEnv("DOCKER_HOST", ""); vi.stubEnv("DOCKER_CONTEXT", ""); });
|
||||
afterEach(async () => { vi.unstubAllEnvs(); for (const root of roots.splice(0)) await rm(root, { recursive: true, force: true }); });
|
||||
@@ -71,7 +73,10 @@ it("binds exact image/engine/resources, strips admission data and removes only o
|
||||
expect(supervisor.source[DOCKER_DATABASE_ENV]).toBeUndefined(); expect(supervisor.source[DATABASE_LANE_ENV]).toBeUndefined();
|
||||
const configPath = path.join(temp, "paperclip-home", "instances", "fixture", "config.json");
|
||||
expect(await prepareRunnerE2EServerConfig({ temporaryRoot: temp, configPath, serverPort: 3100, dockerReceiptPath: owned.filename, dockerRun: run })).toBeNull();
|
||||
const config = JSON.parse(await readFile(configPath, "utf8")); expect(config.database.mode).toBe("postgres"); expect(config.database.connectionString).toBe(owned.receipt.connectionString);
|
||||
const config = JSON.parse(await readFile(configPath, "utf8")); expect(config.database.mode).toBe("postgres"); expect(config.database.connectionString).toBeUndefined();
|
||||
expect(JSON.stringify(config)).not.toContain(secrets[0]);
|
||||
expect(supervisor.serverOverrides.DATABASE_URL).toBe(owned.receipt.connectionString);
|
||||
expect(supervisor.source.DATABASE_URL).toBeUndefined();
|
||||
const key = config.secrets.localEncrypted.keyFilePath; await mkdir(path.dirname(key), { recursive: true }); await writeFile(key, "offline-key", { mode: 0o600 });
|
||||
await expect(assertRunnerDatabaseIsolation(configPath, temp, owned.filename, run)).resolves.toBeUndefined();
|
||||
const changed = structuredClone(config); changed.database.connectionString = "postgresql://foreign"; await writeFile(configPath, JSON.stringify(changed), { mode: 0o600 });
|
||||
@@ -183,3 +188,34 @@ it("allows only exact Docker Desktop host translation with an owned canonical pr
|
||||
await rm(password); await writeFile(password, contents, { mode: 0o600 });
|
||||
await owned.close(); expect(state.container).toBeNull(); expect(state.volume).toBeNull();
|
||||
});
|
||||
|
||||
it("keeps owned DB credentials server-only without any persisted scan exemption", async () => {
|
||||
const temp = await root(), { run } = daemon(), secrets: string[] = [];
|
||||
const owned = await startOwnedDockerDatabase({ temporaryRoot: temp, privateSecrets: secrets, run });
|
||||
const admission = await dockerDatabaseSupervisorEnvironment({ temporaryRoot: temp, env: { [DATABASE_LANE_ENV]: "owned-docker-postgres", [DOCKER_DATABASE_ENV]: owned.filename, DATABASE_URL: "ambient-unrelated", DATABASE_MIGRATION_URL: "ambient-migration" }, run });
|
||||
const home = path.join(temp, "paperclip-home"), configPath = path.join(home, "instances", "custody", "config.json");
|
||||
await prepareRunnerE2EServerConfig({ temporaryRoot: temp, configPath, serverPort: 3100, dockerReceiptPath: owned.filename, dockerRun: run });
|
||||
const expected = { temporaryRoot: temp, paperclipHome: home, configPath, ownedDatabaseUrl: admission.serverOverrides.DATABASE_URL };
|
||||
const env = buildPaperclipServerEnvironment(admission.source, { ...admission.serverOverrides, PAPERCLIP_HOME: home, PAPERCLIP_CONFIG: configPath, XDG_CACHE_HOME: path.join(temp, "xdg-cache"), PAPERCLIP_AGENT_JWT_SECRET: "jwt", PAPERCLIP_DECISION_SIGNING_SECRET: "decision", PAPERCLIP_TOOL_ACTION_SIGNING_SECRET: "tool", BETTER_AUTH_SECRET: "auth" });
|
||||
expect(env.DATABASE_URL).toBe(owned.receipt.connectionString);
|
||||
expect(env.DATABASE_MIGRATION_URL).toBeUndefined();
|
||||
expect(() => assertIsolatedServerEnvironment(env, expected)).not.toThrow();
|
||||
expect(() => assertIsolatedServerEnvironment({ ...env, DATABASE_URL: "ambient-unrelated" }, expected)).toThrow();
|
||||
expect(() => assertIsolatedServerEnvironment(env, { ...expected, ownedDatabaseUrl: undefined })).toThrow();
|
||||
expect(createSanitizedCodexEnvironment(env).DATABASE_URL).toBeUndefined();
|
||||
expect(createSanitizedCodexEnvironment(env).DATABASE_MIGRATION_URL).toBeUndefined();
|
||||
expect(await findSecretLeakInDirectory(home, secrets, { includeShapes: false })).toBeNull();
|
||||
const config = JSON.parse(await readFile(configPath, "utf8"));
|
||||
config.database.connectionString = owned.receipt.connectionString;
|
||||
await writeFile(configPath, JSON.stringify(config), { mode: 0o600 });
|
||||
expect(await findSecretLeakInDirectory(home, secrets, { includeShapes: false })).not.toBeNull();
|
||||
await expect(prepareRunnerE2EServerConfig({ temporaryRoot: temp, configPath, serverPort: 3100, dockerReceiptPath: owned.filename, dockerRun: run })).rejects.toThrow("differs");
|
||||
delete config.database.connectionString;
|
||||
config.unrelatedField = secrets[0];
|
||||
await writeFile(configPath, JSON.stringify(config), { mode: 0o600 });
|
||||
expect(await findSecretLeakInDirectory(home, secrets, { includeShapes: false })).not.toBeNull();
|
||||
config.unrelatedField = "provider-secret-canary";
|
||||
await writeFile(configPath, JSON.stringify(config), { mode: 0o600 });
|
||||
expect(await findSecretLeakInDirectory(home, [...secrets, "provider-secret-canary"], { includeShapes: false })).not.toBeNull();
|
||||
await owned.close();
|
||||
});
|
||||
Reference in new issue
Block a user