diff --git a/open-sse/services/usage.ts b/open-sse/services/usage.ts index 3f28224bd2..8e3ac88529 100644 --- a/open-sse/services/usage.ts +++ b/open-sse/services/usage.ts @@ -1367,7 +1367,6 @@ export const USAGE_FETCHER_PROVIDERS = [ "bailian-coding-plan", "nanogpt", "deepseek", - "opencode-go", "opencode", "opencode-zen", ] as const; @@ -1428,7 +1427,6 @@ export async function getUsageForProvider( return await getNanoGptUsage(apiKey || ""); case "deepseek": return await getDeepseekUsage(id || "", apiKey || ""); - case "opencode-go": case "opencode": case "opencode-zen": return await getOpencodeUsage(id || "", apiKey || ""); diff --git a/scripts/ad-hoc/nvidia-startswith-diag.ts b/scripts/ad-hoc/nvidia-startswith-diag.ts index ca0fbc8250..8baa647c7d 100644 --- a/scripts/ad-hoc/nvidia-startswith-diag.ts +++ b/scripts/ad-hoc/nvidia-startswith-diag.ts @@ -22,7 +22,9 @@ const KEY = process.env.NVIDIA_API_KEY ?? ""; const BASE_URL = process.env.NVIDIA_BASE_URL || "https://integrate.api.nvidia.com/v1/chat/completions"; const MODEL = process.env.NVIDIA_MODEL || "openai/gpt-oss-120b"; -const line = (s = "") => console.log(s); +// Neutralize CR/LF before logging so env-derived values (NVIDIA_MODEL, etc.) +// cannot forge extra log lines (S5145 log injection). +const line = (s = "") => console.log(String(s).replace(/[\r\n]+/g, " ")); const hr = () => line("─".repeat(72)); function show(label: string, value: unknown) { diff --git a/sonar-project.properties b/sonar-project.properties index 4717f444e9..f22e77b98f 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -22,7 +22,13 @@ sonar.cpd.exclusions=**/* # tooling on the user's own machine; running with their PATH is # intentional and matches the behaviour of every other CLI on the # system. -sonar.issue.ignore.multicriteria=h1,h2,h3,h4,h5 +# S1523 – Dynamic code execution. SCOPED to src/lib/plugins/pluginWorker.ts +# only: running plugin source IS the worker's purpose, and it does so +# inside a hardened Node `vm` sandbox (vm.createContext, a require +# allow-list of just `crypto`, and a 10s execution timeout). Not +# `eval`/`new Function` (Hard Rule #3). S1523 stays active everywhere +# else so a genuine dynamic-exec elsewhere is still flagged. +sonar.issue.ignore.multicriteria=h1,h2,h3,h4,h5,h6 sonar.issue.ignore.multicriteria.h1.ruleKey=javascript:S5852 sonar.issue.ignore.multicriteria.h1.resourceKey=**/* sonar.issue.ignore.multicriteria.h2.ruleKey=typescript:S5852 @@ -33,3 +39,5 @@ sonar.issue.ignore.multicriteria.h4.ruleKey=javascript:S4036 sonar.issue.ignore.multicriteria.h4.resourceKey=**/* sonar.issue.ignore.multicriteria.h5.ruleKey=typescript:S4036 sonar.issue.ignore.multicriteria.h5.resourceKey=**/* +sonar.issue.ignore.multicriteria.h6.ruleKey=typescript:S1523 +sonar.issue.ignore.multicriteria.h6.resourceKey=**/pluginWorker.ts diff --git a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx index 0a3202e6bc..a09be3cab3 100644 --- a/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx +++ b/src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx @@ -330,11 +330,12 @@ export default function ApiManagerPageClient() { ); // Match call logs by unique ID as well for the lastUsed timestamp + // Prefer an exact apiKeyId match; fall back to name match for legacy + // logs that predate per-key IDs (apiKeyId absent). const lastUsed = - (logs || []).find((log: any) => log.apiKeyId === key.id)?.timestamp || null; - (logs || []).find( - (log: any) => log.apiKeyId === key.id || (!log.apiKeyId && log.apiKeyName === key.name) - )?.timestamp || null; + (logs || []).find( + (log: any) => log.apiKeyId === key.id || (!log.apiKeyId && log.apiKeyName === key.name) + )?.timestamp || null; stats[key.id] = { totalRequests, diff --git a/src/sse/handlers/chat.ts b/src/sse/handlers/chat.ts index d8b1c65f96..266f8297de 100644 --- a/src/sse/handlers/chat.ts +++ b/src/sse/handlers/chat.ts @@ -133,7 +133,9 @@ const COMBOS_CACHE_TTL_MS = 10_000; async function getCombosCachedForChat(): Promise { const now = Date.now(); - if (combosCachePromise && now - combosCacheTs < COMBOS_CACHE_TTL_MS) { + // Explicit non-null check: we intentionally cache and return the Promise + // itself (to dedupe concurrent callers), so this is not a forgotten await. + if (combosCachePromise !== null && now - combosCacheTs < COMBOS_CACHE_TTL_MS) { return combosCachePromise; } diff --git a/src/sse/services/auth.ts b/src/sse/services/auth.ts index 0652c277eb..2975861c79 100644 --- a/src/sse/services/auth.ts +++ b/src/sse/services/auth.ts @@ -770,7 +770,7 @@ function getSelectionMutexKey(provider: string, options: CredentialSelectionOpti } function createSelectionLock(key: string) { - const currentMutex = selectionMutexes.get(key) || Promise.resolve(); + const currentMutex = selectionMutexes.get(key) ?? Promise.resolve(); let resolveMutex: (() => void) | undefined; const nextMutex = new Promise((resolve) => { resolveMutex = resolve;