{"record":{"id":"1a41164b1c15e758","repo":"nanocoai/nanoclaw","slug":"credential-value-in-contributed-env-key-on","errorCode":null,"errorMessage":"credential value in contributed env '${key}' on ${container.role}","messagePattern":"credential value in contributed env '(.+?)' on (.+?)","errorType":"validation","errorClass":"deniedByPolicy","httpStatus":null,"severity":"critical","filePath":"src/drivers/types.ts","lineNumber":490,"sourceCode":"        throw deniedByPolicy(`identity-material mount ${mount.hostPath} invalid on role ${container.role}`);\n      }\n      if (!mountAllowed(mount, spec, policy)) {\n        throw deniedByPolicy(`mount ${mount.hostPath} violates class ${mount.class} scope ${mount.groupScope}`);\n      }\n    }\n    for (const [key, value] of Object.entries(container.env)) {\n      if (isSecretShaped(key, value)) {\n        throw deniedByPolicy(`secret-shaped env '${key}' on ${container.role}`);\n      }\n    }\n    for (const [key, value] of Object.entries(container.contributedEnv ?? {})) {\n      // The sanctioned lane: credential-shaped NAMES are its purpose — a\n      // provider registering `ANTHROPIC_AUTH_TOKEN=placeholder` for the proxy\n      // to overwrite is the pattern working as intended, and the name check\n      // alone denies every such install. Credential VALUES have no sanctioned\n      // channel, from anyone: real material rides mounts by reference.\n      if (looksLikeCredential(value)) {\n        throw deniedByPolicy(`credential value in contributed env '${key}' on ${container.role}`);\n      }\n    }\n  }\n}\n\n/**\n * The canonical absolute form the mount rules require: rooted, and free of\n * empty, '.' and '..' segments — so the string a prefix rule judges is the\n * path the runtime mounts, and a trusted root cannot be escaped lexically.\n */\nfunction hostPathCanonical(hostPath: string): boolean {\n  if (!hostPath.startsWith('/')) return false;\n  const segments = hostPath.split('/').slice(1);\n  return segments.every((segment) => segment !== '' && segment !== '.' && segment !== '..');\n}\n\n/**\n * The invariant is that no credential VALUE rides in the environment — not that","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/drivers/types.ts#L472-L508","documentation":"A contributedEnv entry's VALUE looks like a real credential. contributedEnv is the sanctioned lane where credential-shaped NAMES are fine (a provider registering ANTHROPIC_AUTH_TOKEN=placeholder for the proxy to overwrite is the intended pattern), so only the name is exempt — the value check still applies, because credential values have no sanctioned channel from anyone; real material rides mounts by reference.","triggerScenarios":"A plugin/provider contribution includes contributedEnv like { ANTHROPIC_AUTH_TOKEN: 'sk-ant-...' } — a plausible-looking placeholder that actually matches the credential-value heuristics (looksLikeCredential). Also any contributed value that happens to be a long token-shaped string.","commonSituations":"Writing a provider integration and putting a real or realistic token in the 'placeholder' slot; test fixtures generating random hex strings long enough to trip the credential heuristic; copying an env block from a working shell session into the contribution.","solutions":["Use an unmistakable placeholder value (e.g. 'placeholder', '__PROXY_FILLED__') — the pattern is that the proxy overwrites it at runtime, so the literal bytes never matter.","If a real credential must reach the container, mount it by reference with the right mount class instead of contributing it as a value.","Adjust test fixture generators to emit short, obviously-fake strings rather than token-shaped randomness."],"exampleFix":"// before\ncontributedEnv: { ANTHROPIC_AUTH_TOKEN: 'sk-ant-api03-a1b2c3d4e5f6...' }\n\n// after\ncontributedEnv: { ANTHROPIC_AUTH_TOKEN: 'placeholder' }","handlingStrategy":"validation","validationCode":"for (const c of spec.containers)\n  for (const [k, v] of Object.entries(c.contributedEnv ?? {}))\n    if (looksLikeCredential(v)) throw new Error(`contributedEnv '${k}' carries a credential-shaped value; use a placeholder`);","typeGuard":"function isSafePlaceholder(v: string): boolean {\n  return !looksLikeCredential(v); // short, non-token-shaped literals","tryCatchPattern":null,"preventionTips":["Use fixed, obviously-fake placeholders ('placeholder', '__PROXY__') in contributedEnv.","Test fixtures: avoid random hex/base64 generators long enough to look like tokens.","Remember only the NAME is exempt in contributedEnv — the value check always applies."],"tags":["secrets","env","security","plugins","policy"],"backgroundTag":"secret-in-environment-variable","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}