Skip to content

fix(proxy): resolve registry assignments for combo and key levels - #3048

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.8from
terence71-glitch:fix/proxy-registry-specific-levels
Jun 1, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.8from
terence71-glitch:fix/proxy-registry-specific-levels

Conversation

@terence71-glitch

Copy link
Copy Markdown
Contributor

Summary

GET /api/settings/proxy?level=...&id=... previously consulted the proxy
registry only for global and provider levels. Requests for combo and
key fell straight through to the legacy getProxyForLevel() path, so a
proxy assigned through the registry UI was invisible to per-level reads at
those two levels — the response showed the old proxyConfig.combos /
proxyConfig.keys value (or null) instead of the registry assignment.

This PR routes all four levels through a single registry-lookup helper, with
the legacy store kept as a fallback when no registry assignment exists.

Changes

src/app/api/settings/proxy/route.ts

  • New PROXY_LEVEL_TO_REGISTRY_SCOPE map translating the route's public
    level names to the canonical ProxyScope used by src/lib/db/proxies.ts:
    global   -> global
    provider -> provider
    combo    -> combo
    key      -> account
    
    The key -> account entry mirrors normalizeScope() in proxies.ts:157,
    where account is the canonical internal scope and key is the legacy
    public-API alias.
  • New helpers:
    • getRegistryScopeForLevel(level) — typed map lookup.
    • getRegistryProxyForLevel(level, id) — fetches the assignment for the
      scope, returns the underlying proxy_registry row (with secrets), or
      null when there is no assignment or the level is unknown.
    • toProxyConfig(proxyData) — extracts { type, host, port, username, password } from a registry row. Previously inlined twice.
  • GET handler collapses the four per-level branches into one:
    if (level) {
      const proxyData = await getRegistryProxyForLevel(level, id);
      if (proxyData) {
        return Response.json({
          level,
          id: level === "global" ? null : id,
          proxy: toProxyConfig(proxyData),
        });
      }
      const proxy = await getProxyForLevel(level, id);
      return Response.json({ level, id, proxy });
    }
  • Full-config GET path also uses toProxyConfig() for the provider loop,
    removing the last inline duplicate.

tests/unit/route-edge-coverage.test.ts

  • New test
    settings proxy route resolves combo and key registry assignments with legacy fallback:
    1. PUTs legacy combos.comboA and keys.accountA proxies — asserts both
      GETs return the legacy values (legacy fallback path).
    2. Creates two proxies via localDb.createProxy, assigns one to
      (combo, comboA) and one to (account, accountA) via
      assignProxyToScope.
    3. Re-issues both per-level GETs — asserts they now return the registry
      proxy hostnames, usernames, and decrypted passwords (registry path).

Behavior contract preserved

  • global: registry uses assignments[0] (there is only ever one row,
    keyed by __global__); response forces id: null.
  • Non-global levels: assignment.scopeId === id match. mapAssignmentRow
    only strips __global__ for the global scope, so combo and account
    scopeIds round-trip unchanged.
  • Registry-first, legacy-fallback ordering is unchanged.
  • No change to PUT or DELETE handlers.

Naming note

The route accepts only key (not account) as the level name on the
public surface. This matches the docstring, the legacy proxyConfig.keys
map, and the PUT body's keys field. If a caller passes
?level=account, the level is unknown to the route, the registry helper
returns null, and the request falls through to legacy
getProxyForLevel("account", ...). This is intentional — keeping the
public-vs-internal naming boundary intact.

Test plan

  • node --import tsx/esm --test tests/unit/route-edge-coverage.test.ts
    — new test passes; existing prefers proxy registry assignments…
    test still passes (no regression on global/provider paths).
  • npm run typecheck:core — clean.
  • npm run lint — 0 errors.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the proxy settings API route to consolidate and simplify proxy assignment retrieval across different levels (global, provider, combo, and key) using helper functions, and adds corresponding unit tests for combo and key registry assignments. Feedback was provided to address a potential prototype lookup vulnerability in the getRegistryScopeForLevel function by safely checking if the key exists on the mapping object before performing the lookup.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/app/api/settings/proxy/route.ts Outdated
Comment on lines +62 to +66
function getRegistryScopeForLevel(level: string) {
return PROXY_LEVEL_TO_REGISTRY_SCOPE[
level as keyof typeof PROXY_LEVEL_TO_REGISTRY_SCOPE
];
}

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.

medium

Using a direct lookup on PROXY_LEVEL_TO_REGISTRY_SCOPE with level as keyof typeof PROXY_LEVEL_TO_REGISTRY_SCOPE can lead to prototype lookup issues if level matches built-in object properties (e.g., "toString", "constructor", "valueOf"). If a client passes ?level=toString, the lookup will return a function instead of undefined, which could cause runtime errors or unexpected behavior when passed to downstream database queries.

