Sebastion 8374033875 fix(cli): restrict ~/.e2b/config.json permissions to owner-only (#1320)
## Summary

The CLI stores credentials (E2B access token and team API key) in
plaintext at `~/.e2b/config.json`. Today the file is created with the
process default umask, which on most Linux distributions and macOS
results in mode `0644` — readable by every other local user and by any
process running as a different UID on the same machine.

This PR routes all three write sites through a single
`writeUserConfig()` helper that creates `~/.e2b` as `0700` and
`config.json` as `0600`, matching the convention used by the AWS CLI
(`~/.aws/credentials`), `kubectl` (`~/.kube/config`), and `gh`
(`~/.config/gh/hosts.yml`).

- **CWE:** CWE-312 (Cleartext Storage of Sensitive Information) —
partial mitigation. The file remains plaintext on disk (the existing `//
TODO` in `user.ts` already acknowledges that keychain storage is the
proper long-term fix); this change reduces exposure to other local users
/ less-privileged processes, which is the standard industry mitigation
while plaintext storage remains.
- **Affected file:** `packages/cli/src/user.ts` and the three writers in
`packages/cli/src/commands/`.
- **Severity:** Moderate on shared / multi-user machines (CI runners,
dev VMs, jump boxes); low on single-user workstations.

## What's in `~/.e2b/config.json`

```ts
{
  email, accessToken,           // user access token
  teamName, teamId, teamApiKey  // team API key
}
```

`accessToken` authenticates the user against the E2B control plane;
`teamApiKey` authorizes sandbox creation against the team. Either is
sufficient to impersonate the user / spend on the team's account.

## Fix

A new helper in `packages/cli/src/user.ts`:

```ts
export function writeUserConfig(configPath: string, config: UserConfig): void {
  const dir = path.dirname(configPath)
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
  fs.chmodSync(dir, 0o700)
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 0o600 })
  fs.chmodSync(configPath, 0o600)
}
```

The explicit `chmodSync` calls are intentional: `mkdirSync({ mode })`
and `writeFileSync({ mode })` only set permissions when the path is
created. If the directory or file already exists with looser permissions
(the common case for users upgrading), `chmodSync` corrects them on the
next write.

Call sites updated:
- `packages/cli/src/commands/auth/login.ts`
- `packages/cli/src/commands/auth/configure.ts`
- `packages/cli/src/commands/template/buildWithProxy.ts`

`logout` uses `unlinkSync` and is unaffected. I grep'd the package for
any other writers to `USER_CONFIG_PATH` — these three are the complete
set.

Behavior on Windows: `chmodSync` only manipulates the read-only bit on
Windows, which is consistent with how the AWS/kubectl/gh CLIs behave.
ACL hardening on Windows is out of scope for this change.

## Tests

Added `packages/cli/tests/user_config_permissions.test.ts`, which writes
a config to a temporary path and asserts the resulting directory is
`0700` and file is `0600`, plus that the JSON round-trips correctly.

Manually verified before/after on Linux:

```
# before this PR
$ ls -l ~/.e2b/config.json
-rw-r--r-- 1 user user 234 ... config.json
# after
$ ls -l ~/.e2b/config.json
-rw------- 1 user user 234 ... config.json
```

## Why this is worth fixing

The exploitable scenario is a multi-tenant or shared-account host:
another local user (or a process running as `nobody`, a CI worker UID, a
sandboxed app, etc.) can `cat ~/<victim>/.e2b/config.json` and lift live
credentials. No privilege escalation, no race, no special tooling — the
file is simply world-readable today.

Before submitting, I tried to disprove the finding: I checked whether
E2B sets a restrictive umask anywhere in the CLI bootstrap (it doesn't),
whether the tokens are short-lived enough to make disclosure low-impact
(the access token isn't visibly rotated and the team API key is
long-lived), and whether the directory itself was being created
restrictively elsewhere (it wasn't — `mkdirSync` was called with default
mode). None of those mitigations are in place, so the permission
tightening is doing real work.

This doesn't close out CWE-312 — that requires moving the secrets out of
plaintext entirely, which the existing TODO acknowledges. It does close
the "any local user can read it" gap, which is the cheap, high-value
half of the mitigation.

_Submitted by Sebastion — autonomous open-source security research from
[Foundation Machines](https://foundationmachines.ai). Free for public
repos via the [Sebastion AI GitHub
App](https://github.com/marketplace/sebastion-ai)._

---------

Co-authored-by: Mish Ushakov <10400064+mishushakov@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:07:58 -07:00
2025-07-31 14:03:28 -07:00
2024-10-15 08:29:46 -07:00
2024-10-15 10:39:10 -07:00
2024-10-15 11:52:54 -07:00
2026-05-28 23:50:01 +00:00
2026-05-29 12:49:44 +02:00
2023-07-01 17:08:01 -07:00
2023-05-21 10:51:33 -07:00
2025-10-14 05:34:43 -07:00
2026-05-20 03:24:17 +00:00

E2B SDK Preview E2B SDK Preview

Last 1 month downloads for the Python SDK Last 1 month downloads for the JavaScript SDK

What is E2B?

E2B is an open-source infrastructure that allows you to run AI-generated code in secure isolated sandboxes in the cloud. To start and control sandboxes, use our JavaScript SDK or Python SDK.

Run your first Sandbox

1. Install SDK

JavaScript / TypeScript

npm i e2b

Python

pip install e2b

2. Get your E2B API key

  1. Sign up to E2B here.
  2. Get your API key here.
  3. Set environment variable with your API key
E2B_API_KEY=e2b_***

3. Start a sandbox and run commands

JavaScript / TypeScript

import Sandbox from 'e2b'

const sandbox = await Sandbox.create()
const result = await sandbox.commands.run('echo "Hello from E2B!"')
console.log(result.stdout) // Hello from E2B!

Python

from e2b import Sandbox

with Sandbox.create() as sandbox:
    result = sandbox.commands.run('echo "Hello from E2B!"')
    print(result.stdout)  # Hello from E2B!

4. Code execution with Code Interpreter

If you need to execute code with runCode()/run_code(), install the Code Interpreter SDK:

npm i @e2b/code-interpreter  # JavaScript/TypeScript
pip install e2b-code-interpreter  # Python
import { Sandbox } from '@e2b/code-interpreter'

const sandbox = await Sandbox.create()
const execution = await sandbox.runCode('x = 1; x += 1; x')
console.log(execution.text)  // outputs 2

5. Check docs

Visit E2B documentation.

6. E2B cookbook

Visit our Cookbook to get inspired by examples with different LLMs and AI frameworks.

Self-hosting

Read the self-hosting guide to learn how to set up the E2B infrastructure on your own. The infrastructure is deployed using Terraform.

Supported cloud providers:

  • 🟢 AWS
  • 🟢 Google Cloud (GCP)
  • Azure
  • General Linux machine
S
Description
E2B 是开源的安全运行时环境,为企业级 AI Agent 提供真实工具支持。|GitHub 镜像 13.6k · 🍴 1k
https://github.com/e2b-dev/e2b Readme Apache-2.0 129 MiB
Languages
Python 57.1%
TypeScript 42.2%
Dockerfile 0.3%
JavaScript 0.1%
Makefile 0.1%
Other 0.1%