Merge remote-tracking branch 'origin/release/v3.8.51' into fix/trivy-cve-2025-68121-tls-client

This commit is contained in:
diegosouzapw
2026-08-30 04:23:31 -03:00
24 changed files with 1169 additions and 109 deletions

View File

@@ -0,0 +1,36 @@
name: API Route Typecheck
on:
pull_request:
branches:
- main
- "release/**"
types: [opened, synchronize, reopened, ready_for_review]
push:
branches: [main]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
api-typecheck:
name: API Route Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: "24"
cache: npm
- uses: ./.github/actions/npm-ci-retry
- name: Reject new API-route TypeScript diagnostics
run: node scripts/check/check-api-typecheck.mjs
- name: API typecheck gate unit tests
run: node --import tsx/esm --test tests/unit/build/check-api-typecheck.test.ts

View File

@@ -0,0 +1 @@
- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan

View File

@@ -0,0 +1,419 @@
{
"open-sse/transformer/responsesTransformer.ts": {
"TS2353": 1
},
"open-sse/utils/progressTracker.ts": {
"TS2353": 1
},
"open-sse/utils/sseHeartbeat.ts": {
"TS2353": 1
},
"open-sse/utils/stream.ts": {
"TS2353": 1
},
"src/app/api/assess/route.ts": {
"TS2339": 1
},
"src/app/api/cache/route.ts": {
"TS2339": 1
},
"src/app/api/cli-tools/all-statuses/route.ts": {
"TS2339": 1
},
"src/app/api/cli-tools/claude-settings/route.ts": {
"TS2339": 1
},
"src/app/api/cli-tools/cline-settings/route.ts": {
"TS2339": 5
},
"src/app/api/cli-tools/codex-settings/route.ts": {
"TS2345": 2
},
"src/app/api/cli-tools/grok-build-settings/route.ts": {
"TS2304": 1
},
"src/app/api/cli-tools/hermes-agent-settings/route.ts": {
"TS2345": 1
},
"src/app/api/cli-tools/letta-settings/route.ts": {
"TS2339": 1
},
"src/app/api/cli-tools/omp-settings/route.ts": {
"TS2339": 8
},
"src/app/api/cli-tools/qwen-settings/route.ts": {
"TS2322": 1
},
"src/app/api/combos/auto/route.ts": {
"TS2322": 1
},
"src/app/api/combos/test/route.ts": {
"TS2345": 1,
"TS2339": 1
},
"src/app/api/compression/compare/route.ts": {
"TS2345": 1
},
"src/app/api/compression/preview/route.ts": {
"TS2345": 1
},
"src/app/api/context/combos/[id]/route.ts": {
"TS2345": 1
},
"src/app/api/context/combos/route.ts": {
"TS2345": 1
},
"src/app/api/copilot/chat/route.ts": {
"TS2345": 1
},
"src/app/api/guardrails/test/route.ts": {
"TS2554": 1
},
"src/app/api/internal/codex-responses-ws/route.ts": {
"TS2740": 1,
"TS2339": 7
},
"src/app/api/keys/[id]/route.ts": {
"TS2339": 1
},
"src/app/api/local/redis/start/route.ts": {
"TS2339": 1
},
"src/app/api/local/redis/stop/route.ts": {
"TS2339": 1
},
"src/app/api/logs/[id]/route.ts": {
"TS2322": 1
},
"src/app/api/model-capability-overrides/route.ts": {
"TS2339": 1
},
"src/app/api/model-combo-mappings/route.ts": {
"TS2339": 1
},
"src/app/api/models/alias/route.ts": {
"TS2339": 5
},
"src/app/api/models/route.ts": {
"TS2345": 3,
"TS2538": 1
},
"src/app/api/monitoring/health/route.ts": {
"TS2322": 1
},
"src/app/api/oauth/codex/import-token/route.ts": {
"TS2339": 3
},
"src/app/api/oauth/codex/import/route.ts": {
"TS2554": 1,
"TS2353": 1,
"TS2339": 3
},
"src/app/api/oauth/cursor/login/poll/route.ts": {
"TS2554": 1
},
"src/app/api/oauth/kiro/auto-import/route.ts": {
"TS2345": 1
},
"src/app/api/omniroute/route/preview/route.ts": {
"TS2345": 1
},
"src/app/api/playground/presets/[id]/route.ts": {
"TS2339": 3
},
"src/app/api/provider-nodes/validate/route.ts": {
"TS2339": 2
},
"src/app/api/providers/[id]/login/route.ts": {
"TS2739": 1
},
"src/app/api/providers/[id]/models/route.ts": {
"TS2367": 1,
"TS2339": 2,
"TS2322": 2,
"TS2554": 2,
"TS2345": 3
},
"src/app/api/providers/[id]/refresh-cursor/route.ts": {
"TS2352": 1
},
"src/app/api/providers/[id]/refresh/route.ts": {
"TS2345": 1,
"TS2698": 1,
"TS2339": 6
},
"src/app/api/providers/[id]/sync-models/route.ts": {
"TS2345": 1
},
"src/app/api/providers/[id]/test/route.ts": {
"TS2362": 1,
"TS2698": 1
},
"src/app/api/providers/free-onboarding/route.ts": {
"TS2345": 1
},
"src/app/api/providers/health-autopilot/actions/route.ts": {
"TS2339": 1
},
"src/app/api/providers/route.ts": {
"TS2352": 1,
"TS2322": 2,
"TS2345": 4
},
"src/app/api/providers/test-batch/route.ts": {
"TS2345": 4
},
"src/app/api/providers/validate/route.ts": {
"TS2322": 1
},
"src/app/api/providers/volcengine-plan/connect/[sessionId]/cancel/route.ts": {
"TS2739": 1
},
"src/app/api/providers/volcengine-plan/connect/[sessionId]/code/route.ts": {
"TS2739": 1
},
"src/app/api/providers/volcengine-plan/connect/[sessionId]/identity/route.ts": {
"TS2739": 1
},
"src/app/api/providers/volcengine-plan/connect/[sessionId]/resend/route.ts": {
"TS2739": 1
},
"src/app/api/providers/volcengine-plan/connect/[sessionId]/status/route.ts": {
"TS2739": 1
},
"src/app/api/providers/volcengine-plan/connect/route.ts": {
"TS2739": 1,
"TS2339": 1
},
"src/app/api/radar/local-model-state/route.ts": {
"TS2339": 4
},
"src/app/api/resilience/model-cooldowns/route.ts": {
"TS2339": 1
},
"src/app/api/services/_shared/installRoute.ts": {
"TS2339": 1
},
"src/app/api/settings/cache-config/route.ts": {
"TS2339": 1,
"TS2322": 1
},
"src/app/api/settings/database/route.ts": {
"TS2345": 1
},
"src/app/api/settings/models-dev/route.ts": {
"TS2339": 1
},
"src/app/api/settings/obsidian/webdav/route.ts": {
"TS2339": 1
},
"src/app/api/settings/proxies/bulk-import/route.ts": {
"TS2345": 1
},
"src/app/api/settings/proxy/cloudflare-deploy/route.ts": {
"TS2769": 1,
"TS2322": 2
},
"src/app/api/settings/proxy/deno-deploy/route.ts": {
"TS2322": 4
},
"src/app/api/settings/proxy/vercel-deploy/route.ts": {
"TS2322": 3
},
"src/app/api/settings/reasoning-routing-rules/[id]/route.ts": {
"TS2339": 1
},
"src/app/api/settings/reasoning-routing-rules/route.ts": {
"TS2339": 1
},
"src/app/api/settings/reasoning-routing-rules/simulate/route.ts": {
"TS2322": 1,
"TS2339": 1
},
"src/app/api/system/env/repair/route.ts": {
"TS2578": 1,
"TS2353": 3
},
"src/app/api/system/version/route.ts": {
"TS2769": 1
},
"src/app/api/tools/agent-bridge/agents/[id]/detected-models/route.ts": {
"TS2769": 1
},
"src/app/api/tools/traffic-inspector/internal/ingest/route.ts": {
"TS1117": 2,
"TS2345": 1
},
"src/app/api/tools/traffic-inspector/ws/route.ts": {
"TS2578": 1
},
"src/app/api/translator/send/route.ts": {
"TS2345": 1,
"TS2322": 1,
"TS2339": 1
},
"src/app/api/translator/translate/route.ts": {
"TS2345": 1,
"TS2322": 1
},
"src/app/api/tunnels/cloudflared/route.ts": {
"TS2339": 1
},
"src/app/api/tunnels/ngrok/route.ts": {
"TS2339": 1
},
"src/app/api/tunnels/tailscale/routeUtils.ts": {
"TS2339": 1
},
"src/app/api/usage/analytics/route.ts": {
"TS2352": 15
},
"src/app/api/usage/combo-health-autopilot/route.ts": {
"TS2769": 2
},
"src/app/api/v1/batches/route.ts": {
"TS2339": 1
},
"src/app/api/v1/chatgpt-web/image/[id]/route.ts": {
"TS2345": 1
},
"src/app/api/v1/classify/route.ts": {
"TS2322": 1
},
"src/app/api/v1/files/[id]/content/route.ts": {
"TS2345": 1
},
"src/app/api/v1/files/route.ts": {
"TS2339": 1
},
"src/app/api/v1/images/edits/route.ts": {
"TS2339": 21,
"TS2322": 5
},
"src/app/api/v1/messages/count_tokens/route.ts": {
"TS2339": 2,
"TS2322": 1
},
"src/app/api/v1/music/generations/route.ts": {
"TS2322": 1,
"TS2345": 1
},
"src/app/api/v1/ocr/route.ts": {
"TS2345": 1
},
"src/app/api/v1/provider-plugin-manifest/route.ts": {
"TS2345": 1
},
"src/app/api/v1/providers/[provider]/embeddings/route.ts": {
"TS2339": 3,
"TS2322": 1
},
"src/app/api/v1/providers/[provider]/images/generations/route.ts": {
"TS2339": 5
},
"src/app/api/v1/rerank/route.ts": {
"TS2339": 2
},
"src/app/api/v1/segment/route.ts": {
"TS2322": 1
},
"src/app/api/v1/session-leases/route.ts": {
"TS2339": 4,
"TS2345": 1
},
"src/app/api/v1/speech-to-text/route.ts": {
"TS2353": 1
},
"src/app/api/v1/text-to-speech/[voiceId]/route.ts": {
"TS2353": 1
},
"src/app/api/v1/web/fetch/route.ts": {
"TS2339": 1
},
"src/app/api/v1beta/models/route.ts": {
"TS2345": 1,
"TS2538": 1
},
"src/app/api/version-manager/restart/route.ts": {
"TS2339": 1
},
"src/app/api/version-manager/start/route.ts": {
"TS2339": 1
},
"src/app/api/version-manager/stop/route.ts": {
"TS2339": 1
},
"src/app/api/webhooks/[id]/route.ts": {
"TS2554": 1
},
"src/app/api/webhooks/[id]/test/route.ts": {
"TS2352": 2
},
"src/app/api/webhooks/route.ts": {
"TS2554": 1,
"TS2345": 1
},
"src/lib/db/tierConfig.ts": {
"TS2345": 2
},
"src/lib/guardrails/videoBridgeHelpers.ts": {
"TS2488": 1,
"TS2365": 2,
"TS2322": 1,
"TS2345": 1
},
"src/lib/monitoring/comboHealthAutopilot.ts": {
"TS2305": 1,
"TS2345": 1
},
"src/lib/monitoring/providerHealthAutopilot.ts": {
"TS2352": 4
},
"src/lib/omnirouteStatus.ts": {
"TS2322": 1,
"TS2558": 1
},
"src/lib/providerModels/managedModelImport.ts": {
"TS2352": 4
},
"src/lib/proxySubscription/parse.ts": {
"TS2345": 3
},
"src/lib/quota/quotaAnalytics.ts": {
"TS2769": 1
},
"src/lib/quota/quotaResetTimers.ts": {
"TS2769": 2
},
"src/lib/usage/comboForecast.ts": {
"TS2345": 1
},
"src/lib/usage/comboHealth.ts": {
"TS2345": 1
},
"src/lib/usage/comboScoringInspector.ts": {
"TS2352": 1,
"TS2741": 1
},
"src/lib/usage/providerWindowCosts.ts": {
"TS2322": 2,
"TS2558": 5,
"TS2339": 12,
"TS2345": 1
},
"src/lib/vscode/modelPresentation.ts": {
"TS2554": 1
},
"src/lib/ws/handshake.ts": {
"TS2339": 1
},
"src/mitm/detection/index.ts": {
"TS2741": 1
},
"src/mitm/inspector/httpProxyServer.ts": {
"TS2769": 1
},
"src/shared/schemas/cliCatalog.ts": {
"TS2554": 2
}
}

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env node
// scripts/check/check-api-typecheck.mjs
// API-route-scoped typecheck gate (#11601).
//
// `typecheck:core` uses a curated file allowlist, the dashboard typecheck gate
// only covers src/app/(dashboard)/**, and Next builds ignore TypeScript build
// errors. That leaves src/app/api/** without a blocking typecheck gate.
//
// This gate runs `tsc` scoped to src/app/api/**/*.{ts,tsx} via
// tsconfig.typecheck-api.json and compares live diagnostics against a frozen
// per-file/per-TS-code count baseline. New diagnostics or count increases fail;
// reductions are reported as improvements and can be ratcheted with --update.
//
// Run:
// node scripts/check/check-api-typecheck.mjs
// node scripts/check/check-api-typecheck.mjs --update
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
const ROOT = process.cwd();
const TSCONFIG = path.join(ROOT, "tsconfig.typecheck-api.json");
const BASELINE_PATH = path.join(ROOT, "config/quality/api-typecheck-baseline.json");
const UPDATE = process.argv.includes("--update");
const TSC_ERROR_LINE = /^(.+?)\((\d+),(\d+)\): error (TS\d+):/;
export function parseTscOutput(raw) {
const counts = {};
for (const line of String(raw).split("\n")) {
const match = TSC_ERROR_LINE.exec(line);
if (!match) continue;
const [, file, , , code] = match;
if (!counts[file]) counts[file] = {};
counts[file][code] = (counts[file][code] || 0) + 1;
}
return counts;
}
export function diffAgainstBaseline(live, baseline) {
const regressions = [];
const improvements = [];
for (const [file, codes] of Object.entries(live)) {
for (const [code, liveCount] of Object.entries(codes)) {
const baselineCount = (baseline[file] && baseline[file][code]) || 0;
if (liveCount > baselineCount) {
regressions.push({ file, code, liveCount, baselineCount });
} else if (liveCount < baselineCount) {
improvements.push({ file, code, liveCount, baselineCount });
}
}
}
for (const [file, codes] of Object.entries(baseline)) {
for (const [code, baselineCount] of Object.entries(codes)) {
const liveCount = (live[file] && live[file][code]) || 0;
if (liveCount === 0 && baselineCount > 0) {
improvements.push({ file, code, liveCount: 0, baselineCount });
}
}
}
return { regressions, improvements };
}
function runTsc() {
try {
return execFileSync(
process.platform === "win32" ? "npx.cmd" : "npx",
["tsc", "--pretty", "false", "--noEmit", "-p", TSCONFIG],
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024, cwd: ROOT }
);
} catch (err) {
if (err.stdout) return String(err.stdout);
throw err;
}
}
function loadBaseline() {
if (!fs.existsSync(BASELINE_PATH)) return {};
return JSON.parse(fs.readFileSync(BASELINE_PATH, "utf8"));
}
function writeBaseline(counts) {
fs.writeFileSync(BASELINE_PATH, JSON.stringify(counts, null, 2) + "\n");
}
function main() {
if (!fs.existsSync(TSCONFIG)) {
process.stderr.write(`[api-typecheck] FAIL — tsconfig not found at ${TSCONFIG}\n`);
process.exit(2);
}
console.log("[api-typecheck] Running tsc scoped to src/app/api/**…");
const stdout = runTsc();
const live = parseTscOutput(stdout);
const baseline = loadBaseline();
const { regressions, improvements } = diffAgainstBaseline(live, baseline);
const liveErrorCount = Object.values(live).reduce(
(sum, codes) => sum + Object.values(codes).reduce((s, c) => s + c, 0),
0
);
console.log(`apiTypecheckErrors=${liveErrorCount}`);
if (UPDATE) {
writeBaseline(live);
console.log(`[api-typecheck] baseline rewritten (${liveErrorCount} errors frozen).`);
process.exit(0);
}
if (improvements.length > 0) {
console.log(
`[api-typecheck] ${improvements.length} baselined error(s) no longer present ` +
`— run 'node scripts/check/check-api-typecheck.mjs --update' to ratchet the baseline down:\n` +
improvements
.map((i) => ` - ${i.file} ${i.code} (baseline ${i.baselineCount} -> live ${i.liveCount})`)
.join("\n")
);
}
if (regressions.length > 0) {
process.stderr.write(
`[api-typecheck] FAIL — ${regressions.length} new/regressed TypeScript error(s) ` +
`under src/app/api/ not covered by the frozen baseline:\n` +
regressions
.map((r) => `${r.file} ${r.code} (baseline ${r.baselineCount}, live ${r.liveCount})`)
.join("\n") +
`\n\nFix new API-route TypeScript regressions rather than widening the baseline.\n`
);
process.exit(1);
}
console.log(
`[api-typecheck] OK — ${liveErrorCount} pre-existing error(s), all within frozen baseline.`
);
process.exit(0);
}
if (import.meta.url === pathToFileURL(process.argv[1] || "").href) {
main();
}

