diff --git a/docs/architecture/security-model.md b/docs/architecture/security-model.md index 15c22a0a40..d9bd9e16f2 100644 --- a/docs/architecture/security-model.md +++ b/docs/architecture/security-model.md @@ -71,7 +71,7 @@ flowchart LR | Subject-token workload identity | Local or metadata subject token and token-exchange destination | Provider callback; built-ins read Kubernetes token file or call Azure/GCP metadata endpoints; `WorkloadIdentityAuth` defaults to `https://auth.openai.com/oauth/token`, while direct callers may supply `token_exchange_url`, and posts the raw subject token to that configured recipient. | Kubernetes defaults to `/var/run/secrets/kubernetes.io/serviceaccount/token`; metadata hosts are fixed by helper; raw subject token enters the exchange JSON body and exchanged bearer is cached in memory. | Local host identity source, configured token-exchange recipient, then selected API destination. | Default exchange URL, redirects disabled on the exchange client, bounded timeout, token-response validation, and in-memory token cache. | [src/openai/auth/__init__.py:6](../../src/openai/auth/__init__.py), [src/openai/auth/_workload.py:17](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:78](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:128](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:181](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:220](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:340](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:345](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:355](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:366](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:369](../../src/openai/auth/_workload.py) | | Webhook consumer | Webhook secret and raw inbound bytes | Explicit secret, then client `webhook_secret`, which may come from `OPENAI_WEBHOOK_SECRET`. | Secret remains in process memory; payload and headers are caller-supplied bytes. | SDK verifier and caller application. | Timestamp tolerance is a bounded-age check, not uniqueness or replay prevention; HMAC-SHA256 and constant-time comparison authenticate bytes; `unwrap` verifies before JSON parsing; caller owns webhook-id deduplication. | [src/openai/_client.py:281](../../src/openai/_client.py), [src/openai/resources/webhooks/webhooks.py:18](../../src/openai/resources/webhooks/webhooks.py), [src/openai/lib/_webhooks.py:13](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:20](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:33](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:57](../../src/openai/lib/_webhooks.py) | | File upload | Caller filesystem read | Caller passes bytes, streams, or `PathLike` values. | Caller-selected path or already-opened stream. | Local OS and remote API. | Local OS permissions and explicit caller invocation; SDK does not discover files automatically. | [src/openai/_files.py:25](../../src/openai/_files.py), [src/openai/_files.py:65](../../src/openai/_files.py) | -| HTTP/SSE response | Remote response bytes | Remote endpoint selected by caller configuration; streaming uses incremental decoders. | Remote JSON, SSE lines, or events in process memory. | SDK model parser and caller application. | Incremental SSE handling and `finally` response cleanup; large legitimate payloads are supported without arbitrary fixed rejection limits. | [src/openai/_streaming.py:53](../../src/openai/_streaming.py), [src/openai/_streaming.py:109](../../src/openai/_streaming.py), [AGENTS.md:112](../../AGENTS.md) | +| HTTP/SSE response | Remote response bytes | Remote endpoint selected by caller configuration; streaming uses incremental decoders. | Remote JSON, SSE lines, or events in process memory. | SDK model parser and caller application. | Incremental SSE handling and `finally` response cleanup; large legitimate payloads are supported without arbitrary fixed rejection limits. | [src/openai/_streaming.py:53](../../src/openai/_streaming.py), [src/openai/_streaming.py:109](../../src/openai/_streaming.py), [AGENTS.md:118](../../AGENTS.md) | | Current and beta Realtime/Responses WebSockets | WebSocket destination and client auth headers | All current and beta paths use explicit `websocket_base_url` or transform the configured HTTP base to a WebSocket scheme, then merge client auth with caller extra headers. Only Realtime branches into Azure `_configure_realtime` for `api-key` or AAD bearer routing; Responses uses inherited `auth_headers` directly. Beta Realtime also adds `OpenAI-Beta: realtime=v1` before caller-header override. | Caller-selected WebSocket origin with `/realtime` or `/responses`; current and beta sync paths call `websockets.sync.client.connect` directly, while current and beta async paths use the SDK same-origin redirect wrapper, with async Azure Realtime using its Azure-specific wrapper. | WebSocket peer and any redirect target. | Caller owns explicit base/options; sync direct-connect and async same-origin redirect paths are distinct credential-routing surfaces, Azure async Realtime rejects cross-origin redirects, Responses keeps the generic client-auth path, and beta Realtime's extra header remains separately reviewable. | [src/openai/resources/realtime/realtime.py:683](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/realtime/realtime.py:693](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/realtime/realtime.py:1153](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/responses/responses.py:4410](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4429](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4855](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4874](../../src/openai/resources/responses/responses.py), [src/openai/resources/beta/realtime/realtime.py:355](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/realtime/realtime.py:382](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/realtime/realtime.py:540](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/responses/responses.py:4515](../../src/openai/resources/beta/responses/responses.py), [src/openai/resources/beta/responses/responses.py:4960](../../src/openai/resources/beta/responses/responses.py), [src/openai/_client.py:611](../../src/openai/_client.py), [src/openai/_client.py:1356](../../src/openai/_client.py), [src/openai/lib/azure.py:500](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:854](../../src/openai/lib/azure.py), [src/openai/lib/_websocket.py:12](../../src/openai/lib/_websocket.py), [src/openai/lib/_azure_websocket.py:8](../../src/openai/lib/_azure_websocket.py) | | Package build and PyPI publish | Executable checkout code, artifacts, and OIDC publication | `scripts/build` invokes the locked build; artifacts pass from build job to upload-only job. | Build artifact; no long-lived PyPI token. | CI runner, artifact store, PyPI. | Locked/provenance-checked build requirements; no OIDC in build; `id-token: write` only in the publish job. | [scripts/build:1](../../scripts/build), [pyproject.toml:71](../../pyproject.toml), [.github/workflows/publish-pypi.yml:8](../../.github/workflows/publish-pypi.yml), [.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml) | | Published dependency supply chain | Runtime, optional, and build dependency execution | `pyproject.toml` declares runtime/optional dependencies and the Hatch build backend; `uv.lock` records registry artifacts and hashes for repository builds. | Published dependency package or build backend executed under installer/runtime authority. | Package builders and SDK consumers. | Locked repository builds and provenance checks reduce CI confusion, but a malicious or provenance-confused dependency published through an ordinary trusted release remains a separate consumer boundary. | [pyproject.toml:11](../../pyproject.toml), [pyproject.toml:42](../../pyproject.toml), [pyproject.toml:71](../../pyproject.toml), [pyproject.toml:108](../../pyproject.toml), [uv.lock:46](../../uv.lock), [.github/workflows/ci.yml:37](../../.github/workflows/ci.yml) | @@ -108,13 +108,13 @@ flowchart LR and exceptions; those are independently readable recipients, not harmless diagnostics. Safe or sanitized caller-visible `APIError.body` diagnostics may remain - ([AGENTS.md:26](../../AGENTS.md), + ([AGENTS.md:32](../../AGENTS.md), [src/openai/_utils/_logs.py:10](../../src/openai/_utils/_logs.py), [src/openai/_exceptions.py:46](../../src/openai/_exceptions.py)). - Keep real API keys, bearer tokens, private keys, `.env` files, and other credentials out of tracked repository contents, history, review diffs, checkouts, and artifacts. Clearly fake or sanitized examples and fixtures may - remain ([AGENTS.md:22](../../AGENTS.md), [AGENTS.md:26](../../AGENTS.md)). + remain ([AGENTS.md:28](../../AGENTS.md), [AGENTS.md:32](../../AGENTS.md)). - Preserve package and release integrity: untrusted PR execution must not reach protected release credentials or publication authority ([.github/workflows/ci.yml:18](../../.github/workflows/ci.yml), @@ -126,7 +126,7 @@ flowchart LR [pyproject.toml:108](../../pyproject.toml), [uv.lock:46](../../uv.lock)). - Preserve large-payload compatibility through incremental processing, timely cleanup, and caller cancellation rather than arbitrary SDK body/event/line - limits ([AGENTS.md:112](../../AGENTS.md), [src/openai/_streaming.py:109](../../src/openai/_streaming.py)). + limits ([AGENTS.md:118](../../AGENTS.md), [src/openai/_streaming.py:109](../../src/openai/_streaming.py)). ### Actors, authority, and boundaries @@ -354,15 +354,15 @@ vulnerabilities. | High | A merged example change reaches the main-branch examples job's `OPENAI_API_KEY`. | Candidate-controlled executable example content is merged into `main`, then the same-repository push job runs that merged content with the secret; ordinary read-only PR execution alone is insufficient. | API-key disclosure or unauthorized API use from CI. | Main/repository trigger condition, `environment: ci` selection, no persisted checkout credentials, and locked dependency install; actual environment protection is external. | Preserve post-merge review of executable examples and verify host-side environment protection separately. | [.github/workflows/ci.yml:218](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:221](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:224](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:227](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:240](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:244](../../.github/workflows/ci.yml) | | High | Same-repository PR source causes an analyzer/action escape that repurposes CodeQL's `security-events: write` token. | Candidate source reaches the pinned CodeQL analyzer and a realistic escape or separately demonstrated execution path reaches the token-bearing job; mere checkout is insufficient. | Unauthorized security-event writes or any broader capability exposed by a regression in token use. | Narrow declared permissions, no persisted checkout credentials, pinned CodeQL actions, and no repository `run` step. | Preserve least privilege and ensure analyzer/action processing cannot exfiltrate or reuse the token outside intended security-result publication. | [.github/workflows/codeql.yml:7](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:15](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:27](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:32](../../.github/workflows/codeql.yml) | | Medium | Forged webhook is accepted, or a caller mistakes a bounded-age verified webhook for unique delivery. | Attacker controls payload/headers but not the secret and verifier is bypassed/weakened, or attacker replays the same valid signed payload within tolerance where caller-side webhook-id deduplication is absent. | Unauthorized downstream action in caller application. | HMAC-SHA256, bounded-age timestamp tolerance, constant-time comparison, and verify-before-parse `unwrap`; SDK verifier is stateless and does not deduplicate. | Keep verification on raw bytes before application logic, protect/rotate secrets, and deduplicate webhook IDs at the caller boundary when uniqueness matters. | [src/openai/resources/webhooks/webhooks.py:18](../../src/openai/resources/webhooks/webhooks.py), [src/openai/lib/_webhooks.py:13](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:20](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:33](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:57](../../src/openai/lib/_webhooks.py) | -| Medium | Attacker-influenced remote JSON, SSE, or WebSocket input causes parser confusion, availability pressure, or unsafe caller-visible state. | Content returned through the normal OpenAI API or a caller-selected endpoint reaches SDK parsing/buffering/cleanup paths; impact must exceed ordinary malformed-response errors. | Process availability or downstream security decision impact. | JSON-only parsing, incremental SSE decoder, response cleanup, caller-configurable timeout/cancellation. | Preserve incremental handling and cleanup for large legitimate payloads; validate before sensitive application sinks. | [src/openai/_base_client.py:672](../../src/openai/_base_client.py), [src/openai/_streaming.py:166](../../src/openai/_streaming.py), [AGENTS.md:112](../../AGENTS.md) | -| Medium | Sensitive data is emitted through diagnostics. | Runtime/API/network data or secret-bearing state reaches logs, exceptions, snapshots, or test output without redaction. Safe or sanitized caller-visible `APIError.body` diagnostics alone are insufficient. | Credentials, customer data, or unredacted sensitive bodies become readable to unintended diagnostic recipients. | Sensitive-header log filter, repository redaction requirements, and the safe/sanitized `APIError.body` carve-out. | Preserve redaction at every diagnostic boundary and avoid copying unredacted sensitive bodies into logs, exceptions, snapshots, and test output. | [AGENTS.md:26](../../AGENTS.md), [src/openai/_utils/_logs.py:10](../../src/openai/_utils/_logs.py), [src/openai/_exceptions.py:46](../../src/openai/_exceptions.py) | -| High | A real credential, private key, bearer token, or `.env` file is committed to repository contents. | Candidate or generated content contains a real secret rather than a clearly fake or sanitized fixture; code execution is not required. | Secret disclosure through history, review diffs, checkouts, or artifacts. | Repository policy forbids real credentials and `.env` files and requires fake/sanitized fixtures. | Remove and rotate exposed credentials, preserve secret scanning, and keep the fake-fixture carve-out narrow. | [AGENTS.md:22](../../AGENTS.md), [AGENTS.md:26](../../AGENTS.md) | +| Medium | Attacker-influenced remote JSON, SSE, or WebSocket input causes parser confusion, availability pressure, or unsafe caller-visible state. | Content returned through the normal OpenAI API or a caller-selected endpoint reaches SDK parsing/buffering/cleanup paths; impact must exceed ordinary malformed-response errors. | Process availability or downstream security decision impact. | JSON-only parsing, incremental SSE decoder, response cleanup, caller-configurable timeout/cancellation. | Preserve incremental handling and cleanup for large legitimate payloads; validate before sensitive application sinks. | [src/openai/_base_client.py:672](../../src/openai/_base_client.py), [src/openai/_streaming.py:166](../../src/openai/_streaming.py), [AGENTS.md:118](../../AGENTS.md) | +| Medium | Sensitive data is emitted through diagnostics. | Runtime/API/network data or secret-bearing state reaches logs, exceptions, snapshots, or test output without redaction. Safe or sanitized caller-visible `APIError.body` diagnostics alone are insufficient. | Credentials, customer data, or unredacted sensitive bodies become readable to unintended diagnostic recipients. | Sensitive-header log filter, repository redaction requirements, and the safe/sanitized `APIError.body` carve-out. | Preserve redaction at every diagnostic boundary and avoid copying unredacted sensitive bodies into logs, exceptions, snapshots, and test output. | [AGENTS.md:32](../../AGENTS.md), [src/openai/_utils/_logs.py:10](../../src/openai/_utils/_logs.py), [src/openai/_exceptions.py:46](../../src/openai/_exceptions.py) | +| High | A real credential, private key, bearer token, or `.env` file is committed to repository contents. | Candidate or generated content contains a real secret rather than a clearly fake or sanitized fixture; code execution is not required. | Secret disclosure through history, review diffs, checkouts, or artifacts. | Repository policy forbids real credentials and `.env` files and requires fake/sanitized fixtures. | Remove and rotate exposed credentials, preserve secret scanning, and keep the fake-fixture carve-out narrow. | [AGENTS.md:28](../../AGENTS.md), [AGENTS.md:32](../../AGENTS.md) | | Medium | Monthly review input or output escapes its intended isolation or publication boundary. | A regression lets independently mutable lifecycle/PyPI data expose the action-held key, mutate protected workspace state, leak sensitive output to the step summary, escape the bounded issue body into publisher commands/metadata/target selection, or obtain broader `issues: write` use. Normal publication of the marker/size-checked issue body is not itself a finding. | OpenAI key exposure or unauthorized issue content/write behavior. | Unprivileged Codex user, non-writable Git metadata, process termination, output marker/size checks, and separate issue publisher. | Preserve environment restriction, action/process isolation, output checks, redaction, and the job boundary. | [.github/workflows/python-version-review.yml:19](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:58](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:94](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:104](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:125](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:160](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:171](../../.github/workflows/python-version-review.yml) | | High | A malicious or provenance-confused published dependency compromises SDK consumers through an ordinary release. | Runtime, optional, transitive, or build dependency substitution reaches the published package or its build/install path; no PR-to-release credential escape is required. | Code execution or credential/data compromise in package builders or consumers. | Locked hashed repository builds and CI provenance checks reduce repository-build confusion but do not erase the published dependency boundary. | Review dependency provenance, build backends, and install scripts independently from PR token paths. | [pyproject.toml:11](../../pyproject.toml), [pyproject.toml:42](../../pyproject.toml), [pyproject.toml:108](../../pyproject.toml), [uv.lock:46](../../uv.lock), [.github/workflows/ci.yml:37](../../.github/workflows/ci.yml) | | Medium | Host identity token misuse mints OpenAI bearer authority. | Attacker independently reads the mounted token or reaches metadata from a process that should not have that authority. | Unauthorized API calls as the workload identity. | Fixed helper endpoints/headers, bounded timeouts, token response validation, in-memory cache. | Restrict pod/file/metadata access and use workload identity only in intended runtimes. | [src/openai/auth/_workload.py:78](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:128](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:283](../../src/openai/auth/_workload.py) | | Low | Attacker-controlled application input becomes a local file upload. | Consuming application passes an attacker-chosen path while the process already has local read authority. | Local data disclosure to the configured API. | Explicit caller invocation and OS file permissions; no automatic discovery. | Validate/allowlist paths at the application boundary or pass opened streams/bytes. | [src/openai/_files.py:25](../../src/openai/_files.py), [src/openai/_files.py:65](../../src/openai/_files.py) | | Low | Local audio is captured or played unexpectedly. | Application explicitly invokes optional helpers with device permission. | Self-only local privacy or nuisance effect unless a distinct application boundary is shown. | Optional dependency, explicit helper path, OS permissions. | Gate helpers behind user intent and OS permission UX. | [pyproject.toml:46](../../pyproject.toml), [src/openai/helpers/microphone.py:81](../../src/openai/helpers/microphone.py) | -| Not a finding by itself | A PR changes a checked-in test, clearly fake or sanitized fixture/example, build script, or other tracked executable file and CI runs it. | PR author already controls that checkout code and no real credential is committed. | No new capability without a separate protected sink; a real committed secret remains reportable without execution. | Repository-code authority is explicit; CI boundary is evaluated at credentials and permissions. | Investigate if execution crosses into protected authority or tracked content contains a real secret. | [.github/workflows/ci.yml:18](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:69](../../.github/workflows/ci.yml), [AGENTS.md:22](../../AGENTS.md) | +| Not a finding by itself | A PR changes a checked-in test, clearly fake or sanitized fixture/example, build script, or other tracked executable file and CI runs it. | PR author already controls that checkout code and no real credential is committed. | No new capability without a separate protected sink; a real committed secret remains reportable without execution. | Repository-code authority is explicit; CI boundary is evaluated at credentials and permissions. | Investigate if execution crosses into protected authority or tracked content contains a real secret. | [.github/workflows/ci.yml:18](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:69](../../.github/workflows/ci.yml), [AGENTS.md:28](../../AGENTS.md) | ## Severity Calibration (Critical, High, Medium, Low) diff --git a/docs/castiron-generated-code-transition.md b/docs/castiron-generated-code-transition.md index b26e275652..eafc607202 100644 --- a/docs/castiron-generated-code-transition.md +++ b/docs/castiron-generated-code-transition.md @@ -681,7 +681,7 @@ Include focused contract/security tests, typing fixtures, relevant API-reference | Parsing and streaming | T3, T5, T6 and T13: implement shared typed hooks with complete consumer families and their tests; retain focused review boundaries for the richer audio, Chat and Responses contracts. | | Webhooks, client and Assistants | Deliver each coherent facade/client/helper family with its full compatibility and security coverage. T16 reference changes accompany the corresponding generated features. | -Reconcile and reuse existing branches/Deltas before starting a batch. Implement independent families in parallel. Publish a coherent ready family while unrelated work is blocked; sibling reviews must not stall all progress. A compiler capability and its dependent SDK regeneration remain separate repository changes, and compiler source must merge before SDK output is promoted. Budget adjustments remain separate ratchet-only PRs. Larger batches do not relax zero-breaking-change, provenance, CODEOWNER, compliance, security, test or protected-merge gates. +Reconcile and reuse existing branches/Deltas before starting a batch. Implement independent families in parallel. Publish a coherent ready family while unrelated work is blocked; sibling reviews must not stall all progress. A compiler capability and its dependent SDK regeneration remain separate repository changes, and compiler source must merge before SDK output is promoted. Larger batches do not relax zero-breaking-change, provenance, CODEOWNER, compliance, security, test or protected-merge gates. ### Dependency order