Mostly safe โ a couple of notes worth reading.
Scanned 5/3/2026, 6:41:36 PMยทCached resultยทFast Scanยท45 rulesยทHow we decide โ
AIVSS Score
Low
Severity Breakdown
0
critical
2
high
109
medium
8
low
MCP Server Information
Findings
This package carries a B grade with a safety score of 61/100 and presents 2 high-severity risks alongside 109 medium-severity issues, primarily concentrated in server configuration (53 findings) and resource exhaustion (49 findings). The most significant concerns involve potential ANSI escape injection vulnerabilities (9 instances) and readiness gaps (8 instances) that could affect stability and security posture. While no critical vulnerabilities were detected, the volume of medium-severity issues and configuration weaknesses warrant careful review before deployment, particularly if this server will handle untrusted input or run in resource-constrained environments.
No known CVEs found for this package or its dependencies.
Scan Details
Want deeper analysis?
Fast scan found 22 findings using rule-based analysis. Upgrade for LLM consensus across 5 judges, AI-generated remediation, and cross-file taint analysis.
Building your own MCP server?
Same rules, same LLM judges, same grade. Private scans stay isolated to your account and never appear in the public registry. Required for code your team hasnโt shipped yet.
22 of 22 findings
22 findings
TLS certificate verification is disabled on an outbound HTTP client. Any MITM in the network path can intercept and modify requests / responses โ credentials, tokens, and tool output flow over a channel with no integrity guarantee. Python requests / httpx: drop `verify=False`. If the peer is using a private CA, set `verify="/path/to/ca-bundle.pem"` or configure the system trust store. Node TS axios / fetch: drop `rejectUnauthorized: false` from the agent / `httpsAgent` options. Same private-CA
Evidence
| 211 | // (e.g., *.hubble-relay.cilium.io) than the default k8s service DNS name. |
| 212 | func (h *HubbleSource) discoverTLSServerName(address string) (string, error) { |
| 213 | probeCfg := &tls.Config{ |
| 214 | InsecureSkipVerify: true, |
| 215 | } |
| 216 | // Include client certs in case the server requires mTLS |
| 217 | if h.tlsConfig != nil && len(h.tlsConfig.Certificates) > 0 { |
Remediation
Drop the verify-disable flag. If the peer presents a private CA: - Python: pass `verify="/path/to/ca.pem"` or trust the system store - Node: `new https.Agent({ ca: fs.readFileSync("ca.pem") })` - Go: load the CA via `x509.NewCertPool().AppendCertsFromPEM(...)` and set `tls.Config.RootCAs` Self-signed certificates: import the cert into the OS trust chain rather than disabling verification per-call.
TLS certificate verification is disabled on an outbound HTTP client. Any MITM in the network path can intercept and modify requests / responses โ credentials, tokens, and tool output flow over a channel with no integrity guarantee. Python requests / httpx: drop `verify=False`. If the peer is using a private CA, set `verify="/path/to/ca-bundle.pem"` or configure the system trust store. Node TS axios / fetch: drop `rejectUnauthorized: false` from the agent / `httpsAgent` options. Same private-CA
Evidence
| 59 | } |
| 60 | } else if cfg.OIDCInsecureSkipVerify { |
| 61 | transport := http.DefaultTransport.(*http.Transport).Clone() |
| 62 | transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // user-requested via CLI flag |
| 63 | httpClient = &http.Client{Transport: transport} |
| 64 | log.Printf("[oidc] WARNING: TLS verification disabled for OIDC provider โ do NOT use in production") |
| 65 | } |
Remediation
Drop the verify-disable flag. If the peer presents a private CA: - Python: pass `verify="/path/to/ca.pem"` or trust the system store - Node: `new https.Agent({ ca: fs.readFileSync("ca.pem") })` - Go: load the CA via `x509.NewCertPool().AppendCertsFromPEM(...)` and set `tls.Config.RootCAs` Self-signed certificates: import the cert into the OS trust chain rather than disabling verification per-call.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 189 | const data = JSON.parse(event.data) as { message: string } |
| 190 | optionsRef.current?.onContextSwitchProgress?.(data.message) |
| 191 | } catch (e) { |
| 192 | console.error('Failed to parse context_switch_progress event:', e) |
| 193 | } |
| 194 | }) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 38 | setIsStreaming(true) |
| 39 | if (handlers.onConnected) { |
| 40 | try { handlers.onConnected(JSON.parse((event as MessageEvent).data)) } catch (e) { |
| 41 | console.error('Failed to parse connected event:', e) |
| 42 | } |
| 43 | } |
| 44 | }) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 206 | // Notify caller to invalidate caches (e.g., helm releases, resources) |
| 207 | optionsRef.current?.onContextChanged?.(data.context) |
| 208 | } catch (e) { |
| 209 | console.error('Failed to parse context_changed event:', e) |
| 210 | } |
| 211 | }) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 221 | const data = JSON.parse(event.data) as ConnectionState |
| 222 | optionsRef.current?.onConnectionStateChange?.(data) |
| 223 | } catch (e) { |
| 224 | console.error('Failed to parse connection_state event:', e) |
| 225 | } |
| 226 | }) |
| 227 | }, [sseNamespaceFilter, viewMode, showPolicyEffect]) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 174 | setEvents((prev) => [data, ...prev].slice(0, MAX_EVENTS)) |
| 175 | optionsRef.current?.onK8sEvent?.(data) |
| 176 | } catch (e) { |
| 177 | console.error('Failed to parse event:', e) |
| 178 | } |
| 179 | }) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 52 | es.addEventListener('pod_added', (event) => { |
| 53 | if (handlers.onPodAdded) { |
| 54 | try { handlers.onPodAdded(JSON.parse((event as MessageEvent).data)) } catch (e) { |
| 55 | console.error('Failed to parse pod_added event:', e) |
| 56 | } |
| 57 | } |
| 58 | }) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 60 | es.addEventListener('pod_removed', (event) => { |
| 61 | if (handlers.onPodRemoved) { |
| 62 | try { handlers.onPodRemoved(JSON.parse((event as MessageEvent).data)) } catch (e) { |
| 63 | console.error('Failed to parse pod_removed event:', e) |
| 64 | } |
| 65 | } |
| 66 | }) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 45 | es.addEventListener('log', (event) => { |
| 46 | try { handlers.onLog(JSON.parse((event as MessageEvent).data)) } catch (e) { |
| 47 | console.error('Failed to parse log event:', e) |
| 48 | } |
| 49 | }) |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 96 | queryCache: new QueryCache({ |
| 97 | onError: (error, query) => { |
| 98 | if (query.state.data !== undefined) { |
| 99 | console.warn('[Background sync failed]', query.queryKey, (error as Error).message); |
| 100 | } |
| 101 | }, |
| 102 | }), |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 1933 | if (!headers.has(k)) headers.set(k, v) |
| 1934 | } |
| 1935 | return new Promise((resolve, reject) => { |
| 1936 | fetch(url, { credentials: getCredentialsMode(), ...options, headers }) |
| 1937 | .then(async (response) => { |
| 1938 | if (!response.ok) { |
| 1939 | const error = await response.json().catch(() => ({ error: 'Unknown error' })) |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 29 | const ConnectionContext = createContext<ConnectionContextValue | null>(null) |
| 30 | |
| 31 | async function fetchConnectionStatus(): Promise<ConnectionStatusResponse> { |
| 32 | const response = await fetch(`${getApiBase()}/connection`, { |
| 33 | credentials: getCredentialsMode(), |
| 34 | headers: getAuthHeaders(), |
| 35 | }) |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 97 | authHeadersProvider = fn; |
| 98 | } |
| 99 | |
| 100 | /** Internal: returns the registered auth headers for fetch() calls. */ |
| 101 | export function getAuthHeaders(): Record<string, string> { |
| 102 | return authHeadersProvider(); |
| 103 | } |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 466 | throw new Error(body.error || `Failed to stop existing port forward (HTTP ${delRes.status})`) |
| 467 | } |
| 468 | deleted = true |
| 469 | const res = await fetch(apiUrl('/portforwards'), { |
| 470 | method: 'POST', |
| 471 | headers: { 'Content-Type': 'application/json' }, |
| 472 | body: JSON.stringify({ |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 255 | export async function fetchJSON<T>(path: string): Promise<T> { |
| 256 | const headers: Record<string, string> = {} |
| 257 | if (activeContext) headers['X-Radar-Context'] = activeContext |
| 258 | const response = await fetch(`${API_BASE}${path}`, { headers }) |
| 259 | // ... |
| 260 | } |
| 261 | ``` |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 74 | } |
| 75 | const configData = await configRes.json() |
| 76 | const updated = { ...configData.file, port: currentPort } |
| 77 | const res = await fetch(apiUrl('/config'), { |
| 78 | method: 'PUT', |
| 79 | credentials: getCredentialsMode(), |
| 80 | headers: { 'Content-Type': 'application/json', ...getAuthHeaders() }, |
Remediation
Pass timeout= on every call: - HTTP: `requests.get(url, timeout=5)`, `httpx.get(url, timeout=5.0)` - Node fetch: `AbortSignal.timeout(5000)` - Subprocess: `subprocess.run(["cmd"], timeout=30, check=True)` Pick a value short enough to fail fast and retry.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 264 | const pickTsFormat = useCallback((fmt: TimestampFormat) => { |
| 265 | setTsFormat(fmt) |
| 266 | try { localStorage.setItem('radar-logs-ts-format', fmt) } catch {} |
| 267 | // Auto-show timestamps when user picks a format โ otherwise the change isn't visible. |
| 268 | setShowTimestamps(true) |
| 269 | try { localStorage.setItem('radar-logs-timestamps', 'true') } catch {} |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 257 | const toggleTimestamps = useCallback(() => { |
| 258 | setShowTimestamps(prev => { |
| 259 | const next = !prev |
| 260 | try { localStorage.setItem('radar-logs-timestamps', String(next)) } catch {} |
| 261 | return next |
| 262 | }) |
| 263 | }, []) |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 136 | if (themeLocked) return |
| 137 | setIsDark(prev => { |
| 138 | const next = !prev |
| 139 | try { localStorage.setItem('radar-logs-dark', String(next)) } catch {} |
| 140 | return next |
| 141 | }) |
| 142 | }, [themeLocked]) |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 249 | const toggleWrap = useCallback(() => { |
| 250 | setWordWrap(prev => { |
| 251 | const next = !prev |
| 252 | try { localStorage.setItem('radar-logs-wrap', String(next)) } catch {} |
| 253 | return next |
| 254 | }) |
| 255 | }, []) |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 267 | try { localStorage.setItem('radar-logs-ts-format', fmt) } catch {} |
| 268 | // Auto-show timestamps when user picks a format โ otherwise the change isn't visible. |
| 269 | setShowTimestamps(true) |
| 270 | try { localStorage.setItem('radar-logs-timestamps', 'true') } catch {} |
| 271 | setShowTsMenu(false) |
| 272 | }, []) |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.