Skip to content

Studio: Bypass Permissions (skip confirmation, disable tool sandbox)#5895

Merged
danielhanchen merged 31 commits into
mainfrom
bypass-permissions
Jun 15, 2026
Merged

Studio: Bypass Permissions (skip confirmation, disable tool sandbox)#5895
danielhanchen merged 31 commits into
mainfrom
bypass-permissions

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Builds on #5869 (the Confirm tool calls gate). This PR is based on the tool-call-confirmation branch so the diff shows only the Bypass Permissions changes. Merge #5869 first, then I will retarget this to main.

What this adds

An opt-in "Bypass Permissions" toggle in chat settings, next to "Confirm tool calls". When on:

  • No tool call shows a confirmation prompt (this takes precedence over Confirm tool calls).
  • The python/terminal execution sandbox is disabled: the Python AST safety checks, the shell command blocklist, and the resource limits are skipped, and tools inherit the host environment.

Default off, so current behavior is unchanged.

Scope

  • The no-confirmation behavior applies to all tools (web search, python, terminal, MCP), since the gate sits ahead of every tool.
  • The sandbox disable only affects the local code tools (python, terminal), which are the only sandboxed tools. Web search keeps its SSRF and host checks, and MCP tools are remote.

Secrets stay hidden

Even in bypass mode, credential-bearing env vars (HF_TOKEN, AWS_*, OPENAI_API_KEY, and similar) are stripped from the tool environment, and HOME is repointed at the per-session sandbox folder so the HF, aws, and wandb SDKs do not auto-read cached credentials. On-disk files stay reachable because the filesystem is unrestricted by design.

Safety detail

Bypass keeps a minimal setsid-only pre-exec. This is required, not optional: _kill_process_tree sends SIGKILL to the child process group, so without a fresh session a timeout or cancel would kill the Studio server itself. Per-call timeouts and cancellation are kept too.

UX

Enabling the toggle opens a warning that must be accepted every time:

Bypass Permissions is dangerous since the AI model might delete, corrupt your machine, and or cause real world damage to you or the world - only accept if you are certain

While on, "Confirm tool calls" is disabled and shown as overridden.

Backend flow

bypass_permissions is a per-request field on the chat completion request. The route enforces precedence (confirm_tool_calls = confirm and not bypass) and threads bypass_permissions into the GGUF and safetensors tool loops, which pass disable_sandbox to the tool executor.

Tests

  • New test_bypass_permissions.py: the secret-name classifier, both env builders, the disable_sandbox branch of python and terminal exec (env, pre-exec, skipped safety checks and blocklist), a real subprocess check that the host env is visible while secrets are stripped, the request-model default, the precedence rule, and that the loop forwards disable_sandbox without gating.
  • Existing tool-confirm and safetensors loop tests still pass; their fakes were updated for the new execute_tool keyword.
  • Frontend typecheck and build pass.

oobabooga and others added 6 commits May 29, 2026 11:55
The Allow / Always allow / Deny controls only lived in the fallback tool
card, but the built-in tools (web search, python, terminal, code
execution, image generation) render with their own components and so
never showed the buttons. Those calls paused after tool_start with no way
to approve them, hanging until the 1 hour timeout. Only MCP tools, which
use the fallback renderer, actually worked.

Render the controls for every tool card by wrapping each registered tool
component (and the fallback) in thread.tsx with a shared
ToolConfirmationControls, so the gate applies uniformly.

Also make the handshake robust:
- The gate keys on a per-call approval_id minted by the backend and
  echoed in tool_start, instead of session_id alone, so a stale or
  concurrent confirmation can no longer resolve the wrong call.
- The approval slot is registered before tool_start is yielded, closing
  the race where a fast click or an auto "Always allow" could reach the
  backend before the waiter existed.
- The frontend resolves with the same session id the request was sent
  with (plus the approval_id), fixing the new-thread mismatch where the
  confirmation targeted a different session than the blocked stream.
- The confirm endpoint returns {resolved}; the UI keeps the buttons and
  shows a retry hint until the backend confirms a match, instead of
  hiding them on a failed or mistargeted post.
- The gate runs after the disabled-tool and duplicate-call checks, so a
  call that will not execute is not put up for approval. A denied call is
  still excluded from duplicate detection, so re-issuing and approving it
  works.
- "Always allow" is scoped per session to match the backend gate.

Add backend tests for the approval registry, the SSE no-deadlock
handshake, and the loop integration (allow, deny, disabled, duplicate,
re-issue after deny).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a 'Bypass Permissions' feature that allows users to skip tool-call confirmations and disable the execution sandbox for Python and terminal tools, while still stripping sensitive environment variables. The changes span backend execution logic, API routes, and frontend settings, along with a comprehensive suite of tests. Feedback on these changes highlights two key improvements: explicitly defining the bypass_permissions field on the Anthropic request model to prevent potential validation or attribute errors, and ensuring Windows compatibility by repointing the TEMP and TMP environment variables to the sandbox working directory.

auto_heal_tool_calls = True,
tool_call_timeout = 300,
session_id = payload.session_id,
bypass_permissions = bool(payload.bypass_permissions),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Instead of using getattr and checking model_extra to safely retrieve bypass_permissions, define bypass_permissions explicitly as an Optional field with a default value (e.g., None or False) on the AnthropicMessagesRequest Pydantic model. This prevents potential validation issues or AttributeError crashes and ensures the model accurately reflects the fields that can be sent by the frontend.

