From fa459dc2552272153bf70b2cc02c8e9cf046bb9c Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 05:39:03 -0700 Subject: [PATCH] fix(staging): run injection-guard only for native runtime so Scout can answer (D20) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2026-10-31 Scout-staging incident had two causes. PR #3077 fixed the first (litellm was not deployed in commonly-staging, so native turns 401'd on a missing backend). This fixes the second: with litellm up, Scout turns still failed with `llm_error` "Model deepseek-v4-flash: HTTP 401" — litellm's logs show a 401 from https://api.openai.com/v1/moderations. The native runtime's default guardrail pair includes openai-moderation-enforce, which calls OpenAI's moderation API with OPENAI_API_KEY; staging's openai-api-key is an 11-byte placeholder, so that guardrail rejected every native turn before its model call. Prod (values-dev.yaml) already carries `nativeRuntimeGuardrails: "injection-guard"` for exactly this reason (its OpenAI key was the literal "placeholder"). Staging was simply missing the override — the same omission class as the missing litellm stanza. injection-guard is LiteLLM-native (no key, no external service) and keeps the prompt-injection block for untrusted input. No template change (the env is already values-driven); prod render is unchanged. Guard test asserts staging runs injection-guard only and stays in lockstep with prod. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01N4VWLErW29t4LnhRfUy1NH --- .../unit/helm/litellmStagingEnabled.test.js | 15 +++++++++++++++ k8s/helm/commonly/values-staging.yaml | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/backend/__tests__/unit/helm/litellmStagingEnabled.test.js b/backend/__tests__/unit/helm/litellmStagingEnabled.test.js index 4b03a8de3e..8a8e9872ce 100644 --- a/backend/__tests__/unit/helm/litellmStagingEnabled.test.js +++ b/backend/__tests__/unit/helm/litellmStagingEnabled.test.js @@ -61,3 +61,18 @@ describe('staging LiteLLM is enabled so native-runtime agents (Scout) can answer expect(tol == null || (Array.isArray(tol) && tol.length === 0)).toBe(true); }); }); + +describe('staging native runtime runs injection-guard only (no real OpenAI moderation key)', () => { + test('nativeRuntimeGuardrails is injection-guard — matches prod', () => { + // The native runtime's default guardrail pair includes openai-moderation-enforce, + // which calls OpenAI's moderation API with OPENAI_API_KEY. Staging's openai-api-key is + // an 11-byte placeholder (as prod's was), so that guardrail returned HTTP 401 on EVERY + // native turn before the model call — the second half of the 2026-10-31 Scout-staging + // incident. injection-guard is LiteLLM-native (no key, no external service). If staging + // ever drops this override, enabling litellm alone will not make Scout answer. + expect(staging.backend.env.nativeRuntimeGuardrails).toBe('injection-guard'); + // Prod carries the identical override; keep them in lockstep until a real OpenAI + // moderation key lands in the api-keys secret and both can restore the pair together. + expect(staging.backend.env.nativeRuntimeGuardrails).toBe(dev.backend.env.nativeRuntimeGuardrails); + }); +}); diff --git a/k8s/helm/commonly/values-staging.yaml b/k8s/helm/commonly/values-staging.yaml index 83cd90dad6..e0149a4c20 100644 --- a/k8s/helm/commonly/values-staging.yaml +++ b/k8s/helm/commonly/values-staging.yaml @@ -160,6 +160,18 @@ litellm: backend: env: + # injection-guard ONLY — matches prod (values-dev.yaml). The native runtime's + # default guardrail pair (nativeRuntimeService.ts) includes openai-moderation-enforce, + # which calls OpenAI's moderation API with OPENAI_API_KEY. Staging's api-keys + # openai-api-key is an 11-byte placeholder (prod's is the literal "placeholder" too), + # so that guardrail 401'd on EVERY native turn BEFORE the model call — litellm logged + # 401 from https://api.openai.com/v1/moderations and returned HTTP 401 for + # deepseek-v4-flash. This was the second half of the 2026-10-31 Scout-staging incident: + # enabling litellm (above) fixed the missing-backend 401, this fixes the guardrail 401. + # injection-guard is LiteLLM-native (no key, no external service) and keeps the + # prompt-injection block. Restore the pair only after a real OpenAI moderation key + # lands in the staging api-keys secret. Guard test: litellmStagingEnabled.test.js. + nativeRuntimeGuardrails: "injection-guard" agentComputerEnabled: "true" agentComputerSteelApiUrlTemplate: "http://steel-{userId}.commonly-staging.svc.cluster.local:3000" agentComputerSteelCdpUrlTemplate: "http://steel-{userId}.commonly-staging.svc.cluster.local:9223"