From 705ab8e69058aa99f5495625c1e1833bd1442c94 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Wed, 3 Jun 2026 00:17:56 -0300 Subject: [PATCH] fix(quality): clear SonarCloud quality-gate findings on PR #2930 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quality Gate was failing on New Code with 1 hotspot, 1 vulnerability and 4 reliability bugs. Resolved each at the source (no SonarCloud mark-as-safe): Reliability (4 bugs → rating back to A): - usage.ts: drop duplicate `case "opencode-go"` (S1862) — the 2nd case was dead (first match wins) and would have called the wrong usage fetcher. Also de-duplicate the same id in USAGE_FETCHER_PROVIDERS (mirrors the switch; prevented a double quota-fetcher registration). - ApiManagerPageClient.tsx: a dangling `.find(...)?.timestamp || null` expression (S905) discarded the better matcher — `lastUsed` silently lost the name-fallback for legacy logs without apiKeyId. Assign the complete matcher. - chat.ts / auth.ts: `Promise` used in a boolean conditional (S6544). Both are intentional (memoized promise / mutex default), not a forgotten await — made the intent explicit via `!== null` and `??` (behaviour identical). Security (vulnerability → rating back to A): - nvidia-startswith-diag.ts: neutralize CR/LF before logging env-derived values (S5145 log injection) in the ad-hoc diagnostic script. Security Hotspot (reviewed → 0 open): - pluginWorker.ts uses `vm.runInContext` to run plugin code — that IS the worker's purpose, inside a hardened vm sandbox (createContext, require allow-list of just `crypto`, 10s timeout); not eval/new Function. Suppress S1523 scoped only to pluginWorker.ts in sonar-project.properties, with the same documented-justification pattern as the existing hotspot suppressions. --- open-sse/services/usage.ts | 2 -- scripts/ad-hoc/nvidia-startswith-diag.ts | 4 +++- sonar-project.properties | 10 +++++++++- .../dashboard/api-manager/ApiManagerPageClient.tsx | 9 +++++---- src/sse/handlers/chat.ts | 4 +++- src/sse/services/auth.ts | 2 +- 6 files changed, 21 insertions(+), 10 deletions(-) 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;