Suggested change
bypass_permissions = bool(payload.bypass_permissions),
bypass_permissions = payload.bypass_permissions or False,
References
  1. Ensure Pydantic models for API requests mark fields as Optional with a default of None if they can be omitted by the frontend, to prevent validation errors and avoid relying on model_extra workarounds.

Comment on lines +370 to +372
"PRIVATE_KEY",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

On Windows, Python's tempfile module and other SDKs/tools respect TEMP and TMP environment variables instead of TMPDIR. Since _build_bypass_env inherits the host's environment, the host's real TEMP and TMP variables will remain in the environment and take precedence over TMPDIR. This causes temporary files to be written to the host's real temp directory instead of the sandbox workdir. Repoint TEMP and TMP to workdir as well.

Suggested change
"PRIVATE_KEY",
)
env = {k: v for k, v in os.environ.items() if not _is_secret_env_name(k)}
env["HOME"] = workdir
env["TMPDIR"] = workdir
env["TEMP"] = workdir
env["TMP"] = workdir

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d2c8de0ed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

auto_heal_tool_calls = True,
tool_call_timeout = 300,
session_id = payload.session_id,
bypass_permissions = bool(payload.bypass_permissions),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Default bypass_permissions on Anthropic requests

When the Anthropic-compatible /v1/messages path runs server-side tools and the client does not send the new extension, this access raises AttributeError because AnthropicMessagesRequest does not declare bypass_permissions (I verified an omitted field is not present on the model despite extra='allow'). That makes ordinary tool-enabled Anthropic requests 500 unless they explicitly include this new nonstandard field; use getattr(payload, 'bypass_permissions', False) or add the field to the request model.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9dee0e8865

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread studio/backend/core/inference/tools.py Outdated
"""Env for bypass exec: full host env (unrestricted) minus credential vars,
with HOME/TMPDIR repointed at the workdir so SDKs cannot read cached creds.
"""
env = {k: v for k, v in os.environ.items() if not _is_secret_env_name(k)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip credential broker variables from bypass env

When bypass mode is used on a host with an SSH agent, this copies SSH_AUTH_SOCK because the name is not matched by _is_secret_env_name, so a model-run terminal command such as git/ssh can authenticate with the operator's loaded SSH keys even though the feature promises credential-bearing environment access is stripped. Please explicitly drop broker/location variables such as SSH_AUTH_SOCK before exposing the host environment in bypass mode.

Useful? React with 👍 / 👎.

imageToolsEnabled: loadBool(CHAT_IMAGE_TOOLS_ENABLED_KEY, false),
mcpEnabledForChat: loadBool(CHAT_MCP_ENABLED_KEY, false),
confirmToolCalls: loadBool(CHAT_CONFIRM_TOOL_CALLS_KEY, false),
bypassPermissions: loadBool(CHAT_BYPASS_PERMISSIONS_KEY, false),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require confirmation after reload before bypass stays active

Because bypassPermissions is loaded directly from localStorage at startup, once a user enables it a browser reload or later Studio session silently starts with the sandbox disabled and tool confirmations bypassed, without showing the warning dialog again. For this high-risk setting, avoid persisting the enabled state or gate the restored value behind the same acceptance flow before requests can send bypass_permissions: true.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df140669c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread studio/backend/core/inference/tools.py Outdated
"""Env for bypass exec: full host env (unrestricted) minus credential vars,
with HOME/TMPDIR repointed at the workdir so SDKs cannot read cached creds.
"""
env = {k: v for k, v in os.environ.items() if not _is_secret_env_name(k)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip credential-bearing URL env vars in bypass

When bypass mode runs on hosts that use credentialed package indexes or proxies, variables such as PIP_INDEX_URL, PIP_EXTRA_INDEX_URL, or HTTP_PROXY are copied here because their names do not match _is_secret_env_name; those values commonly embed user:token@..., so a bypassed python/terminal tool can simply print the environment and recover credentials despite the feature promising secrets are stripped. Please switch to an allowlist or also scrub URL userinfo / known credential-bearing URL variables before exposing the host env.

Useful? React with 👍 / 👎.

oobabooga and others added 3 commits May 31, 2026 10:53
Adds an opt-in Bypass Permissions toggle next to Confirm tool calls. When on,
no tool call shows a confirmation prompt and the python/terminal sandbox is
disabled: safety checks, command blocklist, and resource limits are skipped.
Secret env vars are still stripped and HOME stays repointed at the session
workdir. Default off keeps current behavior, and it takes precedence over
Confirm tool calls. Enabling it requires accepting a warning each time.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57f84569f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread studio/backend/core/inference/tools.py Outdated
"""Env for bypass exec: full host env (unrestricted) minus credential vars,
with HOME/TMPDIR repointed at the workdir so SDKs cannot read cached creds.
"""
env = {k: v for k, v in os.environ.items() if not _is_secret_env_name(k)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent bypass tools from reading parent env via /proc

On Linux hosts with the default same-user /proc visibility, stripping secrets only from the child env is not enough once bypass disables the code/file safety checks: a bypassed python/terminal call can read /proc/<Studio pid>/environ (for example using its parent PID) and recover the original Studio process variables such as HF_TOKEN or cloud keys. Since the UI/API promise that API keys stay hidden while this path explicitly runs unsandboxed, this leaks exactly the credentials this filter is trying to remove; avoid claiming/enabling secret protection here unless the parent environment is also protected or the child is isolated from the parent process environment.

Useful? React with 👍 / 👎.

danielhanchen and others added 2 commits June 11, 2026 15:19
…ool path

Follow-up to the Bypass Permissions feature. Addresses the review findings:

- Anthropic /v1/messages 500: declare bypass_permissions on
  AnthropicMessagesRequest so tool requests that omit the field default to
  False instead of raising AttributeError (extra='allow' does not set absent
  attributes).
- /proc parent-env leak: stripping the child env did not stop a same-uid
  bypassed child from reading /proc/<parent>/environ to recover the
  tool-executing process's unfiltered secrets. Clear PR_SET_DUMPABLE on that
  process before the first bypass exec so its /proc entries become root-owned.
  Hardening is fail-closed: if prctl is denied, bypass execution is refused
  rather than run with the parent environ still readable. Mitigation, not a
  full boundary; documented in the code.
- Broker/capability vars: strip SSH_AUTH_SOCK, SSH_AGENT_PID, GPG_AGENT_INFO,
  GNUPGHOME, KUBECONFIG, DOCKER_HOST so a bypassed tool cannot use the
  operator's live agents.
- Credential-bearing URL values: drop any env var whose value embeds URL
  userinfo (scheme://user:pass@ and token-only scheme://token@) regardless of
  the variable name. Benign proxy/index URLs without credentials are kept, so
  proxy-only and internal-index setups still work in bypass mode.
- Windows temp isolation: repoint TEMP and TMP (not just TMPDIR) at the
  per-session sandbox dir.
- Frontend: stop persisting bypassPermissions; a reload now starts with the
  sandbox/confirmation bypass off and requires re-accepting the warning dialog.

Adds regression tests for each finding in test_bypass_permissions.py.
@danielhanchen

Copy link
Copy Markdown
Member Author

Reviewed this end to end and reproduced each flagged issue locally. They are all real; I pushed a commit on top (eef8983) that fixes them, with regression tests. Summary below.

Findings (all confirmed) and fixes

1. Anthropic /v1/messages 500 on tool requests (gemini high / codex P1). AnthropicMessagesRequest is extra='allow' but does not declare bypass_permissions. In Pydantic v2 an omitted extra field is not set as an attribute, so bool(payload.bypass_permissions) raises AttributeError for any normal tool-enabled Anthropic request:

req = AnthropicMessagesRequest(model="x", messages=[{"role":"user","content":"hi"}], max_tokens=16)
bool(req.bypass_permissions)   # AttributeError: ... has no attribute 'bypass_permissions'

Fix: declare bypass_permissions: Optional[bool] = False on the model. Omitted -> False, no crash.

2. Secret stripping defeated via /proc/<ppid>/environ (codex P1). _build_bypass_env only strips secrets from the child env. The child runs same-uid with no /proc hardening, so it reads the parent process environ directly. Repro (child run under disable_sandbox=True, parent started with a sentinel secret):

CHILD_HF_TOKEN_present=False     # stripped from the child env, as advertised
PROC_PARENT_READABLE=True
PROC_SENTINEL_LEAKED=True         # ...but recovered verbatim from /proc/<ppid>/environ

Fix: clear PR_SET_DUMPABLE on the tool-executing process before the first bypass exec, which makes its /proc entries root-owned and unreadable to the same-uid child (after the fix the same repro prints PROC_PARENT_READABLE=False PermissionError). The hardening is fail-closed: if prctl is denied (e.g. by a seccomp policy) the bypass exec is refused rather than run with the environ still readable. This is a mitigation, not a complete boundary: a bypassed tool is unsandboxed by design and can still walk /proc to a same-uid ancestor (e.g. the launching shell) or read on-disk credentials by absolute path. Full isolation would need a separate uid / PID+mount namespace, which is out of scope; the warning dialog already calls the mode dangerous.

3. SSH_AUTH_SOCK and broker vars not stripped (codex P1). Not secrets by value, but they hand the child the operator's live ssh/gpg agent, kube config, or docker daemon. Fix: strip SSH_AUTH_SOCK, SSH_AGENT_PID, GPG_AGENT_INFO, GNUPGHOME, KUBECONFIG, DOCKER_HOST by name.

4. Credential-bearing URL vars not stripped (codex P1). PIP_INDEX_URL, HTTP(S)_PROXY, DATABASE_URL, and arbitrarily named vars like REPO_URL=https://ghp_token@github.com/... embed credentials in the value. Fix: drop any var whose value matches URL userinfo, covering both ://user:pass@ and token-only ://token@. Benign proxy/index URLs without credentials are kept by value, so proxy-only and internal-index networks still work in bypass mode (no name-based stripping of those config vars).

5. Windows TEMP/TMP not repointed (gemini medium). _build_bypass_env set only TMPDIR; on Windows tempfile honors TEMP/TMP. Fix: repoint all three at the per-session sandbox dir.

6. Persisted bypass reactivates on reload without re-consent (codex P2). The store rehydrated bypassPermissions from localStorage, so a reload silently restarted with the sandbox and confirmation gate disabled and the warning never shown again. Fix: do not persist the flag - it initializes to false every load and must be re-enabled through the warning dialog each session. (Toggling on still requires accepting the dialog, as before.)

Validation

test_bypass_permissions.py extended with regression tests for each finding (broker name stripping; URL-userinfo value stripping incl. token-only and benign-URL keep-through; the parent /proc hardening invocation, a fail-closed test, and a mechanism test that a child can no longer read this process's /proc environ after hardening; the Anthropic model default). Full file plus test_safetensors_tool_loop.py, test_tool_confirm_loop.py, test_tool_approvals.py, and the test_anthropic_* suite pass (202 tests in the combined run).

One follow-up out of scope here: the Anthropic path still does not thread confirm_tool_calls (pre-existing, separate from this PR).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d906139661

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +420 to +424
env = {
k: v
for k, v in os.environ.items()
if not _is_secret_env_name(k) and not _is_secret_env_value(v)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Drop HF cache env vars from bypass env

When the Studio process was started with HF_HOME (or similar Hugging Face cache-location variables) pointing at the operator's real cache, this copies that path into the bypassed child because the name is not classified as secret. Repointing HOME then does not prevent huggingface_hub/Transformers from auto-reading the cached token under $HF_HOME, so a bypassed python/terminal tool can recover the credential despite the bypass env promising SDK cached credentials are hidden; strip or repoint those cache/config location variables before inheriting the host environment.

Useful? React with 👍 / 👎.

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d906139661

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread studio/backend/core/inference/tools.py Outdated
Comment on lines +420 to +423
env = {
k: v
for k, v in os.environ.items()
if not _is_secret_env_name(k) and not _is_secret_env_value(v)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip HF cache env vars from bypass env

When the Studio process has HF_HOME/HF_HUB_CACHE in its environment (startup sets these cache paths, and without_hf_auth already treats HF_HOME as auth-bearing), this bypass env copies them because their names do not match the secret filters. A bypassed python/terminal call can then read $HF_HOME/token or let huggingface_hub load it, so repointing only HOME does not actually prevent cached Hugging Face credentials from reaching unsandboxed tools; strip or repoint the HF cache/auth path variables here as well.

Useful? React with 👍 / 👎.

for k, v in os.environ.items()
if not _is_secret_env_name(k) and not _is_secret_env_value(v)
}
env["HOME"] = workdir

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Repoint Windows profile paths before bypass exec

On Windows, overriding only HOME here does not move the profile/config locations most SDKs use: USERPROFILE, HOMEDRIVE/HOMEPATH, APPDATA, and LOCALAPPDATA are still copied from the host by the env comprehension above. In bypass mode a python/terminal tool can therefore read cached credentials from the operator's real profile (for example .aws, Hugging Face, or wandb config) despite the intended HOME repointing, so these Windows profile variables need to be stripped or redirected to the session workdir too.

Useful? React with 👍 / 👎.

Comment thread studio/backend/core/inference/tools.py Outdated
Comment on lines +420 to +423
env = {
k: v
for k, v in os.environ.items()
if not _is_secret_env_name(k) and not _is_secret_env_value(v)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Drop credential config path env vars

If the operator has path override variables such as NETRC, BOTO_CONFIG, or PIP_CONFIG_FILE pointing at real credential/config files, this comprehension preserves them because neither the names nor path values match the new secret filters. Those variables are honored by common HTTP/cloud/package clients before $HOME, so a bypassed python/terminal tool can still auto-auth through the host's cached credentials even though HOME is repointed; strip or rewrite credential-bearing config path variables here too.

Useful? React with 👍 / 👎.

danielhanchen and others added 2 commits June 14, 2026 12:07
…ions

Repointing HOME did not stop SDKs auto-reading cached creds via vars that
point at the real home/cache/config: HF_HOME (startup always sets it; token
lives under $HF_HOME/token), HF/XDG cache roots, NETRC/BOTO_CONFIG/
PIP_CONFIG_FILE, and Windows HOMEDRIVE/HOMEPATH. Drop those, and repoint
USERPROFILE/APPDATA/LOCALAPPDATA at the per-session workdir. Adds regression
tests.
@danielhanchen

Copy link
Copy Markdown
Member Author

Pushed 0deffdfdd to close the remaining review points on the bypass env. Repointing HOME was not enough: several variables point credential-aware SDKs back at the operator's real home/cache/config, so a bypassed python/terminal tool could still auto-read cached credentials even with the token names stripped from the per-call env.

The clearest case is HF_HOME. Studio startup always sets it (storage_roots._setup_cache_env, and without_hf_auth() already treats it as auth-bearing), and huggingface_hub reads the cached token from $HF_HOME/token regardless of HOME. Before the fix, with HF_HOME pointing at a real cache a bypassed tool reads $HF_HOME/token and recovers the token despite HF_TOKEN being stripped.

Fix in _build_bypass_env:

  • Drop credential-location vars so each tool falls back to the empty repointed HOME: HF cache roots (HF_HOME, HF_HUB_CACHE, HF_XET_CACHE, TRANSFORMERS_CACHE, HF_DATASETS_CACHE, HF_ASSETS_CACHE), XDG base dirs (XDG_CONFIG_HOME/XDG_CACHE_HOME/XDG_DATA_HOME), explicit config-file pointers (NETRC, BOTO_CONFIG, PIP_CONFIG_FILE, CLOUDSDK_CONFIG, KAGGLE_CONFIG_DIR, DOCKER_CONFIG, WANDB_DIR/WANDB_CONFIG_DIR/WANDB_CACHE_DIR), and HOMEDRIVE/HOMEPATH.
  • Repoint the Windows profile dirs callers expect to exist (USERPROFILE, APPDATA, LOCALAPPDATA) at the per-session workdir, since SDKs read .aws/huggingface/wandb config under those rather than $HOME on Windows.

AWS_*/AZURE_/GOOGLE_/GCP_/GCLOUD_ config-path vars were already dropped by the prefix rule, and HF_TOKEN_PATH and similar by the token marker, so those needed no new handling.

Same boundary as before: bypass is unsandboxed by design, so a tool can still read on-disk credentials by absolute path or walk /proc to a same-uid ancestor. This only closes the auto-read path where an inherited env var hands the SDK the real location.

Added regression tests in test_bypass_permissions.py for the dropped names, the HF_HOME cached-token case, the config-path pointers, and the Windows profile repointing. Full run: 295 passed (test_bypass_permissions.py plus the tool-confirm, approval, safetensors, anthropic, openai, and stream suites).

danielhanchen and others added 2 commits June 14, 2026 12:18
The drop-based fix relies on the whole HF_HOME/XDG fallback chain being
removed so huggingface_hub resolves under the repointed HOME. Add a test
that sets HF_HOME and XDG_CACHE_HOME at a real cache and asserts the
resolved token path lands under the workdir, not the operator's cache.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b58752f006

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +375 to +379
_BYPASS_ENV_SECRET_MARKERS = (
"TOKEN",
"API_KEY",
"APIKEY",
"SECRET",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip npm _auth credentials from bypass env

When an operator supplies npm registry credentials through NPM_CONFIG__AUTH (npm maps this env var to the protected _auth config; npm config get _auth reports the option is protected), this marker list does not match AUTH and the base64 value has no URL userinfo, so _build_bypass_env passes it through. In bypass mode a terminal/python tool can simply print that env var and recover registry credentials, contrary to the secret-stripping guarantee; add _AUTH/AUTH or explicit npm auth names to the scrubber.

Useful? React with 👍 / 👎.

Comment on lines +465 to +467
if not _is_secret_env_name(k)
and not _is_secret_env_value(v)
and not _is_cred_location_env_name(k)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Drop shell startup hooks from bypass env

For terminal calls on POSIX, _get_shell_cmd runs bash -c, and bash reads the file named by BASH_ENV for non-interactive shells; because this filter keeps BASH_ENV, an operator/CI shell startup file that re-exports stripped values like HF_TOKEN or cloud keys is sourced before the requested command. In that environment, a bypassed terminal tool can recover secrets just by printing variables that were removed from the child env, so shell startup hook variables such as BASH_ENV should be stripped or repointed to an empty file.

Useful? React with 👍 / 👎.

Comment on lines +380 to +381
"PASSWORD",
"PASSWD",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip MYSQL_PWD from bypass env

When the Studio process is started with the MySQL client password in MYSQL_PWD, this classifier keeps it because the markers include PASSWORD/PASSWD but not the exact MYSQL_PWD spelling, and the value is not a URL. A bypassed python or terminal tool can therefore print the inherited variable and recover the database password despite the secret-stripping guarantee; add the exact MySQL password env name rather than relying on the generic markers.

Useful? React with 👍 / 👎.

danielhanchen and others added 2 commits June 15, 2026 02:14
Three more credential vectors dodged the bypass scrubber: NPM_CONFIG__AUTH
(npm _auth, base64 so no URL userinfo and no AUTH marker), MYSQL_PWD (markers
use PASSWD, not PWD, since PWD is the cwd var), and BASH_ENV (bash -c sources
it for non-interactive shells, so a startup file can re-export stripped
secrets). Add an AUTH marker, the exact MYSQL_PWD name, and drop BASH_ENV plus
PGPASSFILE. Adds regression tests incl. an end-to-end check that a bypass
terminal call does not source BASH_ENV.
@danielhanchen

Copy link
Copy Markdown
Member Author

Addressed the three credential vectors from the latest Codex review (all confirmed and reproduced) in 6d7cfd2ac:

  • npm _auth: NPM_CONFIG__AUTH is base64, so it has no URL userinfo and the markers did not include AUTH. Added an AUTH marker (also covers REDISCLI_AUTH and similar).
  • MYSQL_PWD: the markers use PASSWD/PASSWORD, not PWD (on purpose, since PWD is the working-directory var), so the exact name slipped through. Added MYSQL_PWD by name.
  • BASH_ENV: _get_shell_cmd runs bash -c, and non-interactive bash sources $BASH_ENV, so an operator startup file could re-export stripped secrets back into a bypass terminal call. Reproduced it locally. Dropped BASH_ENV, plus PGPASSFILE which is the same class as NETRC.

Added regression tests, including an end-to-end check that a real bypass terminal call does not source BASH_ENV. Full run: 298 passed.

Worth stating plainly: in bypass mode the secret stripping is best-effort by design. The env is otherwise inherited so tools work, so a name-based denylist cannot be exhaustive, and a bypassed tool can still read on-disk credentials by absolute path or reach a same-uid ancestor through /proc. The warning dialog already frames the mode as dangerous. These fixes close the common named vectors as they surface.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c1cb8e37b6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

auto_heal_tool_calls = True,
tool_call_timeout = 300,
session_id = payload.session_id,
bypass_permissions = bool(payload.bypass_permissions),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard empty Anthropic tool selections before bypassing

When an Anthropic request uses the server-tool path with enable_tools: true, enabled_tools: [], and bypass_permissions: true, _select_anthropic_server_tools returns an empty openai_tools list but this path still enters generate_chat_completion_with_tools. The GGUF tool loop treats an empty allow-list as unrestricted (bool(_allowed) is false), so a prompt-emitted XML terminal/python call can run with disable_sandbox=True even though the request enabled no tools; the OpenAI GGUF/safetensors paths already skip the loop when the selected tool list is empty, so mirror that guard here before forwarding bypass.

Useful? React with 👍 / 👎.

Comment on lines +568 to +570
try:
os.setsid()
except OSError:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve parent-death cleanup in bypass preexec

On Linux bypassed python/terminal calls now use this preexec path instead of _sandbox_preexec, but it drops the existing PR_SET_PDEATHSIG = SIGKILL setup. If the Studio process is killed or crashes while a bypassed command is running, a no-output or long-running child can survive as an orphan with the sandbox disabled and no resource caps; keeping the parent-death signal here would preserve cleanup without re-enabling the sandbox checks.

Useful? React with 👍 / 👎.

danielhanchen and others added 2 commits June 15, 2026 02:46
…loops

From a parallel review pass over the bypass changes:
- Drop more credential-location vars in _build_bypass_env: npm/yarn/git/cargo/
  rclone config pointers (NPM_CONFIG_USERCONFIG, NPM_CONFIG_GLOBALCONFIG,
  YARN_RC_FILENAME, GIT_CONFIG_GLOBAL, GIT_CONFIG_SYSTEM, CARGO_HOME,
  RCLONE_CONFIG) and the GIT_ASKPASS/SSH_ASKPASS auth helpers.
- Enforce confirm_tool_calls AND NOT bypass_permissions inside the safetensors
  and GGUF tool loops, not just at the route, so a direct internal caller
  passing both flags never prompts.
- Soften the toggle hint: environment secrets are stripped, but bypassed code
  can still read files and credentials on the machine (no overclaim that keys
  stay hidden).
Adds regression tests for the new names and the loop-level precedence.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c8d719691

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +616 to +617
if sys.platform != "linux":
return True # no /proc/<pid>/environ same-UID leak to close

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail closed for non-Linux parent env hardening

On macOS/Windows hosts, this branch treats parent-environment hardening as successful even though the bypassed child is unsandboxed and can still query the Studio process environment via native process-inspection APIs (for example Darwin ps -E for same-user processes). Fresh evidence versus the earlier Linux /proc finding is that this version fail-closes only on Linux and explicitly returns True here, so _python_exec/_bash_exec proceed with HF_TOKEN/cloud keys still recoverable from the parent; fail closed or implement equivalent hardening on unsupported platforms.

Useful? React with 👍 / 👎.

Comment on lines +431 to +433
# auth-helper scripts that hand creds to git/ssh
"GIT_ASKPASS",
"SSH_ASKPASS",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip Git SSH command overrides from bypass env

When the operator has GIT_SSH_COMMAND or GIT_SSH set to select a deploy key, this scrub list drops GIT_ASKPASS/SSH_ASKPASS but leaves those SSH command overrides in the inherited bypass environment. Git documents GIT_SSH_COMMAND as the SSH command it runs and gives ssh -i ... as the intended pattern, so a bypassed terminal/python tool can print the key path or run git with the operator's key despite stripping other broker variables; add these Git SSH overrides to the credential-location/capability filter. Source checked: https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables

Useful? React with 👍 / 👎.

Comment on lines +423 to +430
# package-manager / git / cloud config pointers to real cred files
"NPM_CONFIG_USERCONFIG",
"NPM_CONFIG_GLOBALCONFIG",
"YARN_RC_FILENAME",
"GIT_CONFIG_GLOBAL",
"GIT_CONFIG_SYSTEM",
"CARGO_HOME",
"RCLONE_CONFIG",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip inline Git config env pairs from bypass env

When Studio is launched from CI or a wrapper that injects Git credentials through inline config, e.g. GIT_CONFIG_COUNT=1, GIT_CONFIG_KEY_0=http.extraHeader, and GIT_CONFIG_VALUE_0=Authorization: Bearer ..., this filter only drops file-based Git config paths and leaves the GIT_CONFIG_* triplet untouched. Those names do not match the secret markers and the value is not a URL, so a bypassed terminal/python tool can print the bearer header or have git use it; Git documents these env pairs as runtime configuration that overrides config files. Strip GIT_CONFIG_COUNT, GIT_CONFIG_KEY_*, and GIT_CONFIG_VALUE_* before inheriting the host env. Source checked: https://git-scm.com/docs/git-config

Useful? React with 👍 / 👎.

danielhanchen and others added 2 commits June 15, 2026 05:16
The safetensors loop precedence is covered behaviorally; the GGUF loop needs a
live llama-server so add an AST guard asserting its _needs_confirm gate
references both confirm_tool_calls and bypass_permissions, matching the other
llama_cpp source-inspection tests.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23344c8f68

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

"PASSWD",
"CREDENTIAL",
"PRIVATE_KEY",
"AUTH", # e.g. NPM_CONFIG__AUTH (npm _auth), REDISCLI_AUTH

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip webhook URLs from bypass env

When Studio is launched with capability URLs such as SLACK_WEBHOOK_URL or DISCORD_WEBHOOK_URL, _build_bypass_env still passes them through because the name does not contain any of these markers and the current value check only catches URL userinfo, not tokens embedded in the path. In bypass mode a python/terminal tool can print or use that inherited webhook despite the secret-stripping guarantee, so add a WEBHOOK marker/explicit names or move this path to an allowlist.

Useful? React with 👍 / 👎.

When Bypass Permissions is on, show a persistent red pill in the composer
tool-pill row (like the Search/Code pills), matching Claude Code's always-
visible bypass indicator. Clicking it turns bypass off, mirroring the other
composer toggles. Enabling still goes through the settings toggle + warning
dialog. Adds a data-variant=danger style for the destructive-colored pill.
The empty-state and active Thread render their own composer (thread.tsx),
not shared-composer, so the badge only appeared in the split layout. Mirror
the red dismissible pill in ComposerAction so it shows in every composer.
Bring the Bypass Permissions feature up to date with main (262 commits).
main landed the tool-call-confirmation base (#5869) and evolved it, so the
conflicts were resolved by taking main's version of every touched file and
re-applying the bypass deltas on top:

- tools.py: bypass env scrubber (_build_bypass_env, secret/cred-location
  classifiers), _bypass_preexec, /proc fail-closed hardening, and the
  disable_sandbox path in execute_tool/_python_exec/_bash_exec.
- safetensors_agentic.py / llama_cpp.py: thread bypass_permissions and gate
  needs_confirm with 'and not bypass_permissions' (main simplified the gate to
  bool(confirm_tool_calls)).
- routes/inference.py, models/inference.py, orchestrator.py: precedence
  (confirm and not bypass) plus the request fields and loop wiring.
- frontend: bypassPermissions store state, chat-adapter payload, the settings
  toggle + warning dialog, and the red composer badge in both composers.
- tests: fakes accept rag_scope (main added it); GGUF AST test matches main's
  needs_confirm name.
pre-commit-ci Bot and others added 2 commits June 15, 2026 09:14
…s collapsed

The Thread composer only renders the pill row when expanded, so the active-mode
badge vanished on the default (collapsed) empty state. Render it before the
expand gate (it returns null when bypass is off) so the red indicator always
shows while bypass is on.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd81d9762d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const token = getAuthToken();
if (token) {
query.set("allow_network", "1");
query.set("token", token);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop putting bearer tokens in artifact URLs

When allowArtifactNetworkAccess is enabled, the generated HTML runs as script in the preview frame and can read its own location.href, which includes this token query parameter; because the network CSP for that mode also allows outbound http(s)/image requests, any model-generated artifact can exfiltrate the Studio bearer token and then use it against the API until it expires. Use a frame-only capability or an HttpOnly authentication mechanism instead of exposing the main auth token to the untrusted document.

Useful? React with 👍 / 👎.

Comment thread studio/backend/main.py


_MAX_BODY_BYTES = int(os.environ.get("UNSLOTH_STUDIO_MAX_BODY_MB", "500")) * 1024 * 1024
_BODY_PROTECTED_PREFIXES = (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add RAG uploads to the body-size guard

The new RAG document upload routes live under /api/rag/.../documents, but this protected-prefix list does not include /api/rag, so MaxBodyMiddleware skips those requests entirely and routes/rag.py streams the uploaded file until EOF. An authenticated client can therefore write arbitrarily large RAG documents to disk despite the Studio upload cap; include /api/rag here and add the same upload passthrough sizing used by the other multipart upload routes.

Useful? React with 👍 / 👎.

@danielhanchen danielhanchen changed the base branch from tool-call-confirmation to main June 15, 2026 10:01
The destructive button variant is a subtle 10% tint that read as bare red text
next to the outlined Cancel. Force the solid destructive fill (the variant's
class loses to the tint through AlertDialogAction's Slot merge, so use the !
override the codebase already uses for this case) and shorten the label to
'I understand' so it fits the small dialog's two-column footer.
Adds a 'Bypass Permissions' entry to the composer plus-menu (under More by
default) in both composers, so it can be toggled without opening Run settings.
Enabling routes through the same danger warning dialog; disabling is immediate.
A shared BypassPermissionsMenuItem keeps the two composers in sync.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf0f0c8029

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +3977 to +3979
confirm_tool_calls = bool(payload.confirm_tool_calls)
and not bool(payload.bypass_permissions),
bypass_permissions = bool(payload.bypass_permissions),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply bypass before confirm stream validation

When an API client sends confirm_tool_calls: true together with bypass_permissions: true on a local tool request with stream: false, the earlier stream guard still checks the raw payload.confirm_tool_calls and rejects before this effective confirm && !bypass value is used (the safetensors path has the same ordering). That contradicts the new precedence rule documented here and forces clients that have both flags set to know to clear confirm_tool_calls themselves; compute the effective confirm flag before validation, or gate the validation on confirm_tool_calls && !bypass_permissions.

Useful? React with 👍 / 👎.

"DOCKER_HOST",
}
)
_BYPASS_ENV_SECRET_PREFIXES = ("AWS_", "AZURE_", "GOOGLE_", "GCP_", "GCLOUD_", "DYLD_")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve AWS IMDS disable flag in bypass env

When Studio runs on EC2 with AWS_EC2_METADATA_DISABLED=true, this broad AWS_ prefix drops that non-secret guard from the bypass child; AWS documents that the flag disables SDK/CLI credential lookups from IMDS (https://docs.aws.amazon.com/sdkref/latest/guide/feature-imds-credentials.html). With env/file credentials stripped, a bypassed python/terminal call that uses boto or the AWS CLI can now fall back to the instance role and obtain cloud credentials even though the operator explicitly disabled that path, so keep this safety flag while stripping the actual AWS credential variables.

Useful? React with 👍 / 👎.

Comment on lines +365 to +369
"PASSWORD",
"PASSWD",
"CREDENTIAL",
"PRIVATE_KEY",
"AUTH", # e.g. NPM_CONFIG__AUTH (npm _auth), REDISCLI_AUTH

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip connection-string env vars from bypass env

When Studio is launched in environments that expose connection strings, this marker set misses names such as SQLCONNSTR_*, CUSTOMCONNSTR_*, or WEBSITE_CONTENTAZUREFILECONNECTIONSTRING, and _is_secret_env_value only catches URL userinfo, not values like AccountKey=... or Password=...; Azure documents these App Service variables as connection strings (https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings). A bypassed python/terminal tool can therefore print database or storage credentials despite the secret-stripping guarantee, so add connection-string name/value markers or switch this path to an allowlist.

Useful? React with 👍 / 👎.

…rings

Two gaps in the Bypass Permissions secret scrubber:

- The broad AWS_ prefix also dropped AWS_EC2_METADATA_DISABLED, a non-secret
  opt-out. Removing it re-opens the IMDS instance-role credential path that the
  operator explicitly disabled, so a bypassed boto/AWS-CLI call could recover
  cloud creds. Keep that flag (and AWS_EC2_METADATA_V1_DISABLED) via a keep-list
  while still stripping the real AWS credential vars.
- Azure App Service connection strings (SQLCONNSTR_/CUSTOMCONNSTR_/...,
  WEBSITE_CONTENTAZUREFILECONNECTIONSTRING) and values like Password=/AccountKey=
  /SharedAccessKey= slipped past the name and URL-only value classifiers. Add
  CONNSTR/CONNECTIONSTRING name markers and a connection-string value matcher.
The confirm-vs-bypass precedence (confirm and not bypass) was applied at the
loop call sites but not at the earlier request guards, so a client sending
confirm_tool_calls + bypass_permissions together was rejected (stream=true
required / unsupported for external or Anthropic tools) before the precedence
took effect. Gate all four confirm guards on not bypass_permissions so both
flags together proceed with the gate suppressed, matching the documented rule.
@danielhanchen

Copy link
Copy Markdown
Member Author

Thanks, all three are valid and now fixed in 78db58a and 0033c84.

Bypass env scrubber (P1 x2)

  • AWS IMDS opt-out: AWS_EC2_METADATA_DISABLED (and AWS_EC2_METADATA_V1_DISABLED) are kept via an explicit keep-list instead of being dropped by the broad AWS_ prefix, so a bypassed boto / AWS CLI call cannot fall back to the instance role when the operator disabled IMDS. The real AWS credential vars are still stripped.
  • Connection strings: added CONNSTR / CONNECTIONSTRING name markers (covers SQLCONNSTR_, MYSQLCONNSTR_, SQLAZURECONNSTR_, CUSTOMCONNSTR_, WEBSITE_CONTENTAZUREFILECONNECTIONSTRING) plus a value matcher for Password= / AccountKey= / SharedAccessKey=, so ADO.NET and Azure storage / Service Bus connection strings are stripped by name or by value. The key NAME fields (e.g. SharedAccessKeyName=) are left alone.

Confirm vs bypass precedence (P2)

  • The precedence rule (confirm and not bypass) was only applied at the loop call sites, not the earlier request guards. All four confirm guards (local stream-required on the GGUF and safetensors paths, the external-provider guard, and the Anthropic server-tools guard) now gate on not bypass_permissions, so sending both flags together proceeds with the gate suppressed instead of returning 400.

Added regression tests for the keep-list, the connection-string name and value cases, and non-credential connection strings (full suite green).

@danielhanchen danielhanchen merged commit ca0528d into main Jun 15, 2026
34 checks passed
@danielhanchen danielhanchen deleted the bypass-permissions branch June 15, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants