Skip to content

fix: add client-side validation for shell tool allowlist network policy#2947

Open
giulio-leone wants to merge 2 commits intoopenai:mainfrom
giulio-leone:fix/shell-tool-allowlist
Open

fix: add client-side validation for shell tool allowlist network policy#2947
giulio-leone wants to merge 2 commits intoopenai:mainfrom
giulio-leone:fix/shell-tool-allowlist

Conversation

@giulio-leone
Copy link

@giulio-leone giulio-leone commented Mar 9, 2026

Summary

This is a proactive security improvement, not a bug fix — there is no linked issue. Users configuring the shell tool's allowed_domains network policy can accidentally pass malformed entries (protocol prefixes, URL paths, empty lists) that silently produce an opaque server-side 500 error. This PR adds client-side validation to catch these mistakes early with clear error messages, and adds serialization regression tests to lock down the correct wire format.

Addresses #2920 (related server-side regression context).

Changes

  1. Client-side validation (src/openai/lib/_validation.py) — validates allowed_domains entries before the request is sent. Raises a clear ValueError for:

    • Empty domain lists (minItems: 1 in the spec)
    • Domains with protocol prefixes (http://example.com → suggests example.com)
    • Domains with URL paths (example.com/api)
    • Empty/whitespace-only strings
  2. Serialization regression tests (tests/test_shell_tool_allowlist.py, 24 tests) — prove the library sends the correct JSON for:

    • Allowlist with single/multiple domains
    • Allowlist with domain_secrets
    • Disabled network policy
    • Shell tool without environment
    • Full maybe_transform round-trip

The validation is wired into the existing _make_tools() helper so it runs for both sync and async responses.create() calls.

Why This Matters

Without this validation, users who misconfigure their shell tool allowlist get a cryptic 500 error from the API with no indication of what they did wrong. The client-side check provides an actionable error message before the request is ever sent, reducing debugging time and preventing accidental command exposure through misconfigured allowlists.

Workaround for the server-side issue

Until the API regression is resolved, users can work around it by omitting the network_policy field or setting it to {"type": "disabled"}.

@giulio-leone giulio-leone requested a review from a team as a code owner March 9, 2026 11:49
Copy link

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

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: e487c2419b

ℹ️ 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".

if not is_given(tools):
return omit

validate_tools(tools)

Choose a reason for hiding this comment

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

P1 Badge Preserve iterable tools before validating

_make_tools accepts Iterable[ParseableToolParam], but validate_tools(tools) consumes that iterable before the conversion loop runs; when callers pass a one-shot iterator (for example, a generator), the subsequent for tool in tools loop is empty and the request is sent without any tools. This is a behavior regression introduced by the validation call and can silently disable tool execution.

Useful? React with 👍 / 👎.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed in c7fc0a3tools is now materialised to a list before validate_tools() runs, so one-shot iterators are preserved for the conversion loop.

from typing import Any, Iterable, Optional

_PROTOCOL_RE = re.compile(r"^https?://", re.IGNORECASE)
_PATH_RE = re.compile(r"/.")

Choose a reason for hiding this comment

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

P2 Badge Reject trailing slash in allowlist domains

The path regex r"/." fails to match domains ending in a bare slash (for example "example.com/"), so these malformed entries pass client-side validation even though they are not bare domains. In that case users still hit the opaque server error this new validation is intended to prevent.

Useful? React with 👍 / 👎.

@giulio-leone
Copy link
Author

Friendly ping — CI is green, tests pass, rebased on latest. Ready for review whenever convenient. Happy to address any feedback. 🙏

giulio-leone added 2 commits March 9, 2026 21:50
Add client-side validation for network_policy.allowed_domains to catch
common configuration errors (protocol prefixes, empty lists, paths in
domains) before sending the request. These mistakes currently result in
opaque 500 server errors.

Also adds serialization regression tests proving the library sends the
correct JSON payload for shell tool allowlist configurations.

Fixes openai#2920
@giulio-leone giulio-leone force-pushed the fix/shell-tool-allowlist branch from c7fc0a3 to e24a1a6 Compare March 9, 2026 20:52
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.

1 participant