fix(server): redact secret-sourced env vars in run logs by provenance

resolveAdapterConfigForRuntime now returns a secretKeys set tracking
which env vars came from secret_ref bindings. The onAdapterMeta
callback uses this to redact them regardless of key name.

Fixes #234

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-03-07 16:04:09 -08:00
parent 63a876ca3c
commit 977f5570be
3 changed files with 15 additions and 8 deletions

View File

@@ -1240,11 +1240,16 @@ export function heartbeatService(db: Db) {
const mergedConfig = issueAssigneeOverrides?.adapterConfig
? { ...config, ...issueAssigneeOverrides.adapterConfig }
: config;
const resolvedConfig = await secretsSvc.resolveAdapterConfigForRuntime(
const { config: resolvedConfig, secretKeys } = await secretsSvc.resolveAdapterConfigForRuntime(
agent.companyId,
mergedConfig,
);
const onAdapterMeta = async (meta: AdapterInvocationMeta) => {
if (meta.env && secretKeys.size > 0) {
for (const key of secretKeys) {
if (key in meta.env) meta.env[key] = "***REDACTED***";
}
}
await appendRunEvent(currentRun, seq++, {
eventType: "adapter.invoke",
stream: "system",

View File

@@ -331,15 +331,16 @@ export function secretService(db: Db) {
return resolved;
},
resolveAdapterConfigForRuntime: async (companyId: string, adapterConfig: Record<string, unknown>) => {
resolveAdapterConfigForRuntime: async (companyId: string, adapterConfig: Record<string, unknown>): Promise<{ config: Record<string, unknown>; secretKeys: Set<string> }> => {
const resolved = { ...adapterConfig };
const secretKeys = new Set<string>();
if (!Object.prototype.hasOwnProperty.call(adapterConfig, "env")) {
return resolved;
return { config: resolved, secretKeys };
}
const record = asRecord(adapterConfig.env);
if (!record) {
resolved.env = {};
return resolved;
return { config: resolved, secretKeys };
}
const env: Record<string, string> = {};
for (const [key, rawBinding] of Object.entries(record)) {
@@ -355,10 +356,11 @@ export function secretService(db: Db) {
env[key] = binding.value;
} else {
env[key] = await resolveSecretValue(companyId, binding.secretId, binding.version);
secretKeys.add(key);
}
}
resolved.env = env;
return resolved;
return { config: resolved, secretKeys };
},
};
}