View File

@@ -38,11 +38,13 @@ export async function GET(request: Request) {
const apiKeyId = searchParams.get("apiKeyId") || undefined;
const type = (searchParams.get("type") as any) || undefined;
const sessionId = searchParams.get("sessionId") || undefined;
const category = searchParams.get("category")?.trim() || undefined;
const result = await memoryManager.list({
apiKeyId,
type,
sessionId,
category,
query,
limit: paginationParams.limit,
offset:

View File

@@ -3,6 +3,7 @@ import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
import { callCloudWithMachineId } from "@/shared/utils/cloud";
import { handleChat } from "@/sse/handlers/chat";
import { generateRequestId } from "@/shared/utils/requestId";
import { resolveIncomingCorrelationId } from "@/shared/utils/correlationPreserve.ts";
import { errorResponse } from "@omniroute/open-sse/utils/error.ts";
import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
import { createInjectionGuard } from "@/middleware/promptInjectionGuard";
@@ -243,8 +244,13 @@ export async function POST(request) {
// paths) drop the meta the docs promise.
const compressionRequestHeader = readCompressionRequestHeader(request);
// #11739: preserve caller-provided X-Correlation-Id when present; generate only when absent.
const callerCorrelationId = resolveIncomingCorrelationId(
request.headers.get("x-correlation-id")
);
if (wantsStreaming) {
const reqId = generateRequestId();
const reqId = callerCorrelationId ?? generateRequestId();
// Wrap the real handler response, not the synthetic early-keepalive response. If the
// client cancels while handleChat is still pending, earlyStreamKeepalive will cancel the
// eventual handler body; only that confirmed cleanup releases heavyweight capacity.
@@ -265,7 +271,7 @@ export async function POST(request) {
return finishAdmission(
withCompressionHeaderEcho(
await handleChat(request, null, parsedBody),
await handleChat(request, null, parsedBody, callerCorrelationId ?? undefined),
compressionRequestHeader
)
);

View File

@@ -24,15 +24,6 @@ export interface OneproxyProxyRecord {
updatedAt: string;
}
export interface OneproxyStats {
total: number;
active: number;
avgQuality: number | null;
lastValidated: string | null;
byProtocol: Array<{ protocol: string; count: number }>;
byCountry: Array<{ countryCode: string; count: number }>;
}
interface OneproxyUpsertInput {
ip: string;
port: number;
@@ -73,19 +64,6 @@ function mapProxyRow(row: unknown): OneproxyProxyRecord {
};
}
function mapStatsRow(row: unknown) {
const r = toRecord(row);
return {
total: Number(r.total) || 0,
active: Number(r.active) || 0,
avgQuality:
r.avg_quality !== null && r.avg_quality !== undefined
? Math.round(Number(r.avg_quality) * 100) / 100
: null,
lastValidated: typeof r.last_validated === "string" ? r.last_validated : null,
};
}
export async function listOneproxyProxies(options?: {
protocol?: string;
countryCode?: string;
@@ -121,47 +99,6 @@ export async function listOneproxyProxies(options?: {
return rows.map(mapProxyRow);
}
export async function getOneproxyStats(): Promise<OneproxyStats> {
const db = getDbInstance();
const statsRow = db
.prepare(
`SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active,
AVG(quality_score) as avg_quality,
MAX(last_validated) as last_validated
FROM proxy_registry WHERE source = 'oneproxy'`
)
.get();
const stats = mapStatsRow(statsRow);
const byProtocol = db
.prepare(
"SELECT type as protocol, COUNT(*) as count FROM proxy_registry WHERE source = 'oneproxy' GROUP BY type ORDER BY count DESC"
)
.all() as Array<JsonRecord>;
const byCountry = db
.prepare(
"SELECT country_code as countryCode, COUNT(*) as count FROM proxy_registry WHERE source = 'oneproxy' AND country_code IS NOT NULL GROUP BY country_code ORDER BY count DESC LIMIT 20"
)
.all() as Array<JsonRecord>;
return {
...stats,
byProtocol: byProtocol.map((r) => ({
protocol: String(r.protocol || "unknown"),
count: Number(r.count) || 0,
})),
byCountry: byCountry.map((r) => ({
countryCode: String(r.countryCode || "unknown"),
count: Number(r.count) || 0,
})),
};
}
export async function upsertOneproxyProxy(
input: OneproxyUpsertInput
): Promise<{ proxy: OneproxyProxyRecord | null; action: "created" | "updated" }> {
@@ -236,22 +173,6 @@ export async function getOneproxyProxyById(id: string): Promise<OneproxyProxyRec
return mapProxyRow(row);
}
export async function deleteOneproxyProxy(id: string): Promise<boolean> {
const db = getDbInstance();
const result = db
.prepare("DELETE FROM proxy_registry WHERE id = ? AND source = 'oneproxy'")
.run(id);
backupDbFile("pre-write");
return result.changes > 0;
}
export async function clearAllOneproxyProxies(): Promise<number> {
const db = getDbInstance();
const result = db.prepare("DELETE FROM proxy_registry WHERE source = 'oneproxy'").run();
backupDbFile("pre-write");
return result.changes;
}
export async function getOneproxyProxyForRotation(options?: {
strategy?: "random" | "quality" | "sequential";
}): Promise<OneproxyProxyRecord | null> {

View File

@@ -369,7 +369,7 @@ describe("GenericMemoryBackend", () => {
test("applies custom query param names", async () => {
const b = createBackend({
queryParams: { apiKeyId: "owner", limit: "count" },
queryParams: { apiKeyId: "owner", category: "memoryCategory", limit: "count" },
});
const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (url: string) => {
if (url.toString().endsWith("/health")) {
@@ -378,14 +378,16 @@ describe("GenericMemoryBackend", () => {
return new Response(JSON.stringify({ data: [], total: 0, byType: {} }), { status: 200 });
});
await b.list({ apiKeyId: "key-1", limit: 5 });
await b.list({ apiKeyId: "key-1", category: "codegraph", limit: 5 });
const listUrl = fetchMock.mock.calls.find(
([url]) => !url.toString().endsWith("/health")
)![0] as string;
expect(listUrl).toContain("owner=key-1");
expect(listUrl).toContain("memoryCategory=codegraph");
expect(listUrl).toContain("count=5");
expect(listUrl).not.toContain("apiKeyId=");
expect(listUrl).not.toContain("category=");
});
});

View File

@@ -21,6 +21,7 @@ export interface MemoryFilter {
apiKeyId?: string;
type?: MemoryType;
sessionId?: string;
category?: string;
query?: string;
limit?: number;
offset?: number;

View File

@@ -118,6 +118,7 @@ export interface GenericBackendConfig {
maxTokens?: string; // default: "maxTokens"
type?: string; // default: "type"
sessionId?: string; // default: "sessionId"
category?: string; // default: "category"
orderBy?: string; // default: "orderBy"
orderDir?: string; // default: "orderDir"
options?: string; // default: "options"
@@ -181,6 +182,7 @@ export class GenericMemoryBackend implements MemoryBackend {
maxTokens: this.config.queryParams?.maxTokens ?? "maxTokens",
type: this.config.queryParams?.type ?? "type",
sessionId: this.config.queryParams?.sessionId ?? "sessionId",
category: this.config.queryParams?.category ?? "category",
orderBy: this.config.queryParams?.orderBy ?? "orderBy",
orderDir: this.config.queryParams?.orderDir ?? "orderDir",
options: this.config.queryParams?.options ?? "options",
@@ -222,6 +224,7 @@ export class GenericMemoryBackend implements MemoryBackend {
if (filter.apiKeyId) out[qp.apiKeyId] = filter.apiKeyId;
if (filter.type) out[qp.type] = filter.type;
if (filter.sessionId) out[qp.sessionId] = filter.sessionId;
if (filter.category) out[qp.category] = filter.category;
if (filter.limit !== undefined) out[qp.limit] = String(filter.limit);
if (filter.offset !== undefined) out[qp.offset] = String(filter.offset);
if (filter.orderBy) out[qp.orderBy] = filter.orderBy;

View File

@@ -231,6 +231,7 @@ export class ObsidianBackend implements MemoryBackend {
if (filter.apiKeyId && memory.apiKeyId !== filter.apiKeyId) continue;
if (filter.type && memory.type !== filter.type) continue;
if (filter.sessionId && memory.sessionId !== filter.sessionId) continue;
if (filter.category && memory.metadata.category !== filter.category) continue;
memories.push(memory);
byType[memory.type] = (byType[memory.type] || 0) + 1;

View File

@@ -64,6 +64,7 @@ export class SQLiteBackend implements MemoryBackend {
apiKeyId: filter.apiKeyId,
type: filter.type,
sessionId: filter.sessionId,
category: filter.category,
query: filter.query,
limit: filter.limit,
offset: filter.offset,

View File

@@ -468,6 +468,7 @@ export async function listMemories(filters: {
apiKeyId?: string;
type?: MemoryType;
sessionId?: string;
category?: string;
query?: string;
limit?: number;
offset?: number;
@@ -494,6 +495,13 @@ export async function listMemories(filters: {
whereParams.push(filters.sessionId);
}
if (typeof filters.category === "string" && filters.category.trim().length > 0) {
whereClauses.push(
"json_extract(CASE WHEN json_valid(metadata) THEN metadata ELSE '{}' END, '$.category') = ?"
);
whereParams.push(filters.category.trim());
}
if (typeof filters.query === "string" && filters.query.trim().length > 0) {
const likeQuery = `%${filters.query.trim().toLowerCase()}%`;
whereClauses.push("(LOWER(content) LIKE ? OR LOWER(key) LIKE ?)");

View File

@@ -14,7 +14,6 @@ import {
import { syncToCloud } from "@/lib/cloudSync";
import { setQuotaCache } from "@/domain/quotaCache";
import { buildClaudeExtraUsageConnectionUpdate } from "@/lib/providers/claudeExtraUsage";
import { isConnectionUnavailableToAuxiliaryActivity } from "@/lib/exclusiveLeaseIsolation";
import { clearRecoveredProviderState } from "@/sse/services/auth";
import { getMachineId } from "@/shared/utils/machine";
import { USAGE_SUPPORTED_PROVIDERS } from "@/shared/constants/providers";
@@ -860,9 +859,6 @@ async function fetchLiveProviderLimitsWithOptions(
connection: ProviderConnectionLike;
usage: JsonRecord;
}> {
if (await isConnectionUnavailableToAuxiliaryActivity(connectionId)) {
throw withStatus(new Error("Usage refresh deferred while an exclusive lease is active"), 409);
}
let connection = (await getProviderConnectionById(
connectionId
)) as unknown as ProviderConnectionLike | null;
@@ -1073,16 +1069,7 @@ export async function syncAllProviderLimits(
const connectionRows = (await getProviderConnections({
isActive: true,
})) as unknown as ProviderConnectionLike[];
const connections = (
await Promise.all(
connectionRows.map(async (connection) => ({
connection,
blocked: await isConnectionUnavailableToAuxiliaryActivity(connection.id),
}))
)
)
.filter(({ connection, blocked }) => isSupportedUsageConnection(connection) && !blocked)
.map(({ connection }) => connection);
const connections = connectionRows.filter(isSupportedUsageConnection);
const cacheEntries: Array<{ connectionId: string; entry: ProviderLimitsCacheEntry }> = [];
const caches: Record<string, ProviderLimitsCacheEntry> = {};
const errors: Record<string, string> = {};

View File

@@ -4,6 +4,7 @@ import { Agent, fetch as undiciFetch } from "undici";
import {
type OutboundUrlGuardMode,
isPrivateHost,
parseAndValidateNonMetadataUrl,
parseAndValidatePublicUrl,
parseOutboundUrl,
} from "@/shared/network/outboundUrlGuard";
@@ -51,7 +52,9 @@ export type RemoteMediaFetchOptions = RemoteImageFetchOptions;
export type RemoteMediaFetchResult = RemoteImageFetchResult;
function validateRemoteImageUrl(input: string | URL, guard: OutboundUrlGuardMode) {
return guard === "public-only" ? parseAndValidatePublicUrl(input) : parseOutboundUrl(input);
if (guard === "public-only") return parseAndValidatePublicUrl(input);
if (guard === "block-metadata") return parseAndValidateNonMetadataUrl(input);
return parseOutboundUrl(input);
}
function requireHttps(url: URL, enabled: boolean): URL {

View File

@@ -0,0 +1,12 @@
/**
* Resolve caller-provided X-Correlation-Id for preservation (#11739).
* Returns sanitized value when present and within bounds (1-256 chars), otherwise null.
* Strips CRLF to prevent header injection, trims whitespace.
*/
export function resolveIncomingCorrelationId(
headerValue: string | null | undefined
): string | null {
const raw = (headerValue ?? "").trim().replace(/[\r\n]/g, "");
if (raw.length === 0 || raw.length > 256) return null;
return raw;
}

View File

@@ -0,0 +1,130 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-memory-category-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const { updateSettings } = await import("../../src/lib/db/settings.ts");
const localDb = { updateSettings };
const { GET } = await import("../../src/app/api/memory/route.ts");
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
function insertMemoryRow({
id,
category,
type = "factual",
sessionId = "session-a",
metadata,
}: {
id: string;
category?: string;
type?: string;
sessionId?: string;
metadata?: string;
}) {
const db = core.getDbInstance();
const now = new Date().toISOString();
const serializedMetadata =
metadata ?? JSON.stringify(category === undefined ? {} : { category });
db.prepare(
`INSERT INTO memories (
id, api_key_id, session_id, type, key, content, metadata, created_at, updated_at, expires_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
).run(
id,
"key-a",
sessionId,
type,
`memory:${id}`,
`content ${id}`,
serializedMetadata,
now,
now,
null
);
}
async function getMemories(query = "") {
const response = await GET(new Request(`http://localhost/api/memory${query}`));
assert.equal(response.status, 200);
return response.json();
}
test.beforeEach(async () => {
await resetStorage();
await localDb.updateSettings({ requireLogin: false });
});
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("GET /api/memory filters by metadata.category and keeps totals in sync", async () => {
insertMemoryRow({ id: "codegraph-1", category: "codegraph", type: "factual" });
insertMemoryRow({ id: "codegraph-2", category: "codegraph", type: "semantic" });
insertMemoryRow({ id: "decision-1", category: "decision", type: "episodic" });
const body = await getMemories("?category=codegraph");
assert.deepEqual(
body.data.map((memory: { id: string }) => memory.id).sort(),
["codegraph-1", "codegraph-2"]
);
assert.equal(body.total, 2);
assert.equal(body.stats.total, 2);
assert.deepEqual(body.stats.byType, { factual: 1, semantic: 1 });
});
test("GET /api/memory composes category with existing filters and pagination", async () => {
insertMemoryRow({ id: "keep-1", category: "decision", type: "episodic", sessionId: "session-a" });
insertMemoryRow({ id: "keep-2", category: "decision", type: "episodic", sessionId: "session-a" });
insertMemoryRow({ id: "wrong-type", category: "decision", type: "factual", sessionId: "session-a" });
insertMemoryRow({ id: "wrong-session", category: "decision", type: "episodic", sessionId: "session-b" });
const body = await getMemories(
"?category=decision&type=episodic&sessionId=session-a&limit=1&offset=1"
);
assert.equal(body.data.length, 1);
assert.ok(["keep-1", "keep-2"].includes(body.data[0].id));
assert.equal(body.total, 2);
assert.equal(body.stats.total, 2);
assert.deepEqual(body.stats.byType, { episodic: 2 });
});
test("GET /api/memory returns no matches for an unknown category", async () => {
insertMemoryRow({ id: "known", category: "codegraph" });
const body = await getMemories("?category=missing");
assert.deepEqual(body.data, []);
assert.equal(body.total, 0);
assert.equal(body.stats.total, 0);
assert.deepEqual(body.stats.byType, {});
});
test("GET /api/memory ignores malformed metadata when applying category filter", async () => {
insertMemoryRow({ id: "valid", category: "codegraph" });
insertMemoryRow({ id: "malformed", metadata: "{not-json" });
const filtered = await getMemories("?category=codegraph");
assert.deepEqual(
filtered.data.map((memory: { id: string }) => memory.id),
["valid"]
);
assert.equal(filtered.total, 1);
const unfiltered = await getMemories();
assert.equal(unfiltered.total, 2);
});

View File

@@ -0,0 +1,87 @@
// tests/unit/build/check-api-typecheck.test.ts
// Hermetic tests for the API typecheck gate's parsing and baseline-diff logic.
import test from "node:test";
import assert from "node:assert/strict";
import {
parseTscOutput,
diffAgainstBaseline,
} from "../../../scripts/check/check-api-typecheck.mjs";
test("parseTscOutput: parses an API-route TS2554 regression", () => {
const raw =
`src/app/api/v1/models/route.ts(42,7): error TS2554: Expected 2 arguments, but got 3.\n` +
`src/app/api/v1/models/route.ts(52,7): error TS2554: Expected 2 arguments, but got 3.\n`;
assert.deepEqual(parseTscOutput(raw), {
"src/app/api/v1/models/route.ts": { TS2554: 2 },
});
});
test("parseTscOutput: ignores non-error lines", () => {
const raw =
`src/app/api/foo/route.ts(1,1): error TS2339: Property 'bar' does not exist.\n` +
`Found 1 error in 1 file.\n`;
assert.deepEqual(parseTscOutput(raw), {
"src/app/api/foo/route.ts": { TS2339: 1 },
});
});
test("parseTscOutput: returns an empty map for clean output", () => {
assert.deepEqual(parseTscOutput("Found 0 errors.\n"), {});
});
test("diffAgainstBaseline: flags a new API diagnostic", () => {
const live = { "src/app/api/v1/models/route.ts": { TS2554: 1 } };
const { regressions, improvements } = diffAgainstBaseline(live, {});
assert.deepEqual(regressions, [
{
file: "src/app/api/v1/models/route.ts",
code: "TS2554",
liveCount: 1,
baselineCount: 0,
},
]);
assert.equal(improvements.length, 0);
});
test("diffAgainstBaseline: accepts an unchanged frozen diagnostic count", () => {
const baseline = { "src/app/api/foo/route.ts": { TS2339: 2 } };
const live = { "src/app/api/foo/route.ts": { TS2339: 2 } };
const { regressions, improvements } = diffAgainstBaseline(live, baseline);
assert.equal(regressions.length, 0);
assert.equal(improvements.length, 0);
});
test("diffAgainstBaseline: fails a count increase", () => {
const baseline = { "src/app/api/foo/route.ts": { TS2339: 1 } };
const live = { "src/app/api/foo/route.ts": { TS2339: 2 } };
const { regressions } = diffAgainstBaseline(live, baseline);
assert.equal(regressions.length, 1);
assert.equal(regressions[0].baselineCount, 1);
assert.equal(regressions[0].liveCount, 2);
});
test("diffAgainstBaseline: reports a count decrease as an improvement", () => {
const baseline = { "src/app/api/foo/route.ts": { TS2339: 2 } };
const live = { "src/app/api/foo/route.ts": { TS2339: 1 } };
const { regressions, improvements } = diffAgainstBaseline(live, baseline);
assert.equal(regressions.length, 0);
assert.equal(improvements.length, 1);
assert.equal(improvements[0].liveCount, 1);
});
test("diffAgainstBaseline: reports a disappeared diagnostic as an improvement", () => {
const baseline = { "src/app/api/foo/route.ts": { TS2339: 2 } };
const { regressions, improvements } = diffAgainstBaseline({}, baseline);
assert.equal(regressions.length, 0);
assert.equal(improvements.length, 1);
assert.equal(improvements[0].liveCount, 0);
assert.equal(improvements[0].baselineCount, 2);
});

View File

@@ -0,0 +1,37 @@
import test from "node:test";
import assert from "node:assert/strict";
const { resolveIncomingCorrelationId } = await import(
"../../src/shared/utils/correlationPreserve.ts"
);
test("resolveIncomingCorrelationId preserves valid caller ID", () => {
assert.equal(resolveIncomingCorrelationId("caller-correlation"), "caller-correlation");
assert.equal(resolveIncomingCorrelationId(" spaced-id "), "spaced-id");
assert.equal(resolveIncomingCorrelationId("abc-123_XYZ"), "abc-123_XYZ");
});
test("resolveIncomingCorrelationId sanitizes header injection", () => {
assert.equal(resolveIncomingCorrelationId("evil\r\nInjected: true"), "evilInjected: true");
assert.equal(resolveIncomingCorrelationId("with\nnewline"), "withnewline");
assert.equal(resolveIncomingCorrelationId("with\rcarriage"), "withcarriage");
});
test("resolveIncomingCorrelationId rejects empty and overlong", () => {
assert.equal(resolveIncomingCorrelationId(null), null);
assert.equal(resolveIncomingCorrelationId(undefined), null);
assert.equal(resolveIncomingCorrelationId(""), null);
assert.equal(resolveIncomingCorrelationId(" "), null);
const long = "a".repeat(257);
assert.equal(resolveIncomingCorrelationId(long), null);
assert.equal(resolveIncomingCorrelationId("a".repeat(256)), "a".repeat(256));
});
test("resolveIncomingCorrelationId trims before length check", () => {
// 256 chars plus surrounding spaces should still be valid after trim
const spacedLong = " " + "a".repeat(256) + " ";
assert.equal(resolveIncomingCorrelationId(spacedLong), "a".repeat(256));
// 257 after trim should be rejected
const spacedTooLong = " " + "a".repeat(257) + " ";
assert.equal(resolveIncomingCorrelationId(spacedTooLong), null);
});

View File

@@ -24,12 +24,14 @@ const translator = await import("../../src/app/api/translator/send/route.ts");
const translatorPreview = await import("../../src/app/api/translator/translate/route.ts");
const modelTests = await import("../../src/lib/api/modelTestRunner.ts");
const vnc = await import("../../src/lib/vncSession/service.ts");
const usageRoute = await import("../../src/app/api/usage/[connectionId]/route.ts");
const providerLimits = await import("../../src/lib/usage/providerLimits.ts");
const OWNER = "vlo_UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
async function seedConnection(name: string): Promise<{ id: string }> {
async function seedConnection(name: string, provider = "openai"): Promise<{ id: string }> {
return (await providers.createProviderConnection({
provider: "openai",
provider,
authType: "apikey",
name,
apiKey: `sk-${name}`,
@@ -52,6 +54,10 @@ async function resetStorage(): Promise<void> {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
externalCalls = 0;
globalThis.fetch = async () => {
externalCalls += 1;
throw new Error("unexpected external provider/model call");
};
}
test.beforeEach(resetStorage);
@@ -190,3 +196,142 @@ test("browser-login harvest rejects ACTIVE leased connections before credential
);
assert.equal(externalCalls, 0);
});
test("usage refresh allows FREE lease-reserved connection and queries provider quota", async () => {
const connection = await seedConnection("deepseek-free-lease", "deepseek");
await markLeaseOnly(connection.id);
globalThis.fetch = async (input: RequestInfo | URL) => {
externalCalls += 1;
const url =
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
if (url === "https://api.deepseek.com/user/balance") {
return new Response(
JSON.stringify({
is_available: true,
balance_infos: [
{
currency: "USD",
total_balance: "10.00",
granted_balance: "0.00",
topped_up_balance: "10.00",
},
],
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
}
throw new Error(`unexpected fetch call: ${url}`);
};
const response = await usageRoute.GET(
new Request(`http://omniroute.local/api/usage/${connection.id}`),
{ params: Promise.resolve({ connectionId: connection.id }) }
);
assert.equal(response.status, 200);
assert.equal(externalCalls, 1);
const data = (await response.json()) as { quotas?: { credits_usd?: { remaining?: number } } };
assert.equal(data?.quotas?.credits_usd?.remaining, 10);
});
test("usage refresh allows ACTIVE leased connection and queries provider quota", async () => {
const connection = await seedConnection("deepseek-active-lease", "deepseek");
await markLeaseOnly(connection.id);
const acquired = leases.acquireExclusiveConnectionLease({
leaseOwnerId: OWNER,
apiKeyId: "managed-key",
provider: "deepseek",
connectionId: connection.id,
});
assert.equal(acquired.kind, "ACQUIRED");
globalThis.fetch = async (input: RequestInfo | URL) => {
externalCalls += 1;
const url =
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
if (url === "https://api.deepseek.com/user/balance") {
return new Response(
JSON.stringify({
is_available: true,
balance_infos: [
{
currency: "USD",
total_balance: "10.00",
granted_balance: "0.00",
topped_up_balance: "10.00",
},
],
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
}
throw new Error(`unexpected fetch call: ${url}`);
};
const response = await usageRoute.GET(
new Request(`http://omniroute.local/api/usage/${connection.id}`),
{ params: Promise.resolve({ connectionId: connection.id }) }
);
assert.equal(response.status, 200);
assert.equal(externalCalls, 1);
const data = (await response.json()) as { quotas?: { credits_usd?: { remaining?: number } } };
assert.equal(data?.quotas?.credits_usd?.remaining, 10);
});
test("syncAllProviderLimits refreshes all active supported connections regardless of lease state", async () => {
const freeConn = await seedConnection("deepseek-bulk-free", "deepseek");
const activeConn = await seedConnection("deepseek-bulk-active", "deepseek");
await markLeaseOnly(freeConn.id);
await markLeaseOnly(activeConn.id);
const acquired = leases.acquireExclusiveConnectionLease({
leaseOwnerId: OWNER,
apiKeyId: "managed-key",
provider: "deepseek",
connectionId: activeConn.id,
});
assert.equal(acquired.kind, "ACQUIRED");
globalThis.fetch = async (input: RequestInfo | URL) => {
externalCalls += 1;
const url =
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
if (url === "https://api.deepseek.com/user/balance") {
return new Response(
JSON.stringify({
is_available: true,
balance_infos: [
{
currency: "USD",
total_balance: "10.00",
granted_balance: "0.00",
topped_up_balance: "10.00",
},
],
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
}
throw new Error(`unexpected fetch call: ${url}`);
};
const result = await providerLimits.syncAllProviderLimits({
source: "manual",
concurrency: 2,
});
assert.ok(result.caches[freeConn.id]);
assert.ok(result.caches[activeConn.id]);
assert.equal(externalCalls, 2);
});

View File

@@ -71,20 +71,13 @@ test("connection verification skips an ACTIVE exclusive lease before any probe o
assert.equal(row.last_error, null);
});
test("model discovery, quota refresh, and reset-credit paths reject ACTIVE leased connections", async () => {
test("model discovery and reset-credit paths reject ACTIVE leased connections", async () => {
const response = await providerModels.GET(
new Request("http://omniroute.local/api/providers/leased-test-connection/models"),
{ params: { id: "leased-test-connection" } }
);
assert.equal(response.status, 409);
await assert.rejects(
providerLimits.fetchLiveProviderLimits("leased-test-connection"),
(error: unknown) =>
error instanceof Error &&
(error as Error & { status?: number }).status === 409 &&
/exclusive lease/i.test(error.message)
);
await assert.rejects(
codexResetCredits.listCodexResetCredits("leased-test-connection"),
(error: unknown) =>
@@ -94,3 +87,59 @@ test("model discovery, quota refresh, and reset-credit paths reject ACTIVE lease
);
assert.equal(externalCalls, 0);
});
test("quota refresh proceeds on an ACTIVE leased usage-supported connection", async () => {
const db = core.getDbInstance();
db.prepare(
`INSERT INTO provider_connections
(id, provider, auth_type, name, api_key, is_active, test_status, created_at, updated_at)
VALUES (?, ?, 'apikey', ?, ?, 1, 'active', ?, ?)`
).run(
"leased-quota-connection",
"deepseek",
"leased quota connection",
"synthetic-deepseek-key",
new Date().toISOString(),
new Date().toISOString()
);
const acquired = leases.acquireExclusiveConnectionLease({
leaseOwnerId: "vlo_QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ",
apiKeyId: "managed-quota-key",
provider: "deepseek",
connectionId: "leased-quota-connection",
});
assert.equal(acquired.kind, "ACQUIRED");
externalCalls = 0;
globalThis.fetch = async (input: RequestInfo | URL) => {
externalCalls += 1;
const url =
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
if (url === "https://api.deepseek.com/user/balance") {
return new Response(
JSON.stringify({
is_available: true,
balance_infos: [
{
currency: "USD",
total_balance: "10.00",
granted_balance: "0.00",
topped_up_balance: "10.00",
},
],
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
}
throw new Error(`unexpected fetch call: ${url}`);
};
const result = await providerLimits.fetchLiveProviderLimits("leased-quota-connection");
assert.equal(result.connection.id, "leased-quota-connection");
const quotas = result.usage.quotas as { credits_usd?: { remaining?: number } };
assert.equal(quotas?.credits_usd?.remaining, 10);
assert.equal(externalCalls, 1);
});

View File

@@ -310,11 +310,14 @@ test("managed request surfaces are fenced centrally or rejected before independe
"src/lib/api/modelTestRunner.ts",
"src/lib/services/quotaAutoPing.ts",
"src/lib/usage/codexResetCredits.ts",
"src/lib/usage/providerLimits.ts",
"src/lib/vncSession/service.ts",
"src/lib/warmupScheduler.ts",
"src/shared/services/modelSyncScheduler.ts",
].map((file) => fs.readFileSync(path.join(REPO_ROOT, file), "utf8"));
const unfencedUsageRefreshSource = fs.readFileSync(
path.join(REPO_ROOT, "src/lib/usage/providerLimits.ts"),
"utf8"
);
assert.match(chat, /parseManagedLeaseRequestContext\(request\.headers\)/);
assert.match(chat, /isManagedComboUnsupported/);
@@ -329,6 +332,10 @@ test("managed request surfaces are fenced centrally or rejected before independe
for (const source of auxiliaryIsolationSources) {
assert.match(source, /isConnectionUnavailableToAuxiliaryActivity/);
}
// Usage/quota refresh is read-only admin telemetry (#11758) and must not inherit
// the exclusive-lease auxiliary fence that blocks model tests, translation, VNC,
// reset-credits, and warmup.
assert.doesNotMatch(unfencedUsageRefreshSource, /isConnectionUnavailableToAuxiliaryActivity/);
});
test("SQLite claim-race retry removes only the lost candidate from the same policy-valid set", () => {

View File

@@ -56,3 +56,52 @@ test("fetchRemoteImage blocks redirects to private image hosts", async () => {
/Blocked private or local provider URL/
);
});
// The default guard mode (no `guard` option passed, matching production callers that rely on
// `getProviderOutboundGuard()`'s local-first default) is "block-metadata". Every other test in
// this file passes `guard: "public-only"` explicitly, which never exercised this branch — the
// gap that let `validateRemoteImageUrl()`'s fall-through to the unchecked `parseOutboundUrl()`
// for cloud-metadata hosts go undetected.
test("fetchRemoteImage blocks cloud-metadata hosts under the default block-metadata guard", async () => {
let called = false;
await assert.rejects(
() =>
fetchRemoteImage("http://169.254.169.254/latest/meta-data", {
fetchImpl: async () => {
called = true;
return new Response("unexpected");
},
}),
/Blocked cloud-metadata endpoint/
);
assert.equal(called, false);
});
test("fetchRemoteImage allows private/LAN image hosts under the default block-metadata guard", async () => {
const result = await fetchRemoteImage("http://192.168.1.50:8080/local.png", {
fetchImpl: async () =>
new Response(new Uint8Array([1, 2, 3]), {
status: 200,
headers: { "content-type": "image/png" },
}),
});
assert.equal(result.buffer.toString("base64"), "AQID");
});
test("fetchRemoteImage blocks redirects to cloud-metadata hosts under the default block-metadata guard", async () => {
await assert.rejects(
() =>
fetchRemoteImage("https://cdn.example.com/redirect.png", {
fetchImpl: async () =>
new Response(null, {
status: 302,
headers: { location: "http://169.254.169.254/latest/meta-data" },
}),
lookup: publicLookup,
}),
/Blocked cloud-metadata endpoint/
);
});

View File

@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"incremental": false
},
"include": ["src/app/api/**/*.ts", "src/app/api/**/*.tsx"]
}