Use with caution. Address findings before production.
Scanned 5/3/2026, 7:09:34 PMยทCached resultยทFast Scanยท45 rulesยทHow we decide โ
AIVSS Score
Medium
Severity Breakdown
0
critical
3
high
71
medium
10
low
MCP Server Information
Findings
This package carries significant security concerns with a C grade and safety score of 45/100, driven primarily by 19 hardcoded secrets, 21 server configuration issues, and 17 ansi escape injection vulnerabilities across 71 medium-severity findings. The three high-severity issues combined with resource exhaustion and readiness problems suggest the package requires substantial remediation before production use. Installation should be deferred until these critical configuration and credential exposure issues are addressed.
No known CVEs found for this package or its dependencies.
Scan Details
Want deeper analysis?
Fast scan found 84 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.
Showing 1โ30 of 84 findings
84 findings
Tool asserts `readOnlyHint: true` (or `read_only_hint=True`) while the same file contains a destructive sink (fs.unlink / os.remove / DROP TABLE / DELETE FROM / TRUNCATE / `method: "DELETE"`). The annotation is dishonest โ clients will skip confirmation for a handler that in fact mutates state.
Evidence
| 1 | /** |
| 2 | * @license |
| 3 | * Copyright 2025 Google LLC |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
| 7 | import { describe, it, expect, beforeEach, afterEach } from 'vitest'; |
| 8 | import { |
| 9 | PolicyDecision, |
| 10 | ApprovalMode, |
| 11 | PRIORITY_SUBAGENT_TOOL, |
| 12 | } from './types.js'; |
| 13 | import * as fs from 'node:fs/promises'; |
| 14 | import * as path from 'node:path'; |
| 15 | import * as os from 'node:os'; |
| 16 | import { fileURLToPath } from 'node:url'; |
| 17 | import { |
| 18 | loadPoliciesFromToml, |
| 19 | validateMcpPolicyToolNames, |
| 20 | type PolicyLoadResult, |
| 21 | } from './toml- |
Remediation
Missing annotation: add `{ annotations: { destructiveHint: true } }` as the 4th argument to `server.tool(...)` (TS) or pass `destructive_hint=True` to the `@mcp.tool(...)` decorator (Python). readOnly-lying: remove the `readOnlyHint: true` / `read_only_hint=True` assertion, or refactor the handler so it no longer calls the destructive sink. Do not suppress with a waiver โ clients rely on these hints for user-facing confirmation.
File mounts an HTTP route that handles MCP `tools/list` (Express / Fastify / FastAPI / Flask) but the route โ and the router it sits behind โ has no auth middleware applied. An anonymous client can enumerate every tool the server exposes, scope the attack surface, and (if `tools/call` shares the route) invoke them. Apply auth at the route or router level: Express `passport.authenticate(...)` / a `requireAuth`-style middleware, FastAPI `Depends(get_current_user)` or `Depends(verify_jwt)`, Flask
Evidence
| 1 | /** |
| 2 | * @license |
| 3 | * Copyright 2025 Google LLC |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
| 7 | import { |
| 8 | McpServer, |
| 9 | type ToolCallback, |
| 10 | } from '@modelcontextprotocol/sdk/server/mcp.js'; |
| 11 | import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; |
| 12 | import express from 'express'; |
| 13 | import { type Server as HTTPServer } from 'node:http'; |
| 14 | import { type ZodRawShape } from 'zod'; |
| 15 | |
| 16 | export class TestMcpServer { |
| 17 | private server: HTTPServer | undefined; |
| 18 | |
| 19 | async start( |
| 20 | |
Remediation
Apply auth middleware at the route or router level: - Express / Fastify / Koa: `passport.authenticate(...)`, `requireAuth`, `verifyToken`, or an equivalent JWT middleware applied via `router.use(authMw)` or as a per-route handler. - FastAPI: `Depends(get_current_user)`, `OAuth2PasswordBearer`, `HTTPBearer`, or `verify_jwt` dependency. - Flask: `@login_required`, `@auth_required`, `@jwt_required`, or call `verify_jwt_in_request()` in the handler. Mounting MCP behind a s
File mounts an HTTP route that handles MCP `tools/list` (Express / Fastify / FastAPI / Flask) but the route โ and the router it sits behind โ has no auth middleware applied. An anonymous client can enumerate every tool the server exposes, scope the attack surface, and (if `tools/call` shares the route) invoke them. Apply auth at the route or router level: Express `passport.authenticate(...)` / a `requireAuth`-style middleware, FastAPI `Depends(get_current_user)` or `Depends(verify_jwt)`, Flask
Evidence
| 1 | /** |
| 2 | * @license |
| 3 | * Copyright 2025 Google LLC |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
| 7 | import * as vscode from 'vscode'; |
| 8 | import { |
| 9 | CloseDiffRequestSchema, |
| 10 | IdeContextNotificationSchema, |
| 11 | OpenDiffRequestSchema, |
| 12 | } from '@google/gemini-cli-core/src/ide/types.js'; |
| 13 | import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js'; |
| 14 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; |
| 15 | import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableH |
Remediation
Apply auth middleware at the route or router level: - Express / Fastify / Koa: `passport.authenticate(...)`, `requireAuth`, `verifyToken`, or an equivalent JWT middleware applied via `router.use(authMw)` or as a per-route handler. - FastAPI: `Depends(get_current_user)`, `OAuth2PasswordBearer`, `HTTPBearer`, or `verify_jwt` dependency. - Flask: `@login_required`, `@auth_required`, `@jwt_required`, or call `verify_jwt_in_request()` in the handler. Mounting MCP behind a s
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 120 | // GitHub |
| 121 | GITHUB_TOKEN_GHP: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 122 | GITHUB_TOKEN_GHO: 'gho_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 123 | GITHUB_TOKEN_GHU: 'ghu_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 124 | GITHUB_TOKEN_GHS: 'ghs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 125 | GITHUB_TOKEN_GHR: 'ghr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 126 | GITHUB_PAT: 'github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 125 | GITHUB_TOKEN_GHR: 'ghr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 126 | GITHUB_PAT: 'github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 127 | |
| 128 | GOOGLE_KEY: 'AIzaSyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 129 | // AWS |
| 130 | AWS_KEY: 'AKIAxxxxxxxxxxxxxxxx', |
| 131 | // JWT |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 265 | it('should NOT redact GEMINI_CLI_ variables even if their value looks like a secret (fully trusted)', () => { |
| 266 | const env = { |
| 267 | GEMINI_CLI_INTERNAL: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 268 | }; |
| 269 | const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS); |
| 270 | expect(sanitized).toEqual(env); |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 131 | // JWT |
| 132 | JWT_TOKEN: 'eyJhbGciOiJIUzI1NiJ9.e30.ZRrHA157xAA_7962-a_3rA', |
| 133 | // Stripe |
| 134 | STRIPE_SK_LIVE: 'sk_live_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 135 | STRIPE_RK_LIVE: 'rk_live_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 136 | STRIPE_SK_TEST: 'sk_test_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 137 | STRIPE_RK_TEST: 'rk_test_xxxxxxxxxxxxxxxxxxxxxxxx', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 133 | // Stripe |
| 134 | STRIPE_SK_LIVE: 'sk_live_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 135 | STRIPE_RK_LIVE: 'rk_live_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 136 | STRIPE_SK_TEST: 'sk_test_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 137 | STRIPE_RK_TEST: 'rk_test_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 138 | // Slack |
| 139 | SLACK_XOXB: 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 119 | const env = { |
| 120 | // GitHub |
| 121 | GITHUB_TOKEN_GHP: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 122 | GITHUB_TOKEN_GHO: 'gho_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 123 | GITHUB_TOKEN_GHU: 'ghu_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 124 | GITHUB_TOKEN_GHS: 'ghs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 125 | GITHUB_TOKEN_GHR: 'ghr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 233 | describe('value-first security: secret values must be caught even for allowed variable names', () => { |
| 234 | it('should redact ALWAYS_ALLOWED variables whose values contain a GitHub token', () => { |
| 235 | const env = { |
| 236 | HOME: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 237 | PATH: '/usr/bin', |
| 238 | }; |
| 239 | const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS); |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 121 | GITHUB_TOKEN_GHP: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 122 | GITHUB_TOKEN_GHO: 'gho_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 123 | GITHUB_TOKEN_GHU: 'ghu_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 124 | GITHUB_TOKEN_GHS: 'ghs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 125 | GITHUB_TOKEN_GHR: 'ghr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 126 | GITHUB_PAT: 'github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 127 |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 259 | cwd, |
| 260 | env: { |
| 261 | PATH: '/usr/bin', |
| 262 | GITHUB_TOKEN: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 263 | MY_SECRET: 'super-secret', |
| 264 | SAFE_VAR: 'is-safe', |
| 265 | }, |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 81 | it('returns the owner and repo for URL with classic PAT token (ghp_)', async () => { |
| 82 | vi.mocked(child_process.execSync).mockReturnValueOnce( |
| 83 | 'https://ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@github.com/owner/repo.git', |
| 84 | ); |
| 85 | expect(getGitHubRepoInfo()).toEqual({ owner: 'owner', repo: 'repo' }); |
| 86 | }); |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 216 | API_KEY: 'should-be-redacted', |
| 217 | MY_SECRET: 'super-secret', |
| 218 | // Redacted by value |
| 219 | GH_TOKEN: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 220 | JWT: 'eyJhbGciOiJIUzI1NiJ9.e30.ZRrHA157xAA_7962-a_3rA', |
| 221 | // Allowed by name but redacted by value |
| 222 | RANDOM_VAR: '-----BEGIN CERTIFICATE-----...', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 252 | it('should redact user-allowlisted variables whose values contain a secret', () => { |
| 253 | const env = { |
| 254 | MY_SAFE_VAR: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 255 | OTHER: 'fine', |
| 256 | }; |
| 257 | const sanitized = sanitizeEnvironment(env, { |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 118 | it('should redact variables with values matching all token and credential patterns', () => { |
| 119 | const env = { |
| 120 | // GitHub |
| 121 | GITHUB_TOKEN_GHP: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 122 | GITHUB_TOKEN_GHO: 'gho_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 123 | GITHUB_TOKEN_GHU: 'ghu_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 124 | GITHUB_TOKEN_GHS: 'ghs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 136 | STRIPE_SK_TEST: 'sk_test_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 137 | STRIPE_RK_TEST: 'rk_test_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 138 | // Slack |
| 139 | SLACK_XOXB: 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 140 | SLACK_XOXA: 'xoxa-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 141 | SLACK_XOXP: 'xoxp-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 142 | SLACK_XOXB_2: 'xoxr-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 137 | STRIPE_RK_TEST: 'rk_test_xxxxxxxxxxxxxxxxxxxxxxxx', |
| 138 | // Slack |
| 139 | SLACK_XOXB: 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 140 | SLACK_XOXA: 'xoxa-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 141 | SLACK_XOXP: 'xoxp-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 142 | SLACK_XOXB_2: 'xoxr-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 143 | // URL Credentials |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 361 | AppSecret: 'secret-value', |
| 362 | db_password: 'password-value', |
| 363 | RSA_KEY: '-----BEGIN RSA PRIVATE KEY-----...', |
| 364 | GITHUB_TOKEN_GHP: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 365 | SAFE_VAR: 'is-safe', |
| 366 | }; |
| 367 | const options = { |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 138 | // Slack |
| 139 | SLACK_XOXB: 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 140 | SLACK_XOXA: 'xoxa-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 141 | SLACK_XOXP: 'xoxp-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 142 | SLACK_XOXB_2: 'xoxr-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 143 | // URL Credentials |
| 144 | CREDS_IN_HTTPS_URL: 'https://user:password@example.com', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 122 | GITHUB_TOKEN_GHO: 'gho_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 123 | GITHUB_TOKEN_GHU: 'ghu_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 124 | GITHUB_TOKEN_GHS: 'ghs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 125 | GITHUB_TOKEN_GHR: 'ghr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 126 | GITHUB_PAT: 'github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
| 127 | |
| 128 | GOOGLE_KEY: 'AIzaSyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
Hardcoded secret detected in source. MCP servers often proxy between the model and a third-party API, so any committed credential grants that access to anyone who can read the repo. Move to an environment variable or secret manager and rotate the leaked value.
Evidence
| 139 | SLACK_XOXB: 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 140 | SLACK_XOXA: 'xoxa-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 141 | SLACK_XOXP: 'xoxp-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 142 | SLACK_XOXB_2: 'xoxr-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxx', |
| 143 | // URL Credentials |
| 144 | CREDS_IN_HTTPS_URL: 'https://user:password@example.com', |
| 145 | CREDS_IN_HTTP_URL: 'http://user:password@example.com', |
Remediation
Rotate the leaked credential immediately, then replace the hardcoded value with os.environ / process.env lookup. For deployment, inject the value through your secret manager (AWS Secrets Manager, Doppler, Vault).
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
| 111 | const minimized = review.isMinimized |
| 112 | ? ` (Minimized: ${review.minimizedReason})` |
| 113 | : ''; |
| 114 | console.log( |
| 115 | `\n${icon} ${review.state} by ${review.author.login} at ${review.createdAt}${minimized}: "${review.body}"`, |
| 116 | ); |
| 117 | } |
| 118 | }); |
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
| 175 | `๐ Tail collector logs in another terminal: tail -f ${OTEL_LOG_FILE}`, |
| 176 | ); |
| 177 | console.log(`\n๐ View your telemetry data in Google Cloud Console:`); |
| 178 | console.log( |
| 179 | ` - Logs: https://console.cloud.google.com/logs/query;query=logName%3D%22projects%2F${projectId}%2Flogs%2Fgemini_cli%22?project=${projectId}`, |
| 180 | ); |
| 181 | console.log( |
| 182 | ` - Metrics: https://console.cloud.google.com/monitoring/metrics-explorer?project=${projectId}`, |
| 183 | ); |
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
| 145 | ? `(Line ${range}) ` |
| 146 | : ''; |
| 147 | const minimized = c.isMinimized ? ` (Minimized: ${c.minimizedReason})` : ''; |
| 148 | console.log( |
| 149 | `\n๐ฌ ${minimized}${c.author.login} | ${c.createdAt} ${fileInfo}\n${c.body}`, |
| 150 | ); |
| 151 | printThread(c.id); |
| 152 | }); |
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
| 28 | const server = http.createServer((req, res) => { |
| 29 | // Deny all requests other than CONNECT for HTTPS |
| 30 | console.log( |
| 31 | `[PROXY] Denying non-CONNECT request for: ${req.method} ${req.url}`, |
| 32 | ); |
| 33 | res.writeHead(405, { 'Content-Type': 'text/plain' }); |
| 34 | res.end('Method Not Allowed'); |
| 35 | }); |
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
| 167 | console.log(`Processing single issue #${context.payload.issue.number}...`); |
| 168 | await processSingleIssue(context.repo.owner, context.repo.repo, context.payload.issue.number); |
| 169 | } else { |
| 170 | console.log(`Running for event: ${context.eventName}. Processing all open issues...`); |
| 171 | await processAllOpenIssues(context.repo.owner, context.repo.repo); |
| 172 | } |
| 173 | } catch (error) { |
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
| 106 | try { |
| 107 | const input = fs.readFileSync(0, 'utf-8'); |
| 108 | console.error('DEBUG: AfterAgent hook input received'); |
| 109 | process.stdout.write("Received Input: " + input); |
| 110 | } catch (err) { |
| 111 | console.error('Hook Failed:', err); |
| 112 | process.exit(1); |
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
| 128 | const minimized = reply.isMinimized |
| 129 | ? ` (Minimized: ${reply.minimizedReason})` |
| 130 | : ''; |
| 131 | console.log( |
| 132 | `${indent}โณ [${reply.createdAt}] ${reply.author.login}${minimized}: ${reply.body}`, |
| 133 | ); |
| 134 | printThread(reply.id, depth + 1); |
| 135 | }); |
| 136 | }; |
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
| 39 | // req.url will be in the format "hostname:port" for a CONNECT request. |
| 40 | const { port, hostname } = new URL(`http://${req.url}`); |
| 41 | |
| 42 | console.log(`[PROXY] Intercepted CONNECT request for: ${hostname}:${port}`); |
| 43 | |
| 44 | if ( |
| 45 | ALLOWED_DOMAINS.some( |
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
| 1105 | ); |
| 1106 | const baPath = rig.createScript( |
| 1107 | 'ba_hook.cjs', |
| 1108 | "console.log(JSON.stringify({decision: 'allow', hookSpecificOutput: {hookEventName: 'BeforeAgent', additionalContext: 'BeforeAgent: User request processed'}}));", |
| 1109 | ); |
| 1110 | |
| 1111 | const beforeToolCommand = `node "${btPath}"`; |
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
| 194 | run(`git push --set-upstream origin ${hotfixBranch}`, dryRun); |
| 195 | |
| 196 | // Create the pull request. |
| 197 | console.log( |
| 198 | `Creating pull request from ${hotfixBranch} to ${releaseBranch}...`, |
| 199 | ); |
| 200 | let prTitle = `fix(patch): cherry-pick ${commit.substring(0, 7)} to ${releaseBranch} to patch version ${releaseInfo.currentTag} and create version ${releaseInfo.nextVersion}`; |
| 201 | let prBody = `This PR automatically cherry-picks commit ${commit} to patch version ${releaseInfo.currentTag} in the ${channel} releas |
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
| 105 | const fs = require('fs'); |
| 106 | try { |
| 107 | const input = fs.readFileSync(0, 'utf-8'); |
| 108 | console.error('DEBUG: AfterAgent hook input received'); |
| 109 | process.stdout.write("Received Input: " + input); |
| 110 | } catch (err) { |
| 111 | console.error('Hook Failed:', err); |
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
| 164 | // Main execution |
| 165 | try { |
| 166 | if (context.eventName === 'issues') { |
| 167 | console.log(`Processing single issue #${context.payload.issue.number}...`); |
| 168 | await processSingleIssue(context.repo.owner, context.repo.repo, context.payload.issue.number); |
| 169 | } else { |
| 170 | console.log(`Running for event: ${context.eventName}. Processing all open issues...`); |
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
| 789 | // Create script file for hook |
| 790 | const scriptPath = rig.createScript( |
| 791 | 'notification_hook.cjs', |
| 792 | "console.log(JSON.stringify({suppressOutput: false, systemMessage: 'Permission request logged by security hook'}));", |
| 793 | ); |
| 794 | |
| 795 | const hookCommand = `node "${scriptPath}"`; |
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
| 413 | ), |
| 414 | }); |
| 415 | const hookScript = `const fs = require('fs'); |
| 416 | console.log(JSON.stringify({ |
| 417 | decision: "allow", |
| 418 | hookSpecificOutput: { |
| 419 | hookEventName: "BeforeModel", |
| 420 | llm_request: { |
| 421 | messages: [ |
| 422 | { |
| 423 | role: "user", |
| 424 | content: "Please respond with exactly: The security hook modified this request successfully." |
| 425 | } |
| 426 | ] |
| 427 | } |
| 428 | } |
| 429 | }));`; |
| 430 | |
| 431 | const scriptPath = rig.createScript( |
| 432 | 'before_model_hook.cjs', |
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
| 49 | script: | |
| 50 | // Parse the comment body directly to extract channel(s) |
| 51 | const commentBody = process.env.COMMENT_BODY; |
| 52 | console.log('Comment body:', commentBody); |
| 53 | |
| 54 | let channels = ['stable', 'preview']; // default to both |
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
| 30 | * @param event - The stream event to emit |
| 31 | */ |
| 32 | emitEvent(event: JsonStreamEvent): void { |
| 33 | process.stdout.write(this.formatEvent(event)); |
| 34 | } |
| 35 | |
| 36 | /** |
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
| 90 | const minimized = c.isMinimized |
| 91 | ? ` (Minimized: ${c.minimizedReason})` |
| 92 | : ''; |
| 93 | console.log( |
| 94 | `[${c.createdAt}] [${c.author.login}]${minimized}: ${c.body}\n`, |
| 95 | ); |
| 96 | }); |
| 97 | } |
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
| 651 | describe('polling', () => { |
| 652 | it('should poll and emit changes', async () => { |
| 653 | // Initial fetch |
| 654 | (mockServer.fetchAdminControls as Mock).mockResolvedValue({ |
| 655 | strictModeDisabled: true, |
| 656 | adminControlsApplicable: true, |
| 657 | }); |
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 | |
| 76 | this.a2aFetch = (input, init) => |
| 77 | fetch(input, { ...init, dispatcher: this.a2aDispatcher } as RequestInit); |
| 78 | } |
| 79 | |
| 80 | /** |
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
| 13 | ```javascript |
| 14 | // Sequential operations are slower |
| 15 | const data1 = await fetch(url1).then((r) => r.json()); |
| 16 | const data2 = await fetch(url2).then((r) => r.json()); |
| 17 | |
| 18 | // Prefer parallel operations for better performance |
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
| 210 | */ |
| 211 | prompt?: string; |
| 212 | /** |
| 213 | * Direct URL to fetch (experimental mode). |
| 214 | */ |
| 215 | url?: string; |
| 216 | } |
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
| 14 | ```javascript |
| 15 | // Sequential operations are slower |
| 16 | const data1 = await fetch(url1).then((r) => r.json()); |
| 17 | const data2 = await fetch(url2).then((r) => r.json()); |
| 18 | |
| 19 | // Prefer parallel operations for better performance |
| 20 | // Start requests concurrently |
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
| 19 | // Prefer parallel operations for better performance |
| 20 | // Start requests concurrently |
| 21 | const p1 = fetch(url1).then((r) => r.json()); |
| 22 | const p2 = fetch(url2).then((r) => r.json()); |
| 23 | |
| 24 | // Wait for all results |
| 25 | const [data1, data2] = await Promise.all([p1, p2]); |
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 | fs.unlinkSync(tmpPath); |
| 75 | } |
| 76 | |
| 77 | const response = await fetch(url, { redirect: 'follow' }); |
| 78 | if (!response.ok) { |
| 79 | throw new Error( |
| 80 | `Download failed: HTTP ${response.status} ${response.statusText}`, |
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
| 62 | } |
| 63 | |
| 64 | console.log(`[Download] ${url} -> ${archivePath}`); |
| 65 | const response = await fetch(url); |
| 66 | if (!response.ok) { |
| 67 | throw new Error(`Failed to fetch ${url}: ${response.statusText}`); |
| 68 | } |
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
| 18 | // Prefer parallel operations for better performance |
| 19 | // Start requests concurrently |
| 20 | const p1 = fetch(url1).then((r) => r.json()); |
| 21 | const p2 = fetch(url2).then((r) => r.json()); |
| 22 | |
| 23 | // Wait for all results |
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
| 71 | } |
| 72 | |
| 73 | console.log(`Auditing ${url}...`); |
| 74 | fetch(url, { method: 'HEAD' }) |
| 75 | .then((r) => console.log(`Result: Success (Status ${r.status})`)) |
| 76 | .catch((e) => console.error(`Result: Failed (${e.message})`)); |
| 77 | ``` |
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
| 64 | const refToFetch = installMetadata.ref || 'HEAD'; |
| 65 | |
| 66 | await git.fetch(remotes[0].name, refToFetch); |
| 67 | |
| 68 | // After fetching, checkout FETCH_HEAD to get the content of the fetched ref. |
| 69 | // This results in a detached HEAD state, which is fine for this purpose. |
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
| 63 | `[WhisperModelManager] Downloading ${modelName} from ${url}`, |
| 64 | ); |
| 65 | |
| 66 | const response = await fetch(url); |
| 67 | if (!response.ok) { |
| 68 | throw new Error(`Failed to download model: ${response.statusText}`); |
| 69 | } |
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
| 163 | method: 'POST', |
| 164 | }); |
| 165 | |
| 166 | await global.fetch(request); |
| 167 | |
| 168 | expect(mockFetch).toHaveBeenCalled(); |
| 169 | const [, calledInit] = mockFetch.mock.calls[0]; |
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
| 121 | authServerMetadataUrl: string, |
| 122 | ): Promise<OAuthAuthorizationServerMetadata | null> { |
| 123 | try { |
| 124 | const response = await fetch(authServerMetadataUrl); |
| 125 | if (!response.ok) { |
| 126 | return null; |
| 127 | } |
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 | resourceMetadataUrl: string, |
| 98 | ): Promise<OAuthProtectedResourceMetadata | null> { |
| 99 | try { |
| 100 | const response = await fetch(resourceMetadataUrl); |
| 101 | if (!response.ok) { |
| 102 | return null; |
| 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.
Overly permissive file mode or IAM wildcard. chmod 0777 and IAM "Action": "*" or "Resource": "*" grant far more access than a least-privilege MCP server needs.
Evidence
| 690 | expect(result.failed).toBeGreaterThan(0); |
| 691 | } finally { |
| 692 | // Restore permissions so cleanup can proceed in afterEach |
| 693 | await fs.chmod(chatsDir, 0o777); |
| 694 | await fs.chmod(targetFile, 0o666); |
| 695 | } |
| 696 | }, |
Remediation
File modes: use 0600 for secrets, 0644 for read-only data, 0755 for directories. IAM: scope Action to the specific service+verb (s3:GetObject) and Resource to exact ARNs. Never use * for both.
Overly permissive file mode or IAM wildcard. chmod 0777 and IAM "Action": "*" or "Resource": "*" grant far more access than a least-privilege MCP server needs.
Evidence
| 691 | } finally { |
| 692 | // Restore permissions so cleanup can proceed in afterEach |
| 693 | await fs.chmod(chatsDir, 0o777); |
| 694 | await fs.chmod(targetFile, 0o666); |
| 695 | } |
| 696 | }, |
| 697 | ); |
Remediation
File modes: use 0600 for secrets, 0644 for read-only data, 0755 for directories. IAM: scope Action to the specific service+verb (s3:GetObject) and Resource to exact ARNs. Never use * for both.
Overly permissive file mode or IAM wildcard. chmod 0777 and IAM "Action": "*" or "Resource": "*" grant far more access than a least-privilege MCP server needs.
Evidence
| 717 | expect.stringContaining('Session cleanup failed'), |
| 718 | ); |
| 719 | } finally { |
| 720 | await fs.chmod(chatsDir, 0o777); |
| 721 | } |
| 722 | }, |
| 723 | ); |
Remediation
File modes: use 0600 for secrets, 0644 for read-only data, 0755 for directories. IAM: scope Action to the specific service+verb (s3:GetObject) and Resource to exact ARNs. Never use * for both.
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 13 | /** |
| 14 | * The absolute path to the file. |
| 15 | */ |
| 16 | path: z.string(), |
| 17 | /** |
| 18 | * The unix timestamp of when the file was last focused. |
| 19 | */ |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 4236 | "type": "string", |
| 4237 | "description": "Type of hook (currently only \"command\" supported)." |
| 4238 | }, |
| 4239 | "command": { |
| 4240 | "type": "string", |
| 4241 | "description": "Shell command to execute. Receives JSON input via stdin and returns JSON output via stdout." |
| 4242 | }, |
| 4243 | "description": { |
| 4244 | "type": "string", |
| 4245 | "description": "A description of the hook." |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 353 | }); |
| 354 | |
| 355 | const McpServerConfigSchema = z.object({ |
| 356 | url: z.string().optional(), |
| 357 | type: z.enum(['sse', 'http']).optional(), |
| 358 | trust: z.boolean().optional(), |
| 359 | includeTools: z.array(z.string()).optional(), |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 3839 | "description": "Admin-required MCP server configuration (remote transports only).", |
| 3840 | "additionalProperties": false, |
| 3841 | "properties": { |
| 3842 | "url": { |
| 3843 | "type": "string", |
| 3844 | "description": "URL for the required MCP server." |
| 3845 | }, |
| 3846 | "type": { |
| 3847 | "type": "string", |
| 3848 | "description": "Transport type for the required server.", |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 3954 | "description": "Configuration for the bug report helper command.", |
| 3955 | "additionalProperties": false, |
| 3956 | "properties": { |
| 3957 | "urlTemplate": { |
| 3958 | "type": "string", |
| 3959 | "description": "Template used to open a bug report URL. Variables in the template are populated at runtime." |
| 3960 | } |
| 3961 | }, |
| 3962 | "required": ["urlTemplate"] |
| 3963 | }, |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 3753 | "type": "string", |
| 3754 | "description": "Working directory for the server process." |
| 3755 | }, |
| 3756 | "url": { |
| 3757 | "type": "string", |
| 3758 | "description": "URL for SSE or HTTP transport. Use with \"type\" field to specify transport type." |
| 3759 | }, |
| 3760 | "httpUrl": { |
| 3761 | "type": "string", |
| 3762 | "description": "Streaming HTTP transport URL." |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 3731 | "description": "Definition of a Model Context Protocol (MCP) server configuration.", |
| 3732 | "additionalProperties": false, |
| 3733 | "properties": { |
| 3734 | "command": { |
| 3735 | "type": "string", |
| 3736 | "description": "Executable invoked for stdio transport." |
| 3737 | }, |
| 3738 | "args": { |
| 3739 | "type": "array", |
| 3740 | "description": "Command-line arguments for the stdio transport command.", |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 368 | export const RequiredMcpServerConfigSchema = z.object({ |
| 369 | // Connection (required for forced servers) |
| 370 | url: z.string(), |
| 371 | type: z.enum(['sse', 'http']), |
| 372 | |
| 373 | // Auth |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 684 | const keybindingsSchema = z.array( |
| 685 | z |
| 686 | .object({ |
| 687 | command: z.string().transform((val, ctx) => { |
| 688 | const negate = val.startsWith('-'); |
| 689 | const commandId = negate ? val.slice(1) : val; |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 56 | args: z.array(z.string()).optional(), |
| 57 | env: z.record(z.string()).optional(), |
| 58 | cwd: z.string().optional(), |
| 59 | url: z.string().optional(), |
| 60 | http_url: z.string().optional(), |
| 61 | headers: z.record(z.string()).optional(), |
| 62 | tcp: z.string().optional(), |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 77 | mcpName: z.string().optional(), |
| 78 | argsPattern: z.string().optional(), |
| 79 | commandPrefix: z.union([z.string(), z.array(z.string())]).optional(), |
| 80 | commandRegex: z.string().optional(), |
| 81 | priority: z.number().int().default(0), |
| 82 | modes: z.array(z.nativeEnum(ApprovalMode)).optional(), |
| 83 | toolAnnotations: z.record(z.any()).optional(), |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 42 | mcpName: z.string().optional(), |
| 43 | argsPattern: z.string().optional(), |
| 44 | commandPrefix: z.union([z.string(), z.array(z.string())]).optional(), |
| 45 | commandRegex: z.string().optional(), |
| 46 | decision: z.nativeEnum(PolicyDecision), |
| 47 | // Priority must be in range [0, 999] to prevent tier overflow. |
| 48 | // With tier transformation (tier + priority/1000), this ensures: |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 52 | .regex(/^[a-z0-9-_]+$/, 'Name must be a valid slug'); |
| 53 | |
| 54 | const mcpServerSchema = z.object({ |
| 55 | command: z.string().optional(), |
| 56 | args: z.array(z.string()).optional(), |
| 57 | env: z.record(z.string()).optional(), |
| 58 | cwd: z.string().optional(), |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
Package declares an install-time hook (npm postinstall/preinstall/prepare, setup.py cmdclass override, custom setuptools install class, or non-default pyproject build-backend). Anyone installing this package runs the hook. Confirm the hook is necessary and review its contents; prefer shipping a plain library without install-time execution.
Evidence
| 10 | "url": "https://github.com/lvce-editor/ripgrep" |
| 11 | }, |
| 12 | "scripts": { |
| 13 | "postinstall": "node ./src/postinstall.js", |
| 14 | "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", |
| 15 | "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch", |
| 16 | "format": "prettier --write ." |
Remediation
Prefer libraries that do not require install-time code execution: - Drop `postinstall`/`preinstall`/`prepare` scripts if the work can happen at runtime or build-time instead. - Ship pre-built native binaries rather than compiling via a custom `cmdclass` or `build_ext` override. - For Dockerfiles: replace `RUN curl โฆ | sh` with a pinned download + checksum verification + explicit `RUN` of a named script. - If the hook is unavoidable, document exactly what it does so downstream reviewers
Package declares an install-time hook (npm postinstall/preinstall/prepare, setup.py cmdclass override, custom setuptools install class, or non-default pyproject build-backend). Anyone installing this package runs the hook. Confirm the hook is necessary and review its contents; prefer shipping a plain library without install-time execution.
Evidence
| 64 | "format": "prettier --experimental-cli --write .", |
| 65 | "typecheck": "npm run typecheck --workspaces --if-present && tsc -b evals/tsconfig.json integration-tests/tsconfig.json memory-tests/tsconfig.json", |
| 66 | "preflight": "npm run clean && npm ci && npm run format && npm run build && npm run lint:ci && npm run typecheck && npm run test:ci", |
| 67 | "prepare": "husky && npm run bundle", |
| 68 | "prepare:package": "node scripts/prepare-package.js", |
| 69 | "release:version": "node scripts/version.js", |
| 70 | "tel |
Remediation
Prefer libraries that do not require install-time code execution: - Drop `postinstall`/`preinstall`/`prepare` scripts if the work can happen at runtime or build-time instead. - Ship pre-built native binaries rather than compiling via a custom `cmdclass` or `build_ext` override. - For Dockerfiles: replace `RUN curl โฆ | sh` with a pinned download + checksum verification + explicit `RUN` of a named script. - If the hook is unavoidable, document exactly what it does so downstream reviewers
Package declares an install-time hook (npm postinstall/preinstall/prepare, setup.py cmdclass override, custom setuptools install class, or non-default pyproject build-backend). Anyone installing this package runs the hook. Confirm the hook is necessary and review its contents; prefer shipping a plain library without install-time execution.
Evidence
| 111 | "build:dev": "node esbuild.js", |
| 112 | "build:prod": "node esbuild.js --production", |
| 113 | "generate:notices": "node ./scripts/generate-notices.js", |
| 114 | "prepare": "npm run generate:notices", |
| 115 | "check-types": "tsc --noEmit", |
| 116 | "lint": "eslint src", |
| 117 | "watch": "npm-run-all2 -p watch:*", |
Remediation
Prefer libraries that do not require install-time code execution: - Drop `postinstall`/`preinstall`/`prepare` scripts if the work can happen at runtime or build-time instead. - Ship pre-built native binaries rather than compiling via a custom `cmdclass` or `build_ext` override. - For Dockerfiles: replace `RUN curl โฆ | sh` with a pinned download + checksum verification + explicit `RUN` of a named script. - If the hook is unavoidable, document exactly what it does so downstream reviewers
Dockerfile never sets a non-root `USER` directive, so the CMD runs as root by default. Any RCE or library-level vulnerability exploited inside this container gets full privileges (MCP Top-10 R3). Add `USER <non-root>` before CMD / ENTRYPOINT in the final stage โ e.g. `USER 1000`, `USER nobody`, or `USER nonroot` on distroless.
Evidence
| 1 | # Use a common base image like Debian. |
| 2 | # Using 'bookworm-slim' for a balance of size and compatibility. |
| 3 | FROM debian:bookworm-slim |
| 4 | |
| 5 | # Set environment variables to prevent interactive prompts during installation |
| 6 | ENV DEBIAN_FRONTEND=noninteractive |
| 7 | ENV NODE_VERSION=20.12.2 |
| 8 | ENV NODE_VERSION_MAJOR=20 |
| 9 | ENV DOCKER_CLI_VERSION=26.1.3 |
| 10 | ENV BUILDX_VERSION=v0.14.0 |
| 11 | |
| 12 | # Install dependencies for adding NodeSource repository, gcloud, and other tools |
| 13 | # - curl: for downloading files |
| 14 | # - gnupg: for managing GPG keys |
Remediation
Create and switch to a non-root user before the CMD / ENTRYPOINT: RUN adduser --system --uid 1000 app USER 1000 Or reuse the base image's shipped non-root account (e.g. `USER nobody`, `USER nonroot` on distroless). Multi-stage builds only need the USER directive in the final stage.
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
| 109 | try { |
| 110 | execSync('pkill -f "jaeger"'); |
| 111 | console.log('โ Stopped existing jaeger process.'); |
| 112 | } catch {} // eslint-disable-line no-empty |
| 113 | try { |
| 114 | if (fileExists(OTEL_LOG_FILE)) fs.unlinkSync(OTEL_LOG_FILE); |
| 115 | console.log('โ Deleted old collector log.'); |
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
| 44 | { |
| 45 | selector: 'CatchClause > Identifier[name=/^_/]', |
| 46 | message: |
| 47 | 'Do not use underscored identifiers in catch blocks. If the error is unused, use "catch {}". If it is used, remove the underscore.', |
| 48 | }, |
| 49 | ]; |
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
| 151 | issue_number: issueNumber |
| 152 | }); |
| 153 | linkedIssues = [{ number: issueNumber, labels: { nodes: issue.labels.map(l => ({ name: l.name })) } }]; |
| 154 | } catch (e) {} |
| 155 | } |
| 156 | |
| 157 | // 3. Enforcement Logic |
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
| 151 | } else { |
| 152 | try { |
| 153 | fsMod.rmSync(finalRuntimeDir, { recursive: true, force: true }); |
| 154 | } catch {} |
| 155 | } |
| 156 | } else { |
| 157 | try { |
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
| 171 | const input = JSON.parse(fs.readFileSync(0, 'utf-8')); |
| 172 | const messageCount = input.llm_request?.contents?.length || 0; |
| 173 | let counts = []; |
| 174 | try { counts = JSON.parse(fs.readFileSync(${escapedPath}, 'utf-8')); } catch (e) {} |
| 175 | counts.push(messageCount); |
| 176 | fs.writeFileSync(${escapedPath}, JSON.stringify(counts)); |
| 177 | console.log(JSON.stringify({ decision: 'allow' })); |
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
| 224 | if (d > lastActivity) lastActivity = d; |
| 225 | } |
| 226 | } |
| 227 | } catch (e) {} |
| 228 | |
| 229 | if (lastActivity < thirtyDaysAgo) { |
| 230 | const labels = pr.labels.map(l => l.name.toLowerCase()); |
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
| 156 | } else { |
| 157 | try { |
| 158 | fsMod.rmSync(finalRuntimeDir, { recursive: true, force: true }); |
| 159 | } catch {} |
| 160 | } |
| 161 | } |
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
| 105 | try { |
| 106 | execSync('pkill -f "otelcol-contrib"'); |
| 107 | console.log('โ Stopped existing otelcol-contrib process.'); |
| 108 | } catch {} // eslint-disable-line no-empty |
| 109 | try { |
| 110 | execSync('pkill -f "jaeger"'); |
| 111 | console.log('โ Stopped existing jaeger process.'); |
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
| 214 | ); |
| 215 | try { |
| 216 | fsMod.rmSync(setupDir, { recursive: true, force: true }); |
| 217 | } catch {} |
| 218 | process.exit(1); |
| 219 | } |
| 220 | } |
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
| 202 | runtimeDir = finalRuntimeDir; |
| 203 | try { |
| 204 | fsMod.rmSync(setupDir, { recursive: true, force: true }); |
| 205 | } catch {} |
| 206 | } else { |
| 207 | throw renameErr; |
| 208 | } |
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.
allowed-path