Skip to content

Security audit fixes - #13330

Merged
diegosouzapw merged 8 commits into
diegosouzapw:release/v3.8.52from
JJAbrams-eng:fix/security-audit-2026-09-10
Oct 9, 2026
Merged

diegosouzapw merged 8 commits into
diegosouzapw:release/v3.8.52from
JJAbrams-eng:fix/security-audit-2026-09-10

Conversation

@JJAbrams-eng

Copy link
Copy Markdown
Contributor

Summary

  • Describe the user-facing or operational change.

Related Issues

  • Closes #
  • Related to #

Validation

Choose the change type and focused loop from the
Contribution Golden Path. The full unit suite,
Vitest, the 60% coverage gate, and the production build all run in CI on this PR (#8329):

  • Change type: provider / routing / UI / i18n / CLI / DB / build-deploy / other
  • Focused tests and category gates from the golden path
  • npm run lint
  • Reconciled with the current active release base; focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR
  • SonarQube is temporarily opt-in while the private project has no quota; it is not a PR gate.

Tests Added Or Updated

  • List every changed or added automated test file.
  • If no production code changed, state that here.

Coverage Notes

  • If this PR changes src/, open-sse/, electron/, or bin/, explain which tests cover the change.
  • If coverage moved down in any touched file, explain why and what follow-up task will recover it.

Reviewer Notes

  • Call out any risky areas, migrations, feature flags, or manual validation that reviewers should know about.

… in workspace audit

- executors: pin outbound DNS resolution (createPinnedFetch, shared with
  remoteImageFetch.ts) to close a DNS-rebinding bypass of the SSRF guard on
  the provider-dispatch path, including the operator-override baseUrl case
- db: never persist plaintext API keys after creation/regeneration
  (redacted:<id> placeholder, key_hash-only auth lookups); encrypt
  auto-generated JWT_SECRET/API_KEY_SECRET at rest; fail startup in
  production when STORAGE_ENCRYPTION_KEY is unset instead of silently
  falling back to plaintext credential storage
- db: add a dedicated, encrypted-at-rest internal-service key so the
  plaintext-redaction fix above doesn't silently break combo health-check,
  cloud-sync verify, and the MITM agent-bridge auth (diegosouzapw#6403); reveal endpoint
  now reports 410 for a redacted key instead of returning a bogus value;
  key list view falls back to the stored key_prefix for a masked label
- rate limiter: fail toward the existing in-memory limiter on a Redis
  error instead of failing open
- mcp: force per-tool scope enforcement for callers resolved from a
  narrow mcp:connect-only key, regardless of the OMNIROUTE_MCP_ENFORCE_SCOPES
  default; give agentSkillTools their documented scopes so they don't break
  once enforcement applies

All touched areas covered by updated/passing unit tests; typecheck clean.
@JJAbrams-eng
JJAbrams-eng force-pushed the fix/security-audit-2026-09-10 branch from 80df10c to 45b54e8 Compare September 11, 2026 08:37
@diegosouzapw

Copy link
Copy Markdown
Owner

Dense but well-reasoned batch — five independent, real security fixes (DNS-rebinding pinning
extended from image fetch to the provider dispatch path, API keys no longer persisting
plaintext with a clean legacy-row migration, the rate limiter no longer failing fully open on a
Redis outage, a boot-time fail-fast for a missing STORAGE_ENCRYPTION_KEY, and an MCP scope-
enforcement gap closed for narrow-scoped HTTP keys). The two existing test files you updated
(api-key-reveal-route, pick-internal-api-key) pass cleanly against the PR head. Before merge,
please add tests for the two new code paths that don't have any yet: resolvePinnedFetch() in
the executor dispatch path (the change with the largest new attack surface here) and the MCP
forceEnforceScopes branch — plus a changelog fragment. Also worth filling in the PR body
(currently the empty template) with the actual summary.

…P forceEnforceScopes gap

Adds direct coverage for the two new code paths in diegosouzapw#13330 that had no test
yet — BaseExecutor.resolvePinnedFetch() (the DNS-rebinding pin now applied to
provider dispatch, GHSA-cmhj-wh2f-9cgx) and forceEnforceScopes in
withScopeEnforcement() (forces MCP scope checks for HTTP authInfo callers
without manage/admin scope even when OMNIROUTE_MCP_ENFORCE_SCOPES is off) —
plus a changelog fragment for the batch's five security fixes.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@diegosouzapw diegosouzapw changed the title Security audit fixes [defer] Security audit fixes Sep 29, 2026
@diegosouzapw diegosouzapw added the deferred-v3.8.52 Grande demais / suspeito para o lote atual; precisa de sessão dedicada no ciclo v3.8.52 label Sep 29, 2026
@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.51 to release/v3.8.52 September 29, 2026 11:25
@diegosouzapw

Copy link
Copy Markdown
Owner

Re-homed to release/v3.8.52: v3.8.51 entered its release freeze, so the branch now belongs to the release captain and development continues on the next cycle. Nothing is wrong with this PR — it just needed a live base. No action needed from you; CI will re-run against the new base.

JJAbrams-eng and others added 2 commits September 29, 2026 19:42
… the dispatch DNS pin

Merge conflicts (server.ts, db/apiKeys.ts, remoteImageFetch.ts, executors/base.ts) resolved
keeping the tip's behavior plus this PR's MCP scope forcing.

- Dispatch DNS pin redesigned: the pin is only applied to a plain direct connection (no context
  or env proxy, no explicit direct sentinel, no TLS impersonation) and is handed to the ambient
  global fetch as `init.dispatcher`, so outbound proxies, request logging and stubbed fetches
  keep working (previously a private undici fetch bypassed the patched fetch).
- dnsPin.ts now reuses the tip's dnsPinnedFetch.ts primitives instead of duplicating them;
  remoteImageFetch.ts is taken from the tip unchanged.
- API-key redaction (`redacted:<id>` placeholders, hash-only auth, internal service key) is
  dropped from this PR: keys are recoverable server-side by design today (cli-tools keys rawKey).
- forceEnforceScopes moved to scopeEnforcement.ts::shouldForceScopeEnforcement, and the SSRF
  string guard moved next to the pin (dispatchPin.ts) to keep server.ts/base.ts inside their
  file-size ceilings.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
… instead of pre-resolving DNS

Replaces the pre-resolve + private-fetch pin: the guarded dispatcher validates the addresses of
the lookup the connection is actually made with, rides on the ambient patched fetch (so stubs,
request logging and the direct-path policies keep working), is skipped whenever a proxy, TLS
impersonation or the direct sentinel applies, and does no DNS work at dispatch time. The
pre-resolve helpers (dnsPin.ts, createPinnedDispatcher) are dropped as unused.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@diegosouzapw diegosouzapw removed the deferred-v3.8.52 Grande demais / suspeito para o lote atual; precisa de sessão dedicada no ciclo v3.8.52 label Oct 1, 2026
@diegosouzapw diegosouzapw changed the title [defer] Security audit fixes Security audit fixes Oct 1, 2026
diegosouzapw and others added 4 commits October 8, 2026 03:59
# Conflicts:
#	open-sse/executors/base.ts
#	open-sse/utils/proxyDispatcher.ts
…n the Redis-path test with the in-memory fallback

The release tip now sends execute()'s transport through validationFetch() (strict-validation fence); the merge plugs dispatchGuarded in as that fence's transport. The new execute() case proves both the fence (redirect lock, beforeFetch) and the guarded dispatcher reach the global fetch.

rate-limiter-backup-flag-not-test-mode detected the Redis path through the old fail-open { allowed: true }. This PR makes a Redis failure fall back to in-memory limiting, so the test now observes the Redis path through the limiter's 'Redis eval failed' log, and the NODE_ENV=test case asserts Redis is never reached.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
An empty STORAGE_ENCRYPTION_KEY stays the documented disabled contract.
Exiting in production crash-loops Docker images that start from the
example env with NODE_ENV=production.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…s were retranslated

The English docs/frameworks/MCP-SERVER.md changed in this PR. Record that
source hash at the top of .i18n-state.json so the docs-drift gate does not
treat the edit as a new violation. Locale source_hash and target_hash stay
on the previous English source: the mirrors are stale and non-blocking.
A full i18n:run is not part of this fix.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@diegosouzapw
diegosouzapw merged commit fb7e885 into diegosouzapw:release/v3.8.52 Oct 9, 2026
3 checks passed
@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks @JJAbrams-eng — the dispatch path now checks operator-supplied base URLs at connect time, the rate limiter falls back to memory when Redis errors, MCP scope checks apply to a real Authorization header even when the env flag is off, and secrets at rest stay encrypted. A missing STORAGE_ENCRYPTION_KEY logs a loud boot warning and the process keeps serving, because an empty key is the documented way to leave encryption disabled. That commit is on your branch, co-authored. This is in the upcoming release.

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