fix: allow private OpenAI image endpoints · openclaw/openclaw@d16b879
steipete
·
2026-04-24
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai
|
20 | 20 | - Agents/OpenAI: surface selected-model capacity failures from PI, Codex, and auto-reply harness paths with a model-switch hint instead of the generic empty-response error. Thanks @vincentkoc. |
21 | 21 | - Providers/OpenAI: route `openai/gpt-image-2` through configured Codex OAuth directly when an `openai-codex` profile is active, instead of probing `OPENAI_API_KEY` first. |
22 | 22 | - Providers/OpenAI: harden image generation auth routing and Codex OAuth response parsing so fallback only applies to public OpenAI API routes and bounded SSE results. Thanks @Takhoffman. |
| 23 | +- Providers/OpenAI: honor the private-network SSRF opt-in for OpenAI-compatible image generation endpoints, so trusted LocalAI/LAN `image_generate` routes work without disabling SSRF checks globally. Fixes #62879. Thanks @seitzbg. |
23 | 24 | - Providers/OpenAI: stop advertising the removed `gpt-5.3-codex-spark` Codex model through fallback catalogs, and suppress stale rows with a GPT-5.5 recovery hint. |
24 | 25 | - Plugins/QR: replace legacy `qrcode-terminal` QR rendering with bounded `qrcode-tui` helpers for plugin login/setup flows. (#65969) Thanks @vincentkoc. |
25 | 26 | - Voice-call/realtime: wait for OpenAI session configuration before greeting or forwarding buffered audio, and reject non-allowlisted Twilio callers before stream setup. (#43501) Thanks @forrestblount. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -236,6 +236,10 @@ does not first try `OPENAI_API_KEY` or silently fall back to an API key for that
|
236 | 236 | request. Configure `models.providers.openai` explicitly with an API key, |
237 | 237 | custom base URL, or Azure endpoint when you want the direct OpenAI Images API |
238 | 238 | route instead. |
| 239 | +If that custom image endpoint is on a trusted LAN/private address, also set |
| 240 | +`browser.ssrfPolicy.dangerouslyAllowPrivateNetwork: true`; OpenClaw keeps |
| 241 | +private/internal OpenAI-compatible image endpoints blocked unless this opt-in is |
| 242 | +present. |
239 | 243 | |
240 | 244 | Generate: |
241 | 245 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -35,6 +35,10 @@ Codex OAuth uses the same `openai/gpt-image-2` model ref. When an
|
35 | 35 | through that same OAuth profile instead of first trying `OPENAI_API_KEY`. |
36 | 36 | Explicit custom `models.providers.openai` image config, such as an API key or |
37 | 37 | custom/Azure base URL, opts back into the direct OpenAI Images API route. |
| 38 | +For OpenAI-compatible LAN endpoints such as LocalAI, keep the custom |
| 39 | +`models.providers.openai.baseUrl` and explicitly opt in with |
| 40 | +`browser.ssrfPolicy.dangerouslyAllowPrivateNetwork: true`; private/internal |
| 41 | +image endpoints remain blocked by default. |
38 | 42 | |
39 | 43 | 3. Ask the agent: _"Generate an image of a friendly robot mascot."_ |
40 | 44 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -316,6 +316,47 @@ describe("openai image generation provider", () => {
|
316 | 316 | expect(result.images).toHaveLength(1); |
317 | 317 | }); |
318 | 318 | |
| 319 | +it("allows OpenAI-compatible private image endpoints when browser SSRF policy opts in", async () => { |
| 320 | +mockGeneratedPngResponse(); |
| 321 | + |
| 322 | +const provider = buildOpenAIImageGenerationProvider(); |
| 323 | +const result = await provider.generateImage({ |
| 324 | +provider: "openai", |
| 325 | +model: "flux2-klein", |
| 326 | +prompt: "A simple, clean illustration of a red apple with a green leaf", |
| 327 | +cfg: { |
| 328 | +browser: { |
| 329 | +ssrfPolicy: { |
| 330 | +dangerouslyAllowPrivateNetwork: true, |
| 331 | +}, |
| 332 | +}, |
| 333 | +models: { |
| 334 | +providers: { |
| 335 | +openai: { |
| 336 | +baseUrl: "http://192.168.1.15:8082/v1", |
| 337 | +apiKey: "local-noauth", |
| 338 | +models: [], |
| 339 | +}, |
| 340 | +}, |
| 341 | +}, |
| 342 | +}, |
| 343 | +}); |
| 344 | + |
| 345 | +expect(resolveProviderHttpRequestConfigMock).toHaveBeenCalledWith( |
| 346 | +expect.objectContaining({ |
| 347 | +baseUrl: "http://192.168.1.15:8082/v1", |
| 348 | +allowPrivateNetwork: true, |
| 349 | +}), |
| 350 | +); |
| 351 | +expect(postJsonRequestMock).toHaveBeenCalledWith( |
| 352 | +expect.objectContaining({ |
| 353 | +url: "http://192.168.1.15:8082/v1/images/generations", |
| 354 | +allowPrivateNetwork: true, |
| 355 | +}), |
| 356 | +); |
| 357 | +expect(result.images).toHaveLength(1); |
| 358 | +}); |
| 359 | + |
319 | 360 | it("forwards generation count and custom size overrides", async () => { |
320 | 361 | mockGeneratedPngResponse(); |
321 | 362 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -21,6 +21,7 @@ import {
|
21 | 21 | resolveProviderHttpRequestConfig, |
22 | 22 | sanitizeConfiguredModelProviderRequest, |
23 | 23 | } from "openclaw/plugin-sdk/provider-http"; |
| 24 | +import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime"; |
24 | 25 | import { OPENAI_DEFAULT_IMAGE_MODEL as DEFAULT_OPENAI_IMAGE_MODEL } from "./default-models.js"; |
25 | 26 | import { resolveConfiguredOpenAIBaseUrl } from "./shared.js"; |
26 | 27 | |
@@ -190,6 +191,9 @@ function shouldAllowPrivateImageEndpoint(req: {
|
190 | 191 | if (req.provider === MOCK_OPENAI_PROVIDER_ID) { |
191 | 192 | return true; |
192 | 193 | } |
| 194 | +if (isPrivateNetworkOptInEnabled(req.cfg?.browser?.ssrfPolicy)) { |
| 195 | +return true; |
| 196 | +} |
193 | 197 | const baseUrl = resolveConfiguredOpenAIBaseUrl(req.cfg); |
194 | 198 | if (!baseUrl.startsWith("http://127.0.0.1:") && !baseUrl.startsWith("http://localhost:")) { |
195 | 199 | return false; |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。