mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Squashes multiple in-flight changes pending release into release/v3.8.4 since the in-progress 3.8.5 has been consolidated back into 3.8.4. CRITICAL — oauth/codex (multi-account regression revert) Revert the proactive expired-flip that #2743 (multi-agent review) added to open-sse/executors/base.ts. The new behaviour marked accounts as testStatus:"expired" + isActive:false from inside the PROACTIVE refresh path whenever isUnrecoverableRefreshError() fired — including transient sentinels (refresh_token_reused that the rotation map can recover, generic invalid_request blips). On multi-account Codex it sequentially disabled working accounts in the DB before any upstream call confirmed the failure. Keep the classification — that part is legitimate (avoids spreading the sentinel into activeCredentials and sending a non-token upstream). Drop only the DB mutation: the REACTIVE path in chatCore.ts:~3912 still flips the account to expired after the upstream confirms the auth failure, which is the correct moment (by then the rotation map at tokenRefresh.ts:~1541 and the DB-staleness check have already had their chance to recover). Marked the block "SOURCE OF TRUTH — do not flip the proactive path back. Ask the operator first." with the regression history (ad3d4b696->0c94c397d-> this revert) so a future review does not re-introduce the regression on autopilot. oauth/kiro — centralize social-flow constants in KIRO_CONFIG social-authorize/route.ts and social-exchange/route.ts duplicated the AWS Kiro device-auth URL and the "kiro-cli" public client identifier. Move both to KIRO_CONFIG (alongside the existing AWS SSO OIDC + social auth fields) and add an env override on socialClientId so operators can pin a custom value via KIRO_OAUTH_CLIENT_ID. New KIRO_CONFIG fields: socialClientId (env-overridable), socialDeviceAuthorizeUrl, socialDevicePollUrl. tests/unit/oauth-kiro.test.ts locks the contract: routes must import KIRO_CONFIG and must not inline the AWS URL or "kiro-cli" literal. dashboard/providers — memoize ProviderCard lookup constants Move KIND_LABEL and DOT_COLORS into useMemo so they don't recreate on every render. Functional parity, slightly cheaper re-renders. test(authz) — lockdown Next.js 16 proxy.ts contract New tests/unit/authz/proxy-contract.test.ts asserts the file lives at src/proxy.ts (not src/middleware.ts), exports the proxy function, delegates to runAuthzPipeline with enforce:true, and the matcher covers every prefix mounted under /api so unauthenticated requests cannot bypass the centralized tier checks. version — roll back from 3.8.5 to 3.8.4 CHANGELOG.md consolidates the unreleased 3.8.5 entries into the 3.8.4 section. Mirror that in package.json, package-lock.json and docs/reference/openapi.yaml. .source/* picked up the regenerated fumadocs section ordering. docs — env contract additions Add KIRO_OAUTH_CLIENT_ID and OMNIROUTE_PROXY_FETCH_DEBUG to .env.example and docs/reference/ENVIRONMENT.md so the env-doc-sync check stays green.
82 lines
3.3 KiB
TypeScript
82 lines
3.3 KiB
TypeScript
/**
|
|
* Next.js 16 Proxy File Contract — Lockdown Test
|
|
*
|
|
* Next.js 16 deprecated `middleware.ts` in favour of `proxy.ts` (commit
|
|
* 3fb72b973 renamed our copy to match the new convention). The framework
|
|
* only invokes this file when ALL of the following hold:
|
|
*
|
|
* 1. The file lives at `src/proxy.ts` (since `src/app` is the app dir).
|
|
* 2. It exports a function named exactly `proxy` (or default).
|
|
* 3. The function delegates to `runAuthzPipeline` with `enforce: true`.
|
|
* 4. The `config.matcher` covers every prefix routes are mounted under,
|
|
* so unauthenticated requests cannot slip past the centralized
|
|
* authorization tiers (PUBLIC / CLIENT_API / MANAGEMENT).
|
|
*
|
|
* Without ANY of these guarantees the pipeline silently becomes dead code
|
|
* and every `/api/*` route falls back to per-route self-enforcement, which
|
|
* is the failure mode that led to the v3.8.4 hardening pass. Lock the
|
|
* contract down here so a future rename / refactor cannot regress it
|
|
* unnoticed.
|
|
*
|
|
* @see docs/security/ROUTE_GUARD_TIERS.md
|
|
* @see https://nextjs.org/docs/app/api-reference/file-conventions/proxy
|
|
*/
|
|
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
|
|
test("Next.js 16 proxy file exists at src/proxy.ts (not src/middleware.ts)", () => {
|
|
assert.ok(fs.existsSync("src/proxy.ts"), "src/proxy.ts must exist (Next.js 16 file convention)");
|
|
assert.ok(
|
|
!fs.existsSync("src/middleware.ts"),
|
|
"src/middleware.ts must NOT exist — Next.js 16 deprecated middleware.ts, the active file is src/proxy.ts"
|
|
);
|
|
});
|
|
|
|
test("proxy.ts exports a function named 'proxy' (Next.js 16 requires this exact name)", () => {
|
|
const content = fs.readFileSync("src/proxy.ts", "utf8");
|
|
assert.match(
|
|
content,
|
|
/export\s+async\s+function\s+proxy\s*\(/,
|
|
"must export `async function proxy(...)` — Next.js 16 only invokes this exact name"
|
|
);
|
|
});
|
|
|
|
test("proxy.ts delegates to runAuthzPipeline with enforce: true", () => {
|
|
const content = fs.readFileSync("src/proxy.ts", "utf8");
|
|
assert.match(
|
|
content,
|
|
/runAuthzPipeline\([^)]*\{\s*enforce:\s*true\s*\}\s*\)/,
|
|
"must call runAuthzPipeline with { enforce: true } — otherwise the pipeline runs in observe-only mode and never blocks"
|
|
);
|
|
});
|
|
|
|
test("proxy.ts config.matcher covers every /api/* route plus dashboard and v1 aliases", () => {
|
|
const content = fs.readFileSync("src/proxy.ts", "utf8");
|
|
// Required prefixes — drop one and the corresponding routes go unguarded.
|
|
const requiredMatchers = [
|
|
'"/api/:path*"',
|
|
'"/dashboard/:path*"',
|
|
'"/v1/:path*"',
|
|
'"/chat/:path*"',
|
|
'"/responses/:path*"',
|
|
'"/codex/:path*"',
|
|
'"/models"',
|
|
];
|
|
for (const matcher of requiredMatchers) {
|
|
assert.ok(
|
|
content.includes(matcher),
|
|
`proxy.ts config.matcher must include ${matcher} — otherwise routes under that prefix bypass the authz pipeline`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("proxy.ts does not declare runtime: 'edge' (Next.js 16 proxy is Node-only)", () => {
|
|
const content = fs.readFileSync("src/proxy.ts", "utf8");
|
|
assert.ok(
|
|
!/runtime:\s*['"]edge['"]/.test(content),
|
|
"proxy.ts MUST NOT set runtime: 'edge' — Next.js 16 only supports nodejs in proxy.ts. The pipeline depends on Node-only modules (jose, better-sqlite3) and would crash at request time on the edge runtime."
|
|
);
|
|
});
|