Skip to content

Bot Tokens (Discord, Slack, Telegram) Bypass OpenShell Credential Isolation - IssueFinder - SN 01 #1333

@dinuduke

Description

@dinuduke

Description

Discord, Slack, and Telegram bot tokens bypass the OpenShell provider system and are injected as plaintext environment variables directly into the sandbox container. The Telegram bridge also injects raw NVIDIA_API_KEY via SSH. This means any process inside the sandbox can read these credentials.
Impact
API key and bot token exposure to any process in the sandbox, including malicious npm packages and agent-triggered code.

Affected Area

  • Module(s): onboard.js, telegram-bridge.js
  • Service(s): Sandbox container, Telegram bridge

Notes
These issues are well-documented at #1109 and #1108. Our audit confirmed the code paths that expose these credentials. The recommended fix is to use the OpenShell provider system for all credentials rather than env var injection.

Exact Fix

File: bin/lib/onboard.js lines 2193-2200

Problem: Bot tokens are copied from host env into the sandbox env object without going through the OpenShell credential provider system.

Current code (lines 2193-2200):

const discordToken = getCredential("DISCORD_BOT_TOKEN") || process.env.DISCORD_BOT_TOKEN;
if (discordToken) {
  sandboxEnv.DISCORD_BOT_TOKEN = discordToken;
}
const slackToken = getCredential("SLACK_BOT_TOKEN") || process.env.SLACK_BOT_TOKEN;
if (slackToken) {
  sandboxEnv.SLACK_BOT_TOKEN = slackToken;
}

Replace with:

// Do NOT inject bot tokens as env vars.
// Instead, store them via OpenShell credential provider:
// openshell credential set DISCORD_BOT_TOKEN <value>
// The gateway will inject them server-side at request time,
// preventing sandbox-side processes from reading the raw token.
const botTokenKeys = ["DISCORD_BOT_TOKEN", "SLACK_BOT_TOKEN", "TELEGRAM_BOT_TOKEN"];
for (const key of botTokenKeys) {
  delete sandboxEnv[key]; // Ensure token is NOT in sandbox env
  const token = getCredential(key) || process.env[key];
  if (token) {
    // Store via OpenShell provider for server-side injection
    runOpenshell(["credential", "set", key, token]);
    note(`  ${key} stored via OpenShell provider (not exposed to sandbox).`);
  }
}

File: scripts/telegram-bridge.js line 112

Problem: NVIDIA_API_KEY passed directly in SSH command string.

Current code (line 112):

const cmd = `export NVIDIA_API_KEY=${shellQuote(API_KEY)} && nemoclaw-start openclaw agent ...`;

Replace with:

// Pass API key via SSH stdin, not command string
const cmd = `read -r NVIDIA_API_KEY && export NVIDIA_API_KEY && nemoclaw-start openclaw agent --agent main --local -m ${shellQuote(message)} --session-id ${shellQuote("tg-" + safeSessionId)}`;

const proc = spawn("ssh", ["-T", "-F", confPath, `openshell-${SANDBOX}`, cmd], {
  timeout: 120000,
  stdio: ["pipe", "pipe", "pipe"],  // Changed: stdin is now 'pipe'
});
proc.stdin.write(API_KEY + "\n");
proc.stdin.end();

Related GitHub Issue Check

Reproduction Steps

  1. Configure a Discord bot token during onboard:
    nemoclaw onboard  # Enter Discord bot token when prompted
  2. After sandbox creation, exec into the sandbox and check environment:
    openshell sandbox exec <sandbox-name> -- env | grep -i 'DISCORD\|SLACK\|TOKEN'
  3. Observe: DISCORD_BOT_TOKEN and SLACK_BOT_TOKEN are visible as environment variables inside the sandbox
  4. Compare: NVIDIA_API_KEY is correctly removed from sandbox env (line 2179 of onboard.js) but bot tokens are not

Environment

  • OS: macOS / Linux
  • NemoClaw Version: v0.1.0
  • Branch: main
  • Runtime: Node.js v22
  • Container / Orchestration Info: Docker sandbox via OpenShell
  • Network Setup: N/A

Debug Output

# Trace the credential injection in onboard.js:
grep -n 'BOT_TOKEN\|DISCORD\|SLACK' bin/lib/onboard.js
# Lines 2180-2186: Bot tokens passed directly to sandbox env

# Compare with NVIDIA_API_KEY handling:
grep -n 'NVIDIA_API_KEY' bin/lib/onboard.js
# Line 2179: delete sandboxEnv.NVIDIA_API_KEY ← correctly scoped
# Bot tokens: no equivalent delete — they leak into sandbox

Logs

# Inside sandbox — bot tokens visible to the agent:
$ env | grep TOKEN
DISCORD_BOT_TOKEN=MTIzNDU2Nzg5MDEyMzQ1Njc4OQ.XXXXXX.xxxxxxxxxx
SLACK_BOT_TOKEN=xoxb-1234567890-1234567890123-xxxxxxxxxxxx
# ↑ These should be passed via OpenShell credential provider, not env vars

Checklist

  • I confirmed this bug is reproducible
  • I searched existing issues and this is not a duplicate

Metadata

Metadata

Assignees

No one assigned

    Labels

    Integration: TelegramUse this label to identify Telegram bot integration issues with NemoClaw.OpenShellSupport for OpenShell, a safe, private runtime for autonomous AI agentsbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions