Skip to content

Discord, Slack, and Telegram bot tokens bypass OpenShell provider system — injected as plaintext env vars into sandbox #1109

@zredlined

Description

@zredlined

Summary

During sandbox creation, Discord and Slack bot tokens are passed directly into the sandbox as plaintext environment variables (onboard.js:1810-1817). The Telegram bot token follows the same pattern via the bridge's SSH command (telegram-bridge.js:108, covered in #1108). These tokens are readable by any process inside the sandbox via printenv or /proc/self/environ.

const sandboxEnv = { ...process.env };
delete sandboxEnv.NVIDIA_API_KEY;           // correctly removed for inference
const discordToken = getCredential("DISCORD_BOT_TOKEN") || process.env.DISCORD_BOT_TOKEN;
if (discordToken) {
  sandboxEnv.DISCORD_BOT_TOKEN = discordToken;  // raw secret injected
}
const slackToken = getCredential("SLACK_BOT_TOKEN") || process.env.SLACK_BOT_TOKEN;
if (slackToken) {
  sandboxEnv.SLACK_BOT_TOKEN = slackToken;  // raw secret injected
}

Why this matters

A compromised or misaligned agent inside the sandbox can:

  1. Read the real bot token from the environment
  2. Use the allowed egress path to the messaging service (the Telegram, Discord, and Slack policies already exist in openclaw-sandbox.yaml) to exfiltrate data or impersonate the bot
  3. Use the token to interact with messaging APIs beyond what the agent is supposed to do

The whole point of the OpenShell provider system is that the sandbox process should never see the real credential value.

How OpenShell's provider system handles this

OpenShell's SecretResolver replaces real credential values with opaque placeholders in the sandbox process environment. The flow:

  1. openshell provider create --name telegram-bridge --type webhook --credential TELEGRAM_BOT_TOKEN stores the token server-side in the gateway
  2. When the sandbox starts, the runtime fetches credentials via gRPC and replaces each value with openshell:resolve:env:TELEGRAM_BOT_TOKEN in the child process
  3. When the sandbox process makes an HTTP request to api.telegram.org with the placeholder in a header, the L7 proxy (which already has tls: terminate on the Telegram policy) rewrites the header to contain the real token — only in the outbound wire bytes
  4. printenv TELEGRAM_BOT_TOKEN inside the sandbox shows openshell:resolve:env:TELEGRAM_BOT_TOKEN — never the real value

This mechanism is completely generic. The provider type field is free-form and the credential injection pipeline works for any key-value pair. No OpenShell code changes are needed — the same system that handles NVIDIA_API_KEY and ANTHROPIC_API_KEY works for TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, and SLACK_BOT_TOKEN today.

Suggested fix

Replace raw env var injection with provider registration for all messaging credentials:

# During onboard or service setup — credentials stored server-side
openshell provider create --name telegram-creds --type webhook --credential TELEGRAM_BOT_TOKEN
openshell provider create --name discord-creds  --type webhook --credential DISCORD_BOT_TOKEN
openshell provider create --name slack-creds    --type webhook --credential SLACK_BOT_TOKEN

# Attach providers to the sandbox at creation
openshell sandbox create \
  --provider nvidia-prod \
  --provider telegram-creds \
  --provider discord-creds \
  --provider slack-creds \
  ...

Then remove these lines from onboard.js:

// DELETE — no longer needed; provider system handles injection
sandboxEnv.DISCORD_BOT_TOKEN = discordToken;
sandboxEnv.SLACK_BOT_TOKEN = slackToken;

The existing L7 network policies in openclaw-sandbox.yaml for Telegram, Discord, and Slack already use protocol: rest with tls: terminate, which is exactly what's needed for the proxy to perform header rewriting.

Relationship to existing issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions