- claude provider enabled:false by default (opt-in; was incorrectly true)
- add OpEditor component: per-op form editor for all 9 DSL kinds
(add/move-up/move-down/delete via UI buttons, JSON editor collapsed)
- rename card back to 'CLI Fingerprint Matching' per user feedback
- JSON import/export now collapsible under '▸ Import / export JSON'
- tests updated to explicitly enable claude provider where pipeline behavior
is under test (not the opt-in default)
The sentinel field set by remapToolNamesInRequest() was being included in
the JSON body sent to Anthropic, which rejects unknown top-level fields
with 400 invalid_request_error. Stripped before JSON.stringify in both
code paths:
- buildAndSignClaudeCodeRequest (CC bridge / anthropic-compatible-cc-*)
- base executor serialization path (native claude/ provider)
Pre-existing bug, not introduced by issue #2260 changes.
- Remove dead setWordsForOp function (unused, acknowledged in comment)
- Remove unused obfuscateSensitiveWords import and re-export from systemTransforms
- Increase textarea rows cap from 20→40 for long CC-bridge pipelines (~100 lines JSON)
- Add [SystemTransforms] console.log at both call sites (cc-bridge step 5b + claude native path)
Tests: 58/58 green
v2 of the CC bridge body transforms (issue #2260). Generalizes the
single-provider `ccBridgeTransforms` config (commit e3e962db) into a
per-provider registry keyed by OmniRoute provider id, and wires the
native `claude` OAuth path into the same DSL so raw `claude/<model>`
requests no longer bypass the sanitization layer.
Reference: comment 4459544580 reported a 429 on raw `claude/<model>`
from Open WebUI; CC-bridge-only v1 didn't help that path. v2 closes
three gaps named in the comment:
1. `openwebui` / `open-webui` added to the default obfuscation
word list (new `obfuscate_words` op kind, configurable).
2. Native `claude` provider path now runs the per-provider pipeline
after its existing billing+sentinel prepend (executors/base.ts).
Its default pipeline is cosmetic only (Open WebUI paragraph
anchors + identity-prefix drop + ZWJ obfuscation); it deliberately
omits `inject_billing_header` so it never collides with the native
prepend.
3. Open WebUI paragraph anchors (github.com/open-webui/open-webui,
openwebui.com, docs.openwebui.com) and identity prefix
("You are Open WebUI") added to both `claude` and CC bridge
default pipelines.
API surface:
- New module: open-sse/services/systemTransforms.ts
* `SystemTransformsConfig { providers: Record<id, { enabled, pipeline }> }`
* `TransformOp` extends the base CC bridge op set with
`obfuscate_words { words[], targets[] }`.
* `applySystemTransformPipeline(providerId, body, config?)`
routes to the right per-provider pipeline (with CC bridge prefix
match so `anthropic-compatible-cc-*` all share one config).
* `setSystemTransformsConfig` accepts both legacy single-provider
shape (migrates into providers[anthropic-compatible-cc]) and the
new per-provider shape (merges with defaults for unset providers).
- Native claude wedge: executors/base.ts now calls
`applySystemTransformPipeline(PROVIDER_CLAUDE, tb)` after the
existing billing+sentinel prepend (line ~789).
- CC bridge step 5b: claudeCodeCompatible.ts step 5b switched from
`applyCcBridgeTransformPipeline` (single-config) to
`applySystemTransformPipeline(PROVIDER_CC_BRIDGE, body)`. The
underlying ccBridgeTransforms.ts module remains the base executor
that systemTransforms.ts delegates to for the shared op kinds —
no rename to keep the diff reviewable, and all 30 base-op tests
stay green.
- Settings:
* Zod schema gains `systemTransforms.providers[*]` with full
discriminated union over the 9 op kinds (including
obfuscate_words).
* Legacy `ccBridgeTransforms` zod field kept for back-compat read;
runtime now feeds it through `setSystemTransformsConfig` migration
shim so persisted Phase-2 data keeps working. v2 `systemTransforms`
wins on conflict (applied last).
* Runtime settings registry gains `systemTransforms` reload section
next to legacy `ccBridgeTransforms`.
- UI rewrite (RoutingTab.tsx): the two standalone cards (CLI
Fingerprint + CC Bridge Transforms) collapse into one
"Provider Upstream Compatibility" card. Per-provider tiles render
the pipeline summary + JSON editor with client-side shape
validation + Apply / Reset. Copy clarifies that no local Claude
Code binary is required (the lock badge on the Claude tile means
the fingerprint is force-applied for OAuth account safety, not
that the user must install the CLI).
Tests:
- tests/unit/system-transforms.test.ts (22 new tests covering
defaults, per-op semantics, ordering, per-provider routing, opt-in
pass-through, Open WebUI fixture, migration shim, idempotency).
- tests/unit/cc-bridge-transforms.test.ts (30 base-op tests
unchanged, still green).
- tests/unit/claude-code-compatible-{helpers,request}.test.ts and
8 related claude/cc/executor test files all green (140 tests).
Closes#2260.
Adds a config-driven DSL pipeline for normalizing the system blocks sent
to Anthropic via the Claude Code bridge. Removes third-party agent CLI
fingerprints (OpenCode, Cline, Cursor, Continue) and prepends the SDK
identity + signed billing header so the request looks classifier-correct
regardless of which client originally sent it.
Why a DSL: future fingerprint defenses ship as config rows, not new code.
Mode presets (basic/aggressive/custom), reorder, add, delete, reset — all
hot-reloaded through the existing runtimeSettings registry.
Files
- open-sse/services/ccBridgeTransforms.ts (~440 LOC; 8 op-kinds, executor,
buildBillingHeaderValue ported from ex-machina cch.ts, singleton config
matching cliFingerprints pattern, T4-200 fixture-matching defaults)
- tests/unit/cc-bridge-transforms.test.ts (30 tests; per-op, idempotency,
ordering, verbatim-OpenCode regression)
- open-sse/services/claudeCodeCompatible.ts (step 5b in buildAndSign
pipeline + re-exports for downstream)
- open-sse/executors/base.ts (maintainer comment: native
OAuth path is intentionally not routed through the DSL; it has its own
billing prepend at lines 744-773)
- src/lib/config/runtimeSettings.ts (RuntimeReloadSection +
snapshot + default + normalize + apply + change-detection)
- src/shared/validation/settingsSchemas.ts (zod discriminatedUnion over
the 8 op-kinds, max 50 ops per pipeline)
- src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx
(Settings UI Card: enabled toggle, ordered pipeline list with reorder
buttons, add-op dropdown, reset-to-defaults)
- src/i18n/messages/en.json (10 new strings under
settings.ccBridgeTransforms.*)
Defaults match the empirical T4=200 layout: [billing, identity, sanitized,
extras]. Verified against prod call logs (1c1946a2 = plugin-200,
de8ab5bd = raw-429) and four-variant live A/B test (T1=429 baseline,
T2=429 phrase-only, T3=429 sanitizer-only, T4=200 with billing+identity).
Test result: 30/30 ccBridgeTransforms + 6/6 baseline claude-code-compatible-helpers green.
Scope: CC bridge surface only (open-sse/services/claudeCodeCompatible.ts).
Native claude OAuth path is NOT routed through the DSL — it has its own
billing prepend mechanism that already works.
Refs: https://github.com/diegosouzapw/OmniRoute/issues/2260
CodeQL js/stack-trace-exposure does not recognize sanitizers reached
through a custom helper indirection — flagging callsites like
open-sse/utils/error.ts::errorResponse and
open-sse/executors/cursor.ts::buildErrorResponse even though both route
through sanitizeErrorMessage().
Record the dismissal precedent (alerts #224 and #231, May 2026):
- Add a "Known CodeQL limitation" section in
docs/security/ERROR_SANITIZATION.md
- Extend CLAUDE.md Hard Rule #14 with the precedent
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PATH_REGEX introduced in 1a39c31f used a greedy character class
([\w\-./\\]+) that can backtrack quadratically on inputs like
"///////..." — flagged by CodeQL js/polynomial-redos (#232).
Replace it with simple whitespace tokenization + a length-bounded
prefix-check helper. Same observable behaviour (paths replaced with
<path>), but linear time regardless of input shape. Adds a 4096-char
input cap as defence in depth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes the 4 fixable alerts opened in the recent scan and adds enforceable
guardrails so future development follows the same pattern.
Code fixes:
- src/mitm/cert/install.ts: pass certPath/certName/action via exec()'s env
option instead of string-interpolating them into the bash script
(CodeQL js/shell-command-injection-from-environment #225)
- scripts/docs/{gen-provider-reference,add-frontmatter,fix-internal-links}:
escape backslash before other regex/markdown metacharacters
(CodeQL js/incomplete-sanitization #227, #228, #229)
Documentation (mandatory patterns):
- docs/security/PUBLIC_CREDS.md — embedding public upstream OAuth/Firebase
identifiers via resolvePublicCred(); never as string literals
- docs/security/ERROR_SANITIZATION.md — routing every error response through
sanitizeErrorMessage()/buildErrorBody(); never raw err.stack/err.message
- CLAUDE.md: 4 new Hard Rules (#11-#14) + Security section + scenario notes
- AGENTS.md, CONTRIBUTING.md: cross-reference the two new docs
- SECURITY.md: extended Hard Security Rules with the new mandatory patterns
- docs/README.md: index entries pointing to the two new docs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Embed Gemini, Antigravity and Windsurf public OAuth/Firebase identifiers
(extracted from upstream CLI binaries) through a XOR-masked byte sequence
in open-sse/utils/publicCreds.ts instead of source literals, so pattern
scanners (GitHub Secret Scanning, Semgrep) stop raising false positives on
every release. decodePublicCred passes raw values through unchanged for
users who already have plaintext in their .env (no migration needed).
- New utils/publicCreds.ts with decode/encode + tests
- Replace 6 hardcoded Google client_id/secret in oauth.ts + providerRegistry
- Drop literals from .env.example (comment-only documentation)
- Sanitize error messages inside buildErrorBody so every caller (incl.
createErrorResult) is covered; cursor.ts now reuses the shared helper
- Cover the new helpers with unit tests (publicCreds + error sanitization)
Resolves the open code-scanning js/stack-trace-exposure findings and the
secret-scanning Google API Key alert without breaking existing setups.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Resolve <<<<<<< HEAD conflict in RoutingTab.tsx by keeping the
HEAD version with aria-disabled and pre-computed titleText
- Fix inconsistent indentation in CLI help text (providers commands
and CLI Tools section)
After cherry-picking PR #2231, the function signature changed from
positional (provider, model) to object ({ provider, model }). Fixes the
2 pre-existing tests that still used the old positional style.
- chatCore.ts: pass {requestId:skillRequestId,messageIndex:0} to cacheReasoningFromAssistantMessage
- responseSanitizer.ts: widen isDeepSeekV4Model regex to match all deepseek-v4 variants
Fixes Anthropic HTTP 400 errors (~49/h on claude-opus-4-7) by preserving
the latest assistant message's thinking blocks verbatim instead of
rewriting them to redacted_thinking.
Co-authored-by: NomenAK <anton@nomenak.dev>
The root package.json was updated in 52f3285d to drop Node 20.x support
(http-proxy-middleware 4.x requirement). electron/package.json had no
engines field declared, leaving the desktop build implicitly permissive.
Adds the same constraint (>=22.22.2 <23 || >=24.0.0 <27) to keep the
electron workspace consistent with the root engine policy.
Refs: #2228
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
http-proxy-middleware 4.x (introduced via #2228) requires Node >=22.15.0.
Updated engines.node to >=22.22.2 <23 || >=24.0.0 <27 (drops 20.x).
BREAKING CHANGE: users on Node 20.x must upgrade to Node 22.22.2+ or 24+.
Refs: #2228
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- antigravity: AntigravityCredentials.projectId widened to string|null
to match base ProviderCredentials shape post-#2227 squash merge.
- responses-handler: heartbeat assertion updated for #2233's new
openai-responses-in-progress shape (was: keepalive comment).
- search-registry: expected count is now 12 (ollama-search +
zai-search both landed in this release).
After merging PRs #2221 (ModelSync shared loopback readiness gate + IPv4 force)
and #2219 (Antigravity loadCodeAssist bootstrap + fetchAvailableModels fallback)
into release/v3.8.0, two test suites needed updates to match the new routing:
- tests/unit/model-sync-route.test.ts:
* resetStorage() now calls __resetLoopbackReadinessForTests() so the
module-level __loopbackReadyPromise cache does not leak between tests.
* Every fetch mock now answers the /__readiness_probe__/ URL with 404 so
the gate opens immediately (any HTTP response satisfies the probe).
* Self-fetch target URL assertions updated from http://localhost/...
to http://127.0.0.1:20128/... per PR #2221's IPv4-force.
- tests/unit/provider-models-route.test.ts:
* The Antigravity discovery-retry test now treats loadCodeAssist calls as
non-fatal failures so the discovery path is still exercised.
* The expected discovery URL sequence is updated to the new
fetchAvailableModels-first order introduced by PR #2219.
The local-aliases-precedence path used `typeof aliases[parsed.model] === "string"`
to guard string-only operations, but TypeScript does not narrow the variable
`directTarget` from that index-expression test — the variable retained the union
type ModelAliasValue (string | object), so `indexOf`/`slice` were typed as
property accesses on the object branch and the strict-core typecheck failed.
Refactors to capture `directTarget` first and run `typeof directTarget === "string"`
on the variable, which TS does narrow. No runtime semantics change — local-aliases
tests still pass.