Merge pull request #261 from mvanhorn/fix/234-secret-env-redaction

fix(server): redact secret-sourced env vars in logs by provenance
This commit is contained in:
Dotta
2026-03-07 21:22:00 -06:00
committed by GitHub
3 changed files with 20 additions and 11 deletions

View File

@@ -245,7 +245,7 @@ export function agentRoutes(db: Db) {
adapterConfig: Record<string, unknown>,
) {
if (adapterType !== "opencode_local") return;
const runtimeConfig = await secretsSvc.resolveAdapterConfigForRuntime(companyId, adapterConfig);
const { config: runtimeConfig } = await secretsSvc.resolveAdapterConfigForRuntime(companyId, adapterConfig);
const runtimeEnv = asRecord(runtimeConfig.env) ?? {};
try {
await ensureOpenCodeModelConfiguredAndAvailable({
@@ -420,7 +420,7 @@ export function agentRoutes(db: Db) {
inputAdapterConfig,
{ strictMode: strictSecretsMode },
);
const runtimeAdapterConfig = await secretsSvc.resolveAdapterConfigForRuntime(
const { config: runtimeAdapterConfig } = await secretsSvc.resolveAdapterConfigForRuntime(
companyId,
normalizedAdapterConfig,
);
@@ -1264,7 +1264,7 @@ export function agentRoutes(db: Db) {
}
const config = asRecord(agent.adapterConfig) ?? {};
const runtimeConfig = await secretsSvc.resolveAdapterConfigForRuntime(agent.companyId, config);
const { config: runtimeConfig } = await secretsSvc.resolveAdapterConfigForRuntime(agent.companyId, config);
const result = await runClaudeLogin({
runId: `claude-login-${randomUUID()}`,
agent: {

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

@@ -308,10 +308,11 @@ export function secretService(db: Db) {
return normalized;
},
resolveEnvBindings: async (companyId: string, envValue: unknown) => {
resolveEnvBindings: async (companyId: string, envValue: unknown): Promise<{ env: Record<string, string>; secretKeys: Set<string> }> => {
const record = asRecord(envValue);
if (!record) return {} as Record<string, string>;
if (!record) return { env: {} as Record<string, string>, secretKeys: new Set<string>() };
const resolved: Record<string, string> = {};
const secretKeys = new Set<string>();
for (const [key, rawBinding] of Object.entries(record)) {
if (!ENV_KEY_RE.test(key)) {
@@ -326,20 +327,22 @@ export function secretService(db: Db) {
resolved[key] = binding.value;
} else {
resolved[key] = await resolveSecretValue(companyId, binding.secretId, binding.version);
secretKeys.add(key);
}
}
return resolved;
return { env: resolved, secretKeys };
},
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 +358,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 };
},
};
}