refactor: share portable env entry normalization

This commit is contained in:
Peter Steinberger 2026-03-13 21:11:34 +00:00
parent ef15600b3e
commit 84a50acb55
1 changed files with 21 additions and 22 deletions

View File

@ -80,6 +80,23 @@ export function isDangerousHostEnvOverrideVarName(rawKey: string): boolean {
return HOST_DANGEROUS_OVERRIDE_ENV_PREFIXES.some((prefix) => upper.startsWith(prefix)); return HOST_DANGEROUS_OVERRIDE_ENV_PREFIXES.some((prefix) => upper.startsWith(prefix));
} }
function listNormalizedPortableEnvEntries(
source: Record<string, string | undefined>,
): Array<[string, string]> {
const entries: Array<[string, string]> = [];
for (const [rawKey, value] of Object.entries(source)) {
if (typeof value !== "string") {
continue;
}
const key = normalizeEnvVarKey(rawKey, { portable: true });
if (!key) {
continue;
}
entries.push([key, value]);
}
return entries;
}
export function sanitizeHostExecEnv(params?: { export function sanitizeHostExecEnv(params?: {
baseEnv?: Record<string, string | undefined>; baseEnv?: Record<string, string | undefined>;
overrides?: Record<string, string> | null; overrides?: Record<string, string> | null;
@ -90,12 +107,8 @@ export function sanitizeHostExecEnv(params?: {
const blockPathOverrides = params?.blockPathOverrides ?? true; const blockPathOverrides = params?.blockPathOverrides ?? true;
const merged: Record<string, string> = {}; const merged: Record<string, string> = {};
for (const [rawKey, value] of Object.entries(baseEnv)) { for (const [key, value] of listNormalizedPortableEnvEntries(baseEnv)) {
if (typeof value !== "string") { if (isDangerousHostEnvVarName(key)) {
continue;
}
const key = normalizeEnvVarKey(rawKey, { portable: true });
if (!key || isDangerousHostEnvVarName(key)) {
continue; continue;
} }
merged[key] = value; merged[key] = value;
@ -105,14 +118,7 @@ export function sanitizeHostExecEnv(params?: {
return markOpenClawExecEnv(merged); return markOpenClawExecEnv(merged);
} }
for (const [rawKey, value] of Object.entries(overrides)) { for (const [key, value] of listNormalizedPortableEnvEntries(overrides)) {
if (typeof value !== "string") {
continue;
}
const key = normalizeEnvVarKey(rawKey, { portable: true });
if (!key) {
continue;
}
const upper = key.toUpperCase(); const upper = key.toUpperCase();
// PATH is part of the security boundary (command resolution + safe-bin checks). Never allow // PATH is part of the security boundary (command resolution + safe-bin checks). Never allow
// request-scoped PATH overrides from agents/gateways. // request-scoped PATH overrides from agents/gateways.
@ -140,14 +146,7 @@ export function sanitizeSystemRunEnvOverrides(params?: {
return overrides; return overrides;
} }
const filtered: Record<string, string> = {}; const filtered: Record<string, string> = {};
for (const [rawKey, value] of Object.entries(overrides)) { for (const [key, value] of listNormalizedPortableEnvEntries(overrides)) {
if (typeof value !== "string") {
continue;
}
const key = normalizeEnvVarKey(rawKey, { portable: true });
if (!key) {
continue;
}
if (!HOST_SHELL_WRAPPER_ALLOWED_OVERRIDE_ENV_KEYS.has(key.toUpperCase())) { if (!HOST_SHELL_WRAPPER_ALLOWED_OVERRIDE_ENV_KEYS.has(key.toUpperCase())) {
continue; continue;
} }