To prevent this, safely check if the key exists on the object using Object.prototype.hasOwnProperty.call before performing the lookup.

function getRegistryScopeForLevel(level: string): "global" | "provider" | "combo" | "account" | undefined {
  if (Object.prototype.hasOwnProperty.call(PROXY_LEVEL_TO_REGISTRY_SCOPE, level)) {
    return PROXY_LEVEL_TO_REGISTRY_SCOPE[
      level as keyof typeof PROXY_LEVEL_TO_REGISTRY_SCOPE
    ];
  }
  return undefined;
}

@kilo-code-bot

kilo-code-bot Bot commented Jun 1, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The PR refactors the proxy settings route to use the proxy registry for combo and key level proxy resolution, with proper legacy fallback. The changes are well-structured and maintain backward compatibility.

Files Reviewed (2 files)
  • src/app/api/settings/proxy/route.ts — 35 lines added, 44 lines removed (refactored with helper functions)
  • tests/unit/route-edge-coverage.test.ts — 75 lines added (test coverage for combo/key registry assignments)

Overview

Changes:

  • Added PROXY_LEVEL_TO_REGISTRY_SCOPE mapping to normalize API level names to registry scope names (key → account)
  • Created getRegistryScopeForLevel() and getRegistryProxyForLevel() helper functions
  • Created toProxyConfig() helper to avoid repetitive proxy config object construction
  • Refactored GET handler to consolidate redundant conditional blocks into a single flow
  • Added test covering registry proxy resolution for combo and key levels with legacy fallback

Security & Error Handling:

  • All routes use requireManagementAuth for authentication
  • Input validation handled by Zod schema (updateProxyConfigSchema)
  • Legacy fallback gracefully handles missing registry entries
  • Type safety preserved with proper nullable checks

Test Coverage:

  • New test "settings proxy route resolves combo and key registry assignments with legacy fallback" validates both legacy and registry proxy resolution paths
  • Tests verify proxy host, username, and password are correctly returned from registry
  • Edge cases covered for missing id, invalid levels, etc.

Reviewed by laguna-m.1-20260312:free · 2,823,953 tokens

@terence71-glitch

Copy link
Copy Markdown
Contributor Author

getRegistryScopeForLevel() now verifies the requested level with Object.prototype.hasOwnProperty.call(...) before indexing PROXY_LEVEL_TO_REGISTRY_SCOPE, so inherited object properties like toString/constructor cannot be treated as valid registry scopes.

Validation rerun:

  • npx eslint src/app/api/settings/proxy/route.ts tests/unit/route-edge-coverage.test.ts
  • npx cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --test --test-force-exit tests/unit/route-edge-coverage.test.ts

Result: 16 pass, 0 fail.

@diegosouzapw
diegosouzapw merged commit 08c7057 into diegosouzapw:release/v3.8.8 Jun 1, 2026
2 checks passed
diegosouzapw added a commit that referenced this pull request Jun 2, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: #3052 (heap-pressure auto-calibration),
  #3051/#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  #3049/#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (#2958 scope fix, #2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (#2966–#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
@diegosouzapw diegosouzapw mentioned this pull request Jun 2, 2026
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…egosouzapw#3048)

* fix(proxy): resolve registry assignments for combo and key levels

* fix(proxy): guard registry scope level lookup
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: diegosouzapw#3052 (heap-pressure auto-calibration),
  diegosouzapw#3051/diegosouzapw#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  diegosouzapw#3049/diegosouzapw#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (diegosouzapw#2958 scope fix, diegosouzapw#2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (diegosouzapw#2966–diegosouzapw#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…egosouzapw#3048)

* fix(proxy): resolve registry assignments for combo and key levels

* fix(proxy): guard registry scope level lookup
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: diegosouzapw#3052 (heap-pressure auto-calibration),
  diegosouzapw#3051/diegosouzapw#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  diegosouzapw#3049/diegosouzapw#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (diegosouzapw#2958 scope fix, diegosouzapw#2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (diegosouzapw#2966–diegosouzapw#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…egosouzapw#3048)

* fix(proxy): resolve registry assignments for combo and key levels

* fix(proxy): guard registry scope level lookup
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: diegosouzapw#3052 (heap-pressure auto-calibration),
  diegosouzapw#3051/diegosouzapw#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  diegosouzapw#3049/diegosouzapw#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (diegosouzapw#2958 scope fix, diegosouzapw#2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (diegosouzapw#2966–diegosouzapw#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
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