Mostly safe โ a couple of notes worth reading.
Scanned 5/3/2026, 7:20:14 PMยทCached resultยทFast Scanยท45 rulesยทHow we decide โ
AIVSS Score
Low
Severity Breakdown
0
critical
0
high
29
medium
12
low
MCP Server Information
Findings
This package receives a B grade with a safety score of 75/100, driven primarily by 29 medium-severity issues across server readiness, configuration, and error handling rather than critical vulnerabilities. The main concerns are verbose error messages that could leak sensitive information, suboptimal server configuration practices, and readiness checks that may not adequately validate the server state before operation. While there are no critical or high-severity findings, addressing the medium-severity issuesโparticularly around error verbosity and configuration hardeningโwould significantly improve its security posture before production deployment.
No known CVEs found for this package or its dependencies.
Scan Details
Want deeper analysis?
Fast scan found 41 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 41 findings
41 findings
Service binds to 0.0.0.0 โ all network interfaces. For MCP servers that only need to talk to a single parent process, bind to 127.0.0.1 (or a Unix domain socket) instead.
Evidence
| 68 | if __name__ == "__main__": |
| 69 | import uvicorn |
| 70 | |
| 71 | uvicorn.run(app, host="0.0.0.0", port=8233) |
Remediation
Bind to 127.0.0.1 for local-only access. If cross-host access is truly required, put the service behind an authenticated reverse proxy rather than exposing it on 0.0.0.0.
Service binds to 0.0.0.0 โ all network interfaces. For MCP servers that only need to talk to a single parent process, bind to 127.0.0.1 (or a Unix domain socket) instead.
Evidence
| 242 | port = int(os.environ.get("WEB_PORT", "8233")) |
| 243 | config = uvicorn.Config( |
| 244 | build_web_app(), host="0.0.0.0", port=port, log_level="warning", |
| 245 | ) |
| 246 | web_server = uvicorn.Server(config) |
Remediation
Bind to 127.0.0.1 for local-only access. If cross-host access is truly required, put the service behind an authenticated reverse proxy rather than exposing it on 0.0.0.0.
Service binds to 0.0.0.0 โ all network interfaces. For MCP servers that only need to talk to a single parent process, bind to 127.0.0.1 (or a Unix domain socket) instead.
Evidence
| 17 | backend-api: |
| 18 | build: |
| 19 | context: ./backend |
| 20 | command: uvicorn main:app --host 0.0.0.0 --port 8233 |
| 21 | environment: |
| 22 | DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?ssl=disable |
| 23 | API_TOKEN: ${API_TOKEN:-} |
Remediation
Bind to 127.0.0.1 for local-only access. If cross-host access is truly required, put the service behind an authenticated reverse proxy rather than exposing it on 0.0.0.0.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 502 | return content |
| 503 | except Exception as e: |
| 504 | # Catch both ValueError (not found) and other exceptions |
| 505 | return f"Error: {str(e)}" |
| 506 | |
| 507 | |
| 508 | @write_tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 379 | return "\n".join(lines) |
| 380 | |
| 381 | except Exception as e: |
| 382 | return f"Error generating glossary index: {str(e)}" |
| 383 | |
| 384 | |
| 385 | async def generate_diagnostic_view(domain: str, days_stale: int = 30, max_children: int = 10) -> str: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 331 | return "\n".join(lines) |
| 332 | |
| 333 | except Exception as e: |
| 334 | return f"Error generating recent memories view: {str(e)}" |
| 335 | |
| 336 | |
| 337 | async def generate_glossary_index_view() -> str: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 1088 | except ValueError as e: |
| 1089 | return f"Error: {str(e)}" |
| 1090 | except Exception as e: |
| 1091 | return f"Error: {str(e)}" |
| 1092 | |
| 1093 | |
| 1094 | @mcp.tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 965 | except ValueError as e: |
| 966 | return f"Error: {str(e)}" |
| 967 | except Exception as e: |
| 968 | return f"Error: {str(e)}" |
| 969 | |
| 970 | |
| 971 | @write_tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 601 | except ValueError as e: |
| 602 | return f"Error: {str(e)}" |
| 603 | except Exception as e: |
| 604 | return f"Error: {str(e)}" |
| 605 | |
| 606 | |
| 607 | @write_tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 814 | except ValueError as e: |
| 815 | return f"Error: {str(e)}" |
| 816 | except Exception as e: |
| 817 | return f"Error: {str(e)}" |
| 818 | |
| 819 | |
| 820 | @write_tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 867 | except ValueError as e: |
| 868 | return f"Error: {str(e)}" |
| 869 | except Exception as e: |
| 870 | return f"Error: {str(e)}" |
| 871 | |
| 872 | |
| 873 | @write_tool() |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 599 | ) |
| 600 | |
| 601 | except ValueError as e: |
| 602 | return f"Error: {str(e)}" |
| 603 | except Exception as e: |
| 604 | return f"Error: {str(e)}" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 486 | return "\n".join(lines).strip() |
| 487 | |
| 488 | except Exception as e: |
| 489 | return f"Error generating diagnostic view: {str(e)}" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 865 | return msg |
| 866 | |
| 867 | except ValueError as e: |
| 868 | return f"Error: {str(e)}" |
| 869 | except Exception as e: |
| 870 | return f"Error: {str(e)}" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 281 | return "\n".join(lines) |
| 282 | |
| 283 | except Exception as e: |
| 284 | return f"Error generating index: {str(e)}" |
| 285 | |
| 286 | |
| 287 | async def generate_recent_memories_view(limit: int = 10) -> str: |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 1086 | return "\n".join(lines) |
| 1087 | |
| 1088 | except ValueError as e: |
| 1089 | return f"Error: {str(e)}" |
| 1090 | except Exception as e: |
| 1091 | return f"Error: {str(e)}" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 812 | return message |
| 813 | |
| 814 | except ValueError as e: |
| 815 | return f"Error: {str(e)}" |
| 816 | except Exception as e: |
| 817 | return f"Error: {str(e)}" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 963 | return msg |
| 964 | |
| 965 | except ValueError as e: |
| 966 | return f"Error: {str(e)}" |
| 967 | except Exception as e: |
| 968 | return f"Error: {str(e)}" |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
Full exception detail or stack trace returned to the caller. Leaking tracebacks exposes internal paths, library versions, and query structure โ useful recon for attackers.
Evidence
| 1143 | return "\n".join(lines) |
| 1144 | |
| 1145 | except Exception as e: |
| 1146 | return f"Error: {str(e)}" |
| 1147 | |
| 1148 | |
| 1149 | # ============================================================================= |
Remediation
Log the full exception server-side with a correlation ID; return only {"error_id": id, "message": "internal error"} to the caller. Never enable Flask debug mode in production.
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
| 212 | @router.put("/node") |
| 213 | async def update_node( |
| 214 | path: str = Query(...), |
| 215 | domain: str = Query("core"), |
| 216 | body: NodeUpdate = ... |
| 217 | ): |
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
| 135 | if scope["type"] != "http": |
| 136 | await self.backend(scope, receive, send) |
| 137 | return |
| 138 | path: str = scope.get("path", "/") |
| 139 | if any(path == p or path.startswith(p + "/") for p in backend_prefixes): |
| 140 | await self.backend(scope, receive, send) |
| 141 | return |
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
| 73 | @router.get("/node") |
| 74 | async def get_node( |
| 75 | path: str = Query("", description="URI path like 'nocturne' or 'nocturne/salem'"), |
| 76 | domain: str = Query("core"), |
| 77 | nav_only: bool = Query(False, description="Skip expensive processing if only navigating tree") |
| 78 | ): |
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
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 | # Stage 1: ๆๅปบ React ๅบ็จ |
| 2 | FROM node:20-alpine AS build |
| 3 | |
| 4 | WORKDIR /app |
| 5 | |
| 6 | # ๅ
ๅคๅถไพ่ตๅฃฐๆ๏ผๅฉ็จ Docker ็ผๅญๅฑ |
| 7 | COPY package*.json ./ |
| 8 | RUN npm ci |
| 9 | |
| 10 | # ๅคๅถๆบ็ ๅๆๅปบ้
็ฝฎ |
| 11 | COPY index.html ./ |
| 12 | COPY vite.config.js ./ |
| 13 | COPY tailwind.config.js ./ |
| 14 | COPY postcss.config.js ./ |
| 15 | COPY src/ ./src/ |
| 16 | |
| 17 | RUN npm run build |
| 18 | |
| 19 | # Stage 2: ็ไบง็ฏๅข nginx ๆๅก |
| 20 | FROM nginx:alpine AS production |
| 21 | |
| 22 | # ็จ่ชๅฎไน้
็ฝฎ่ฆ็้ป่ฎค้
็ฝฎ |
| 23 | RUN rm /etc/nginx/conf.d/default.conf |
| 24 | COPY nginx.conf /etc/nginx/conf.d/default.conf |
| 25 | |
| 26 | # ไปๆๅปบ้ถๆฎตๅคๅถไบง็ฉ |
| 27 | COPY --from=build /app/dist /usr/share/nginx/html |
| 28 | |
| 29 | EXPOSE 80 |
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.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 55 | steps: |
| 56 | - uses: actions/checkout@v4 |
| 57 | |
| 58 | - uses: actions/setup-python@v5 |
| 59 | with: |
| 60 | python-version: "3.12" |
| 61 | cache: pip |
Remediation
Pin every `uses:` to a 40-character commit SHA. Trailing comment with the version helps reviewers: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v4.1.1` Automate the migration with `pinact` (https://github.com/suzuki-shunsuke/pinact) or `ratchet` (https://github.com/sethvargo/ratchet). Add a `pinact run --check` pre-commit hook so future PRs stay pinned. Re-pin when the action releases a new version โ Dependabot can do this automatically with `version-update-strategy: inc
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 19 | steps: |
| 20 | - uses: actions/checkout@v4 |
| 21 | |
| 22 | - uses: actions/setup-python@v5 |
| 23 | with: |
| 24 | python-version: ${{ matrix.python-version }} |
| 25 | cache: pip |
Remediation
Pin every `uses:` to a 40-character commit SHA. Trailing comment with the version helps reviewers: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v4.1.1` Automate the migration with `pinact` (https://github.com/suzuki-shunsuke/pinact) or `ratchet` (https://github.com/sethvargo/ratchet). Add a `pinact run --check` pre-commit hook so future PRs stay pinned. Re-pin when the action releases a new version โ Dependabot can do this automatically with `version-update-strategy: inc
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 53 | --health-retries 5 |
| 54 | |
| 55 | steps: |
| 56 | - uses: actions/checkout@v4 |
| 57 | |
| 58 | - uses: actions/setup-python@v5 |
| 59 | with: |
Remediation
Pin every `uses:` to a 40-character commit SHA. Trailing comment with the version helps reviewers: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v4.1.1` Automate the migration with `pinact` (https://github.com/suzuki-shunsuke/pinact) or `ratchet` (https://github.com/sethvargo/ratchet). Add a `pinact run --check` pre-commit hook so future PRs stay pinned. Re-pin when the action releases a new version โ Dependabot can do this automatically with `version-update-strategy: inc
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 17 | python-version: ["3.10", "3.12"] |
| 18 | |
| 19 | steps: |
| 20 | - uses: actions/checkout@v4 |
| 21 | |
| 22 | - uses: actions/setup-python@v5 |
| 23 | with: |
Remediation
Pin every `uses:` to a 40-character commit SHA. Trailing comment with the version helps reviewers: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v4.1.1` Automate the migration with `pinact` (https://github.com/suzuki-shunsuke/pinact) or `ratchet` (https://github.com/sethvargo/ratchet). Add a `pinact run --check` pre-commit hook so future PRs stay pinned. Re-pin when the action releases a new version โ Dependabot can do this automatically with `version-update-strategy: inc
MCP server uses `session_id` as the sole key for session-data lookup without binding it to a user identifier. The official MCP security best practices require keys to combine user_id + session_id (e.g. `<user_id>:<session_id>`) so a guessed/obtained session_id alone cannot impersonate another user. Fix: use a compound key in DDB (`Key={"user_id": user_id, "session_id": session_id}`), or build the key as `f"{user_id}:{session_id}"` for dict/cache/redis access. Sourced `user_id` MUST come from th
Evidence
| 135 | # โโ SSE follow-up message โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ |
| 136 | # Restore namespace from the session established at connect time. |
| 137 | session_id = request.query_params.get("session_id", "") |
| 138 | ns = _sse_sessions.get(session_id, "") |
| 139 | # Fall back to per-request header/param if no session entry. |
| 140 | if not ns: |
| 141 | ns = request.headers.get("x-namespace", "") |
Remediation
Bind session IDs to user IDs in every key: # BAD ddb.get_item(Key={"session_id": session_id}) data = sessions[session_id] redis.get(session_id) # GOOD ddb.get_item(Key={"user_id": user_id, "session_id": session_id}) data = sessions[f"{user_id}:{session_id}"] redis.get(f"{user_id}:{session_id}") The user_id should come from the verified authorization token (JWT claim, validated session cookie) โ never from a request parameter the user can choose.
Server sets a CORS configuration with wildcard / reflected origin AND credentials enabled. Browsers either reject the combo (literal `*` + credentials) or accept it (reflected `req.headers.origin` + credentials), but in both cases this creates a CSRF amplifier โ any origin can issue authenticated requests to the server with the user's cookies. Fix: enumerate the trusted origins explicitly in the CORS config, or drop credentials and use `Authorization` header bearer tokens (browser-safe under wi
Evidence
| 97 | docs_url="/docs", |
| 98 | openapi_url="/openapi.json", |
| 99 | ) |
| 100 | api.add_middleware( |
| 101 | CORSMiddleware, |
| 102 | allow_origins=["*"], |
| 103 | allow_credentials=True, |
| 104 | allow_methods=["*"], |
| 105 | allow_headers=["*"], |
| 106 | ) |
Remediation
Replace wildcard / reflected origin with an explicit allowlist of trusted origins: cors({ origin: ["https://app.example.com", "https://admin.example.com"], credentials: true, }) Or, if the API genuinely should be accessible from any origin, drop credentials and use bearer tokens in the `Authorization` header instead โ that combo is browser-safe.
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
| 38 | pass |
| 39 | finally: |
| 40 | try: |
| 41 | process.stdin.close() |
| 42 | except Exception: |
| 43 | pass |
| 44 | |
| 45 | def forward_stdout(): |
| 46 | """Forward stdout from the subprocess, normalizing CRLF to LF.""" |
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
| 130 | try: |
| 131 | parsed = urlparse(db_url) |
| 132 | if parsed.password: |
| 133 | db_url = db_url.replace(f":{parsed.password}@", ":***@") |
| 134 | except Exception: |
| 135 | pass |
| 136 | raise RuntimeError( |
| 137 | f"Failed to connect to database.\n" |
| 138 | f" URL: {db_url}\n" |
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
| 187 | output_parts.append("") |
| 188 | output_parts.append("---") |
| 189 | output_parts.append("") |
| 190 | output_parts.append(recent_view) |
| 191 | except Exception: |
| 192 | pass |
| 193 | |
| 194 | return "\n".join(output_parts) |
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
| 65 | path = self._path(session_id) |
| 66 | if os.path.exists(path): |
| 67 | try: |
| 68 | os.remove(path) |
| 69 | except OSError: |
| 70 | pass |
| 71 | return val |
| 72 | |
| 73 | # Populated when the FastMCP "endpoint" SSE event is intercepted on /sse. |
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
| 354 | def _remove_changeset(self): |
| 355 | p = self._changeset_path |
| 356 | try: |
| 357 | os.remove(p) |
| 358 | except FileNotFoundError: |
| 359 | pass |
| 360 | except PermissionError: |
| 361 | os.chmod(p, stat.S_IWRITE) |
| 362 | os.remove(p) |
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
| 788 | for (name,) in result.all(): |
| 789 | try: |
| 790 | num = int(name) |
| 791 | max_num = max(max_num, num) |
| 792 | except ValueError: |
| 793 | pass |
| 794 | return max_num + 1 |
| 795 | |
| 796 | async def _would_create_cycle( |
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
| 111 | lines.append("GLOSSARY (keywords detected in this content)") |
| 112 | lines.append("") |
| 113 | lines.extend(lines_to_add) |
| 114 | lines.append("") |
| 115 | except Exception: |
| 116 | pass |
| 117 | |
| 118 | if children: |
| 119 | lines.append("=" * 60) |
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
| 33 | if not data: |
| 34 | break |
| 35 | process.stdin.write(data) |
| 36 | process.stdin.flush() |
| 37 | except Exception: |
| 38 | pass |
| 39 | finally: |
| 40 | try: |
| 41 | process.stdin.close() |
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
| 43 | def __setitem__(self, session_id: str, namespace: str) -> None: |
| 44 | try: |
| 45 | with open(self._path(session_id), "w", encoding="utf-8") as f: |
| 46 | f.write(namespace) |
| 47 | except OSError: |
| 48 | pass |
| 49 | |
| 50 | def get(self, session_id: str, default: str = "") -> str: |
| 51 | if not session_id: |
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
| 54 | if os.path.exists(path): |
| 55 | try: |
| 56 | with open(path, "r", encoding="utf-8") as f: |
| 57 | return f.read() |
| 58 | except OSError: |
| 59 | pass |
| 60 | return default |
| 61 | |
| 62 | def pop(self, session_id: str, default: str = "") -> str: |
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
| 52 | # Skip \r characters (convert CRLF to LF) |
| 53 | if data != b'\r': |
| 54 | sys.stdout.buffer.write(data) |
| 55 | sys.stdout.buffer.flush() |
| 56 | except Exception: |
| 57 | pass |
| 58 | |
| 59 | # Start threads for bidirectional communication |
| 60 | stdin_thread = threading.Thread(target=forward_stdin, daemon=True) |
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
| 152 | f = (self.dist / path.lstrip("/")).resolve() |
| 153 | if path != "/" and f.is_file() and f.is_relative_to(self.dist): |
| 154 | await FileResponse(f)(scope, receive, send) |
| 155 | return |
| 156 | except (ValueError, OSError): |
| 157 | pass |
| 158 | |
| 159 | index_file = self.dist / "index.html" |
| 160 | if index_file.is_file(): |
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.