Skip to content

feat(compliance): add no-stealth-providers option - #3340

Merged
steebchen merged 1 commit into
mainfrom
feat/compliance-block-stealth-providers
Jul 31, 2026
Merged

steebchen merged 1 commit into
mainfrom
feat/compliance-block-stealth-providers

Conversation

@steebchen

@steebchen steebchen commented Jul 31, 2026 •

Copy link
Copy Markdown
Member

Adds a No stealth providers toggle to the enterprise provider compliance policy (Settings → Compliance), so stealth providers can be excluded explicitly instead of only incidentally.

Stealth providers (Glacier, Iceberg, Granite, …) have a null dataPolicy and headquarters, so they already fail every existing requirement fail-closed. But that only holds if at least one other requirement is on — an org that wants a policy consisting solely of "no undisclosed platforms" previously had no way to express it. This makes the exclusion intentional and self-documenting in the impact preview.

Changes

  • packages/models: blockStealthProviders on ProviderCompliancePolicy, plus the matching ComplianceFailureReason. Because the check is provider-level (not data-policy-level), it lives in a new getProviderRequirementFailures(provider, policy) that getProviderComplianceFailures composes — the dashboard pickers need requirement-level failures without the fine-grained provider lists, and now pick up the stealth reason too.
  • isStealthProvider moves from helpers.ts to providers.ts (helpers imports providers, so the compliance predicates couldn't call it without a circular import). Same export surface via the package index; only the internal import paths in specs changed.
  • apps/api: accept the new field in providerCompliancePolicySchema. The policy is a JSON column, so no migration.
  • apps/ui: new requirement switch + "Stealth provider" blocked-chip reason.
  • apps/docs: requirement table row and a paragraph on why the option exists.

Gateway enforcement needed no change — it routes through isProviderCompliant, which takes the full ProviderDefinition. Custom-provider attestations are unaffected: a self-attested deployment is never stealth.

Testing

  • New blockStealthProviders suite in packages/models/src/compliance.spec.ts covering the enabled/disabled/toggle-off matrix, non-stealth providers, every catalogue stealth provider, and attestation non-applicability.
  • pnpm test:unit — 3475 passed, 2 skipped.
  • pnpm build — clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a compliance option to block stealth providers.
    • Added clear compliance failure messaging when a stealth provider is blocked.
    • Provider compliance evaluation now consistently applies all configured requirements.
  • Documentation

    • Documented the new “No stealth providers” compliance requirement.
  • Tests

    • Added coverage for stealth-provider blocking, disabled policies, supported providers, and custom-provider attestations.

Adds a `blockStealthProviders` toggle to the enterprise provider
compliance policy so stealth providers can be excluded explicitly,
without having to enable an unrelated certification requirement.

Stealth providers have a null dataPolicy/headquarters, so they already
fail every existing requirement fail-closed — this makes the exclusion
intentional rather than incidental.

Moves `isStealthProvider` from helpers.ts into providers.ts so the
compliance predicates can use it without a circular import, and adds
`getProviderRequirementFailures` for the dashboard pickers, which need
requirement-level failures without the fine-grained provider lists.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 31, 2026 17:42
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The PR adds optional stealth-provider blocking to provider compliance policies. Model logic detects providers without required baseUrl configuration, the UI evaluates the new requirement, the API accepts the policy, and tests and documentation cover the behavior.

Changes

Stealth provider compliance

Layer / File(s) Summary
Policy contract and stealth detection
apps/api/src/routes/organization.ts, packages/models/src/providers.ts, packages/models/src/helpers.ts
The policy accepts blockStealthProviders. Provider models detect stealth providers and expose the new failure reason. The helper implementation is moved from helpers.ts to providers.ts.
Failure aggregation and UI enforcement
packages/models/src/providers.ts, apps/ui/src/app/dashboard/[orgId]/org/compliance/compliance-client.tsx
Provider requirement failures include stealth-provider violations. The UI displays the new failure and uses consolidated requirement evaluation for provider selection.
Compliance validation and documentation
packages/models/src/compliance.spec.ts, packages/models/src/providers.spec.ts, apps/docs/content/features/compliance.mdx
Tests cover enabled, disabled, catalogue, non-stealth, and custom-provider cases. Documentation describes the “No stealth providers” requirement.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OrganizationRoute
  participant ComplianceClient
  participant ProviderRequirements
  participant StealthDetection
  OrganizationRoute->>ComplianceClient: Provide blockStealthProviders policy
  ComplianceClient->>ProviderRequirements: Evaluate provider requirements
  ProviderRequirements->>StealthDetection: Inspect baseUrl configuration
  StealthDetection-->>ProviderRequirements: Return stealth status
  ProviderRequirements-->>ComplianceClient: Return compliance failures
Loading

Possibly related PRs

Suggested reviewers: smakosh

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an option to block stealth providers in compliance policies.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/compliance-block-stealth-providers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/docs/content/features/compliance.mdx`:
- Around line 31-36: Revise the fail-closed statement in the compliance
requirements documentation to apply only to certification, data-policy, and
headquarters requirements. Preserve the behavior that a non-stealth provider
with unknown attributes can pass when only the No stealth providers requirement
is active.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 84b65dbd-9722-4072-9745-f1f8d2215838

📥 Commits

Reviewing files that changed from the base of the PR and between 9381e73 and 3d81798.

📒 Files selected for processing (7)
  • apps/api/src/routes/organization.ts
  • apps/docs/content/features/compliance.mdx
  • apps/ui/src/app/dashboard/[orgId]/org/compliance/compliance-client.tsx
  • packages/models/src/compliance.spec.ts
  • packages/models/src/helpers.ts
  • packages/models/src/providers.spec.ts
  • packages/models/src/providers.ts

Comment on lines +31 to +36
| **No stealth providers** | it is not a stealth provider |

Every requirement is **fail-closed**: a provider passes only if its published data policy explicitly satisfies the requirement. If an attribute is unknown for a provider, that provider is treated as non-compliant.

Stealth providers are undisclosed platforms that LLM Gateway routes to without naming the operator, so their data policy and headquarters are unknown. That already makes them fail every certification and data-policy requirement above, but **No stealth providers** excludes them explicitly — useful when you want them gone without turning on any other requirement.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Limit the fail-closed statement to attribute requirements.

Line 33 says every requirement needs an explicit published data policy. A non-stealth provider with unknown data-policy attributes passes when blockStealthProviders is the only active requirement. Update the statement to cover certification, data-policy, and headquarters requirements only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/docs/content/features/compliance.mdx` around lines 31 - 36, Revise the
fail-closed statement in the compliance requirements documentation to apply only
to certification, data-policy, and headquarters requirements. Preserve the
behavior that a non-stealth provider with unknown attributes can pass when only
the No stealth providers requirement is active.

@steebchen
steebchen enabled auto-merge July 31, 2026 17:46
@steebchen
steebchen added this pull request to the merge queue Jul 31, 2026
Merged via the queue into main with commit 587951a Jul 31, 2026
12 checks passed
@steebchen
steebchen deleted the feat/compliance-block-stealth-providers branch July 31, 2026 18:09
steebchen added a commit to AmineAce/llmgateway that referenced this pull request Aug 3, 2026
## Summary

The video pipeline forwards upstream provider error bodies to the client
**verbatim**, skipping the stealth redaction every other pipeline
already applies. `fetchUpstreamJson`
(`apps/gateway/src/videos/videos.ts:2788`) throws `HTTPException(status,
{ message: body.error.message })` on `!response.ok` — with the full raw
text when the error isn't JSON — and re-forwards `body.msg` untouched on
the application-error branch; `app.ts`'s `onError` then renders that
message to the client via `renderGatewayError`. Unlike `chat`, `rerank`
and `embeddings`, `videos.ts` never calls `shouldRedactProviderError`.

For stealth providers this leaks exactly what theopenco#3340 and the
`stealth-provider-errors.ts` invariant exist to protect: provider
identity, hostnames and vendor markings inside the raw error body (e.g.
`quota exceeded at https://<secret-host>` from `avalanche`, whose base
URL is a deployment secret).

## Fix

Mirror the sibling pattern (`rerank.ts:1060`, `embeddings.ts:1347`):

- New pure helper `videos/upstream-error.ts`:
`clientFacingUpstreamMessage(providerId, statusCode, rawMessage)` —
`redactedProviderErrorText(statusCode)` when
`shouldRedactProviderError(providerId)`, the raw message unchanged
otherwise. Same comment as the sibling sites.
- `fetchUpstreamJson` takes an optional `providerId` and routes both
throw branches through the helper (the `Upstream provider error
(<status>)` fallback was already generic and stays as-is). Internal
`logger.warn` keeps the full body, consistent with the rest of the
codebase.
- All 11 call sites pass `providerContext.providerId` (each already has
it in scope). Non-stealth providers are byte-identical to before.

## Tests

New pure spec `videos/upstream-error.spec.ts` (DB-light, no harness):

- stealth (`avalanche`): the raw message never reaches the client — the
response is exactly `redactedProviderErrorText(500)` and contains
neither the secret host nor the original message;
- non-stealth (`openai`): raw message passes through unchanged;
- `undefined` provider: raw message passes through unchanged.

Adversarial cycle run: commenting the guard makes the stealth test
**fail** (`expected 'quota exceeded at https://plataforma-…' to be
'Upstream provider error (500…)'`); restoring it passes again.

## Verification

- New spec 3/3 ✅ · `normalize-streaming-error.spec.ts` 13/13 ✅ (the
stealth sibling suites stay green)
- `turbo run build --filter=gateway` ✅ (10/10 tasks — full typecheck of
`videos.ts` + helper + spec)
- eslint ✅ · prettier ✅ (touched files)

Not verified: end-to-end against a real stealth provider (no DB/Docker
on this machine) — the `fetchUpstreamJson` → `HTTPException` →
`renderGatewayError` path is verified by reading, and the redaction
decision itself by the new unit tests.

---

Disclosure: an AI coding assistant helped survey the pipeline and draft
the test scaffolding. The diff is small and was verified the slow way: I
read every call site, confirmed `videos.ts` is the only pipeline without
the guard, and ran the revert-the-guard check above to prove the test
actually catches the leak. I own the change and will follow up on review
comments.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Sensitive upstream provider details are now hidden from error messages
for supported providers.
* Video creation, media uploads, and background processing return
consistent generic errors when provider requests fail.
* Non-sensitive provider errors continue to display their original
details.
* Failed video status responses now include the appropriate sanitized
error message.

* **Tests**
* Added coverage for HTTP, application-level, and background
video-processing error redaction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

## Follow-up: async video-job redaction

This update also redacts stealth-provider failures before the worker
persists the public job error, so the asynchronous status endpoint
cannot expose upstream response text or network details.

It adds a full `POST /v1/videos` leak-regression case and a
worker-to-status regression case, moves the HTTP helper into the common
stealth-error module, and requires provider context at every video
upstream request.

---------

Co-authored-by: Luca Steeb <contact@luca-steeb.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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