Summary
Secrets are written into env.sh as export VAR=\$(echo 'base64...' | base64 -d). The base64-encoded value is visible in /proc/[pid]/cmdline of the base64 subprocess for the duration of decoding.
Where
packages/runtime/src/band-server.ts:519-522
for (const [key, value] of Object.entries(req.secrets ?? {})) {
const b64 = Buffer.from(value).toString("base64");
envLines.push(\`export \${key}=\$(echo '\${b64}' | base64 -d)\`);
}
Impact
Inside the sandbox, any process walking /proc/[pid]/cmdline during script startup sees the encoded secret. The decode is trivial. Also, if a script enables set -x, the decoded secret is printed to stderr.
This is limited to processes the band-runner uid can see, but /proc is fully mounted (see #28), so any subprocess of the script can observe sibling subprocess cmdlines.
Fix sketch
Write the secret to a 0600 file in the workdir and read it without command substitution:
export VAR=\"\$(< \$WORKDIR/secrets/VAR)\"
Or use bash read from a heredoc to avoid the file:
read -r VAR <<< \"actual-secret-value\"
export VAR
Or pass secrets to the script's stdin / a pipe, never via the env.sh that becomes a process argument.
Summary
Secrets are written into
env.shasexport VAR=\$(echo 'base64...' | base64 -d). The base64-encoded value is visible in/proc/[pid]/cmdlineof thebase64subprocess for the duration of decoding.Where
packages/runtime/src/band-server.ts:519-522Impact
Inside the sandbox, any process walking
/proc/[pid]/cmdlineduring script startup sees the encoded secret. The decode is trivial. Also, if a script enablesset -x, the decoded secret is printed to stderr.This is limited to processes the band-runner uid can see, but
/procis fully mounted (see #28), so any subprocess of the script can observe sibling subprocess cmdlines.Fix sketch
Write the secret to a 0600 file in the workdir and read it without command substitution:
Or use bash
readfrom a heredoc to avoid the file:Or pass secrets to the script's stdin / a pipe, never via the env.sh that becomes a process argument.