openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
107 lines (106 loc) • 3.2 kB
JavaScript
//#region src/agents/sandbox/sanitize-env-vars.ts
/**
* Filters environment variables before they cross into sandbox runtimes.
*
* The default path blocks common credential names and suspicious value shapes while allowing
* ordinary process environment needed for shells and Node-based tools.
*/
const BLOCKED_ENV_VAR_PATTERNS = [
/^ANTHROPIC_API_KEY$/i,
/^OPENAI_API_KEY$/i,
/^GEMINI_API_KEY$/i,
/^OPENROUTER_API_KEY$/i,
/^MINIMAX_API_KEY$/i,
/^ELEVENLABS_API_KEY$/i,
/^SYNTHETIC_API_KEY$/i,
/^TELEGRAM_BOT_TOKEN$/i,
/^DISCORD_BOT_TOKEN$/i,
/^SLACK_(BOT|APP)_TOKEN$/i,
/^LINE_CHANNEL_SECRET$/i,
/^LINE_CHANNEL_ACCESS_TOKEN$/i,
/^OPENCLAW_GATEWAY_(TOKEN|PASSWORD)$/i,
/^AWS_(SECRET_ACCESS_KEY|SECRET_KEY|SESSION_TOKEN)$/i,
/^(GH|GITHUB)_TOKEN$/i,
/^(AZURE|AZURE_OPENAI|COHERE|AI_GATEWAY|OPENROUTER)_API_KEY$/i,
/_?(API_KEY|TOKEN|PASSWORD|PRIVATE_KEY|SECRET)$/i
];
const ALLOWED_ENV_VAR_PATTERNS = [
/^LANG$/,
/^LC_.*$/i,
/^PATH$/i,
/^HOME$/i,
/^USER$/i,
/^SHELL$/i,
/^TERM$/i,
/^TZ$/i,
/^NODE_ENV$/i
];
/** Returns a warning or block reason for environment values that look unsafe to forward. */
function validateEnvVarValue(value) {
if (value.includes("\0")) return "Contains null bytes";
if (value.length > 32768) return "Value exceeds maximum length";
if (/^[A-Za-z0-9+/=]{80,}$/.test(value)) return "Value looks like base64-encoded credential data";
}
function matchesAnyPattern(value, patterns) {
return patterns.some((pattern) => pattern.test(value));
}
/** Sanitizes inherited environment variables for automatic sandbox propagation. */
function sanitizeEnvVars(envVars, options = {}) {
const allowed = {};
const blocked = [];
const warnings = [];
const blockedPatterns = [...BLOCKED_ENV_VAR_PATTERNS, ...options.customBlockedPatterns ?? []];
const allowedPatterns = [...ALLOWED_ENV_VAR_PATTERNS, ...options.customAllowedPatterns ?? []];
for (const [rawKey, value] of Object.entries(envVars)) {
const key = rawKey.trim();
if (!key || value === void 0) continue;
if (matchesAnyPattern(key, blockedPatterns)) {
blocked.push(key);
continue;
}
if (options.strictMode && !matchesAnyPattern(key, allowedPatterns)) {
blocked.push(key);
continue;
}
const warning = validateEnvVarValue(value);
if (warning) {
if (warning === "Contains null bytes") {
blocked.push(key);
continue;
}
warnings.push(`${key}: ${warning}`);
}
allowed[key] = value;
}
return {
allowed,
blocked,
warnings
};
}
/** Sanitizes env vars explicitly requested by config, preserving names but still validating values. */
function sanitizeExplicitSandboxEnvVars(envVars) {
const allowed = {};
const blocked = [];
const warnings = [];
for (const [rawKey, value] of Object.entries(envVars)) {
const key = rawKey.trim();
if (!key || value === void 0) continue;
const warning = validateEnvVarValue(value);
if (warning) {
if (warning === "Contains null bytes") {
blocked.push(key);
continue;
}
warnings.push(`${key}: ${warning}`);
}
allowed[key] = value;
}
return {
allowed,
blocked,
warnings
};
}
//#endregion
export { sanitizeExplicitSandboxEnvVars as n, validateEnvVarValue as r, sanitizeEnvVars as t };