Use with caution. Address findings before production.
Scanned 6/8/2026, 3:35:36 PMยทCached resultยทDeep Scanยท91 rulesยทHow we decide โ
AIVSS Score
Medium
Severity Breakdown
0
critical
11
high
39
medium
54
low
MCP Server Information
Findings
This package has significant security concerns with a C grade and safety score of 45/100, driven by 11 high-severity issues across prompt injection, server configuration, and resource exhaustion vulnerabilities. The 53 readiness findings suggest the package lacks proper implementation standards and safeguards, while 18 resource exhaustion risks indicate it could be exploited to consume excessive system resources. You should address the high-severity issues and readiness gaps before deploying this in production.
AIPer-finding remediation generated by bedrock-claude-haiku-4-5 โ 31 of 31 findings. Click any finding to read.
No known CVEs found for this package or its dependencies.
Scan Details
Done
Sign in to save scan history and re-scan automatically on new commits.
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 31 findings
31 findings
Tool 'search_datanexus_tools' shadows reserved generic tool 'search' โ lacks server-specific prefix and performs search semantics.
RemediationAI
The tool 'search_datanexus_tools' conflicts with the reserved generic 'search' tool name in the MCP protocol, creating ambiguity for clients. Rename the tool in mcp-manifest.json from 'search_datanexus_tools' to 'datanexus_search' or another server-specific prefix to avoid shadowing reserved semantics. This ensures clients can unambiguously route requests to the correct implementation. Verify by checking that the MCP client can call the renamed tool without protocol conflicts and that no other tools reference the old name.
Tool 'security_fetch_package_vulnerabilities' uses module-level NVD_API_KEY from os.environ (config.py line 47) to call NIST NVD API without consulting caller identity or per-request credentials.
RemediationAI
The tool 'security_fetch_package_vulnerabilities' reads NVD_API_KEY from os.environ at module level (config.py line 47) without validating caller identity or enforcing per-request credentials. Modify the function signature to accept an optional api_key parameter, and add a check in the tool handler to require the caller to provide credentials via request context or environment override. This ensures each request is authenticated and auditable to a specific caller. Test by invoking the tool with and without credentials and confirming that unauthenticated requests are rejected.
Tool 'security_fetch_dependency_graph' fetches transitive dependency data from untrusted external source (Google deps.dev) and returns it verbatim without visible provenance wrapper or source delimiter.
RemediationAI
The tool 'security_fetch_dependency_graph' returns transitive dependency data from deps.dev without any provenance wrapper, source delimiter, or attribution marker, making it impossible for the LLM to distinguish untrusted external data from authoritative sources. Wrap the returned dependency data in a structured object with a 'source' field set to 'deps.dev', a 'fetched_at' timestamp, and a 'provenance' field documenting the external origin. This allows the LLM to reason about data trustworthiness. Verify by checking that the response includes the provenance wrapper and that the source is clearly labeled.
Tool 'nonprofit_fetch_nonprofit_by_ein' returns IRS 990 data including mission statements and organizational descriptions from untrusted external sources (IRS EO BMF, IRS TEOS) without provenance delimiters or source attribution.
RemediationAI
The tool 'nonprofit_fetch_nonprofit_by_ein' returns IRS 990 data including mission statements and organizational descriptions from external sources (IRS EO BMF, IRS TEOS) without provenance delimiters or source attribution, preventing the LLM from assessing data reliability. Wrap all returned fields in a response object with 'source' (e.g., 'IRS_EO_BMF'), 'fetched_at' timestamp, and 'attribution' fields that cite the external origin. This enables transparent data lineage. Test by verifying that the response includes source attribution for each data field and that the origin is clearly visible.
Tool 'security_fetch_package_vulnerabilities' fetches CVE data from untrusted external sources (Google OSV.dev, NIST NVD) and returns severity, CVSS scores, and vulnerability descriptions verbatim to the LLM without provenance delimiters or source attribution markers.
RemediationAI
The tool 'security_fetch_package_vulnerabilities' returns CVE data from OSV.dev and NIST NVD verbatim without provenance delimiters or source attribution, making it impossible for the LLM to distinguish between authoritative and untrusted sources. Wrap the vulnerability response in a structured object with 'source' field (e.g., 'NIST_NVD' or 'OSV_DEV'), 'fetched_at' timestamp, and 'severity_source' indicating which source provided the CVSS score. This ensures data provenance is transparent. Verify by checking that each vulnerability record includes source attribution and that the LLM can identify the origin of severity scores.
Tool 'nonprofit_fetch_charity_uk' returns UK charity data including activities and trustee information from untrusted external source (Charity Commission England and Wales) without visible provenance wrapper.
RemediationAI
The tool 'nonprofit_fetch_charity_uk' returns UK charity data including activities and trustee information from the Charity Commission England and Wales without visible provenance wrapper or source attribution, obscuring data origin and reliability. Wrap the returned charity data in a response object with 'source' set to 'Charity_Commission_England_Wales', 'fetched_at' timestamp, and 'data_fields' documenting which fields came from the external source. This provides transparent attribution. Test by confirming that the response includes a provenance wrapper and that the external source is clearly labeled.
digest_generator tool calls classify() which invokes Anthropic SDK without token cap enforcement
Evidence
| 1 | """ |
| 2 | datanexus/agents/digest_generator.py โ Haiku Trigger 4: weekly digest. |
| 3 | |
| 4 | Spec: DataNexus_MCP_Spec_v7_4.docx Section 13.6 / Phase 6 |
| 5 | |
| 6 | Batches ALL feedback records for a tool into ONE Haiku call (cost control) |
| 7 | and produces a DigestItem with data quality score, top issues, suggested |
| 8 | rules, and sprint recommendations. |
| 9 | |
| 10 | Rules (CLAUDE.md): |
| 11 | S13-1: This is Haiku Trigger T4. Delegate to haiku_classifier.classify() only. |
| 12 | S13-4: Never raises. Always returns structured DigestItem. |
| 13 | |
| 14 | Special case โ emp |
RemediationAI
The digest_generator tool calls classify() which invokes the Anthropic SDK via anthropic.messages.create() without an explicit max_tokens parameter, allowing unbounded token consumption and cost overruns. Add a max_tokens parameter to the anthropic.messages.create() call in haiku_classifier.classify() with a reasonable cap (e.g., 500 tokens for Haiku), and pass this parameter from digest_generator.py. This enforces a hard token budget per request. Verify by monitoring API costs and confirming that token usage per request does not exceed the configured limit.
feedback_classifier tool calls classify() which invokes Anthropic SDK without token cap enforcement
Evidence
| 1 | """ |
| 2 | datanexus/agents/feedback_classifier.py โ Haiku Trigger 2: feedback classification. |
| 3 | |
| 4 | Spec: DataNexus_MCP_Spec_v7_4.docx Section 13.4 / Phase 4 |
| 5 | |
| 6 | Most important file in Section 13. Closes the loop between user-reported |
| 7 | feedback and automated data quality triage. |
| 8 | |
| 9 | Rules (CLAUDE.md): |
| 10 | S13-1: This is Haiku Trigger T2. Delegate to haiku_classifier.classify() only. |
| 11 | S13-4: Never raises. All exceptions caught. Always returns structured result. |
| 12 | S13-5: FeedbackRecord.classification is ONE-WAY onl |
RemediationAI
The feedback_classifier tool calls classify() which invokes the Anthropic SDK without an explicit max_tokens parameter, allowing unbounded token consumption. Add a max_tokens parameter (e.g., 500 tokens) to the anthropic.messages.create() call in haiku_classifier.classify() and ensure feedback_classifier.py passes this parameter. This prevents cost overruns and enforces predictable resource usage. Test by running the classifier and confirming that token usage stays within the configured budget.
anomaly_reviewer tool calls classify() which invokes Anthropic SDK without token cap enforcement
Evidence
| 1 | """ |
| 2 | datanexus/agents/anomaly_reviewer.py โ Haiku Trigger 1: anomaly review. |
| 3 | |
| 4 | Spec: DataNexus_MCP_Spec_v7_4.docx Section 13.3 / Phase 3 |
| 5 | |
| 6 | Called when the deterministic validator (validator.py) flags an anomaly |
| 7 | that needs a second-opinion classification before deciding whether to |
| 8 | keep, suppress, or flag the affected record. |
| 9 | |
| 10 | Rules (CLAUDE.md): |
| 11 | S13-1: This is Haiku Trigger T1. Do NOT call Anthropic API directly โ |
| 12 | always delegate to haiku_classifier.classify(). |
| 13 | S13-4: Never raises. All |
RemediationAI
The anomaly_reviewer tool calls classify() which invokes the Anthropic SDK without token cap enforcement, risking unbounded costs. Add a max_tokens parameter to anthropic.messages.create() in haiku_classifier.classify() (e.g., 500 tokens) and ensure anomaly_reviewer.py passes this parameter. This enforces a hard token limit per classification. Verify by checking API usage logs and confirming that token consumption per request does not exceed the cap.
schema_monitor tool calls classify() which invokes Anthropic SDK without token cap enforcement
Evidence
| 1 | """ |
| 2 | datanexus/agents/schema_monitor.py โ Haiku Trigger 3: schema change assessment. |
| 3 | |
| 4 | Spec: DataNexus_MCP_Spec_v7_4.docx Section 13.5 / Phase 5 |
| 5 | |
| 6 | Called when the ingest worker detects a structural change between the |
| 7 | previously-stored schema fingerprint and the newly-fetched payload shape. |
| 8 | Haiku decides whether the change is breaking and what severity to assign. |
| 9 | |
| 10 | Rules (CLAUDE.md): |
| 11 | S13-1: This is Haiku Trigger T3. Delegate to haiku_classifier.classify() only. |
| 12 | S13-4: Never raises. Always return |
RemediationAI
The schema_monitor tool calls classify() which invokes the Anthropic SDK without an explicit max_tokens parameter, allowing unbounded token usage. Add a max_tokens parameter (e.g., 500 tokens) to the anthropic.messages.create() call in haiku_classifier.classify() and ensure schema_monitor.py passes this parameter. This enforces predictable token budgets. Test by running schema monitoring and confirming that token usage per request stays within the configured limit.
Tool handlers call Anthropic SDK via haiku_classifier.classify() without explicit max_tokens parameter passed to anthropic.messages.create()
Evidence
| 1 | """ |
| 2 | datanexus/agents/haiku_classifier.py โ Shared Haiku API wrapper for all 4 triggers. |
| 3 | |
| 4 | Spec: DataNexus_MCP_Spec_v7_4.docx Section 13.2 / Phase 2 |
| 5 | |
| 6 | Rules (CLAUDE.md): |
| 7 | S13-1: Called ONLY from the 4 permitted triggers โ never directly from tool handlers. |
| 8 | S13-2: HAIKU_MODEL imported from feedback/config.py. Never hardcoded here. |
| 9 | S13-3: Daily cap HAIKU_MAX_CALLS_PER_DAY (100) enforced before every call. |
| 10 | incr โ expire โ check, in that exact order. |
| 11 | S13-4: On any API error: return err |
RemediationAI
The haiku_classifier.classify() function calls anthropic.messages.create() without an explicit max_tokens parameter, allowing unbounded token consumption across all four triggers (digest_generator, feedback_classifier, anomaly_reviewer, schema_monitor). Add a max_tokens parameter to the anthropic.messages.create() call in haiku_classifier.py (e.g., max_tokens=500) and accept it as a function parameter with a sensible default. This enforces a hard token cap across all callers. Verify by checking that API responses include token usage and that it does not exceed the configured limit.
MCP tool file registers a tool, performs a destructive sink (fs.unlink / shutil.rmtree / DROP TABLE / DELETE FROM / TRUNCATE / UPDATE ... SET / HTTP DELETE|PUT|PATCH / subprocess / exec / spawn), and emits no audit event anywhere in the file. Without an audit event, an investigator cannot answer "who deleted record X on day Y?" โ the irreversible action leaves no trail. Closes the OWASP MCP Top 10:2025 MCP08 (Lack of Audit and Telemetry) gap. Distinct from MCP-201 (no confirmation) and MCP-283
Evidence
| 1 | """ |
| 2 | datanexus/tools/security_stateful.py โ Sprint 6 stateful security tools. |
| 3 | |
| 4 | Tools: |
| 5 | fetch_cve_watch โ persistent CVE watchlist (create/check/delete) |
| 6 | audit_sbom_continuous โ continuous SBOM monitoring (register/check/deregister) |
| 7 | |
| 8 | Redis key schema (dn: prefix): |
| 9 | dn:cve_watch:{watch_id} โ Hash (watch data) |
| 10 | dn:cve_watch_ids โ SET (watch index) |
| 11 | dn:sbom_watch:{watch_id} โ Hash (SBOM watch data) |
| 12 | dn:sbom_watch_ids โ SET (SBOM watch index) |
| 13 | |
| 14 | Scheduler: see datanex |
RemediationAI
The security_stateful.py tool performs destructive operations (DELETE from watchlist, DEREGISTER from SBOM monitoring) but emits no audit event, leaving no trail for investigators to answer 'who deleted record X on day Y?'. Add an audit logging call (e.g., log_audit_event(action='delete_cve_watch', watch_id=watch_id, user=caller_id, timestamp=now())) immediately after each destructive operation (fetch_cve_watch delete, audit_sbom_continuous deregister). This creates an immutable audit trail. Test by performing a destructive operation and confirming that an audit event is logged with the action, resource ID, caller, and timestamp.
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
| 83 | # โโ Structured error response shape โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ |
| 84 | # Return this dict on ANY error. Never raise. Never return str(e). |
| 85 | # error_code must come from ErrorCode enum below. |
| 86 | # |
| 87 | # { |
RemediationAI
The schema.py error handler returns full exception details via str(e), leaking internal paths, library versions, and query structure useful for reconnaissance. Replace str(exc) with a sanitized error message from the ErrorCode enum (e.g., 'SCHEMA_VALIDATION_FAILED') and log the full traceback server-side only. This prevents information disclosure while preserving debugging capability. Verify by triggering an error and confirming that the response contains only a generic error code, not the full stack trace.
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
| 180 | "error": str(exc), |
| 181 | "haiku_available": False, |
| 182 | })) |
| 183 | return {"error": str(exc), "haiku_available": False} |
RemediationAI
The haiku_classifier.py error handler returns str(exc) to the caller, leaking internal exception details and library versions. Replace str(exc) with a generic error message (e.g., 'Classification service unavailable') and log the full exception server-side only. This prevents information disclosure. Test by triggering a classification error and confirming that the response contains only a generic message, not the full exception traceback.
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
| 53 | schedule_seconds=_SCHEDULE, |
| 54 | ) |
| 55 | |
| 56 | async def fetch(self) -> bytes: |
| 57 | """Fetch and cache EPO OPS OAuth token; probe fallback sources.""" |
| 58 | client_id = os.environ.get("EPO_CLIENT_ID", "") |
| 59 | client_secret = os.environ.get("EPO_CLIENT_SECRET", "") |
RemediationAI
The t11_worker.py fetch() method makes HTTP requests to EPO OPS without an explicit timeout parameter, allowing a hung upstream to pin threads indefinitely. Add timeout=30 (or appropriate value) to the httpx.AsyncClient() or httpx.get() call. This ensures requests fail fast if the upstream is unresponsive. Verify by testing with a slow/hung upstream and confirming that the request times out within the specified duration.
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
| 153 | schedule_seconds=_SCHEDULE, |
| 154 | ) |
| 155 | |
| 156 | async def fetch(self) -> bytes: |
| 157 | """ |
| 158 | Fetch NPPES data for top specialities. Returns a summary JSON blob |
| 159 | for the base class to hash and store. Per-speciality cache entries |
RemediationAI
The t22_worker.py fetch() method makes HTTP requests to NPPES without an explicit timeout, risking thread exhaustion if the upstream hangs. Add timeout=30 to the httpx.AsyncClient() or httpx.get() call. This prevents indefinite blocking. Test by simulating a slow upstream and confirming that the request times out within the configured duration.
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 | schedule_seconds=_SCHEDULE, |
| 65 | ) |
| 66 | |
| 67 | async def fetch(self) -> bytes: |
| 68 | """Pre-seed top keyword categories.""" |
| 69 | seeded = 0 |
| 70 | async with httpx.AsyncClient( |
RemediationAI
The t18_worker.py fetch() method creates an httpx.AsyncClient() without an explicit timeout parameter, allowing hung upstreams to block indefinitely. Add timeout=30 to the httpx.AsyncClient() constructor. This enforces a bounded wait time. Verify by testing with a slow upstream and confirming that the request times out within the specified duration.
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
| 59 | self.ttl_seconds = ttl_seconds |
| 60 | self.schedule_seconds = schedule_seconds |
| 61 | |
| 62 | async def fetch(self) -> bytes: |
| 63 | """ |
| 64 | Override in subclass. Must return raw upstream response bytes. |
| 65 | Raise any exception on failure โ run_forever will catch it. |
RemediationAI
The IngestBase.fetch() base class documentation does not enforce timeout requirements for subclass implementations, and subclasses may make network calls without timeouts. Add a timeout parameter to the IngestBase class (e.g., timeout_seconds=30) and document that all subclass fetch() implementations must use this timeout when making HTTP or subprocess calls. This ensures all ingest workers respect timeout boundaries. Test by checking that all subclass implementations pass the timeout parameter to their network calls.
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
| 83 | schedule_seconds=604800, # weekly โ data updates monthly |
| 84 | ) |
| 85 | |
| 86 | async def fetch(self) -> bytes: |
| 87 | """Download all 4 regional CSVs and index every EIN into Redis.""" |
| 88 | |
| 89 | total_rows = 0 |
RemediationAI
The t04_worker.py fetch() method downloads regional CSVs without an explicit timeout on the HTTP client, risking indefinite blocking. Add timeout=60 to the httpx.AsyncClient() constructor (or increase if needed for large downloads). This prevents thread exhaustion. Verify by testing with a slow upstream and confirming that the download times out within the configured duration.
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
| 69 | schedule_seconds=_SCHEDULE, # 21600 โ rate limit awareness |
| 70 | ) |
| 71 | |
| 72 | async def fetch(self) -> bytes: |
| 73 | """Pre-seed top regulatory keyword categories.""" |
| 74 | api_key = os.environ.get("REGULATIONS_GOV_KEY", "") |
| 75 | if not api_key: |
RemediationAI
The t19_worker.py fetch() method makes HTTP requests to Regulations.gov without an explicit timeout, allowing hung upstreams to pin threads. Add timeout=30 to the httpx.AsyncClient() or httpx.get() call. This enforces a bounded wait time. Test by simulating a slow upstream and confirming that the request times out within the specified duration.
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
| 4 | Downloads from the official CISA GitHub mirror (cisagov/kev-data) โ see KEV_URL constant. |
| 5 | and stores in Redis as: |
| 6 | datanexus:kev:catalog โ full catalog JSON (TTL 25h) |
| 7 | datanexus:kev:fetched_at โ ISO timestamp of last successful fetch (TTL 25h) |
| 8 | datanexus:kev:last_refresh_error โ error message if refresh fails (TTL 7d) |
| 9 | |
| 10 | Invoked two ways: |
RemediationAI
The kev_refresh.py module downloads from the CISA GitHub mirror without an explicit timeout on the HTTP client, risking indefinite blocking if the upstream is slow or hung. Add timeout=60 to the httpx.get() or httpx.AsyncClient() call. This prevents thread exhaustion. Verify by testing with a slow upstream and confirming that the download times out within the configured duration.
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
| 6 | Endpoints: |
| 7 | GET /ops/dashboard โ full HTML dashboard |
| 8 | GET /ops/api/metrics โ JSON metrics snapshot (same data, for fetch()) |
| 9 | GET /ops/health โ {"ok": true} |
| 10 | |
| 11 | The dashboard auto-refreshes every 15 seconds via JavaScript fetch(). |
RemediationAI
The dashboard.py endpoints make HTTP requests (e.g., via fetch() in JavaScript or backend calls) without explicit timeouts, risking unresponsive dashboard if upstreams hang. Add timeout=30 to any httpx or requests calls in the backend, and set a reasonable fetch() timeout in the JavaScript (e.g., 10 seconds). This ensures the dashboard remains responsive. Test by simulating slow upstreams and confirming that requests time out and the dashboard remains usable.
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
| 38 | class IngestBase: |
| 39 | """ |
| 40 | Base ingest worker. Subclasses override fetch() only. |
| 41 | |
| 42 | Args: |
| 43 | tool_id: Tool this worker feeds (e.g. 'T04') |
RemediationAI
The IngestBase class does not enforce timeout requirements for network or subprocess calls made by subclasses, allowing hung upstreams to block indefinitely. Add a timeout_seconds parameter to IngestBase.__init__() (e.g., timeout_seconds=30) and document that all subclass fetch() implementations must pass this timeout to httpx.AsyncClient(), httpx.get(), subprocess calls, etc. This ensures all ingest workers respect timeout boundaries. Verify by checking that all subclass implementations use the timeout parameter in their network calls.
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
| 52 | schedule_seconds=_SCHEDULE, |
| 53 | ) |
| 54 | |
| 55 | async def fetch(self) -> bytes: |
| 56 | """Fetch IANA RDAP bootstrap and store in Redis.""" |
| 57 | async with httpx.AsyncClient( |
| 58 | timeout=_TIMEOUT, headers=_HEADERS, follow_redirects=True |
RemediationAI
The t07_worker.py fetch() method already includes timeout=_TIMEOUT in the httpx.AsyncClient() call, which is correct. Ensure that _TIMEOUT is defined with a reasonable value (e.g., 30 seconds) and that all other network calls in the file also use this timeout. Verify by checking that _TIMEOUT is set to a non-None value and that all HTTP calls use it.
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
| 106 | schedule_seconds=3600, |
| 107 | ) |
| 108 | |
| 109 | async def fetch(self) -> bytes: |
| 110 | """Pre-seed all 50 packages. Returns last raw response bytes.""" |
| 111 | seeded = 0 |
| 112 | last_raw = b"" |
RemediationAI
The t10_worker.py fetch() method pre-seeds packages without an explicit timeout on HTTP calls, risking indefinite blocking. Add timeout=30 to the httpx.AsyncClient() or httpx.get() call. This prevents thread exhaustion. Test by simulating a slow upstream and confirming that the request times out within the configured duration.
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
| 344 | schedule_seconds=86400, |
| 345 | ) |
| 346 | |
| 347 | async def fetch(self) -> bytes: |
| 348 | """Download bulk extract and index all main charities into Redis.""" |
| 349 | log.info(json.dumps({ |
| 350 | "ts": _iso_now(), "event": "uk_bulk_download_start", "tool": self.tool_id, |
RemediationAI
The t04_worker.py fetch() method downloads bulk charity data without an explicit timeout on the HTTP client, risking indefinite blocking if the upstream is slow. Add timeout=60 to the httpx.AsyncClient() or httpx.get() call (increase if needed for large downloads). This prevents thread exhaustion. Verify by testing with a slow upstream and confirming that the download times out within the configured duration.
datanexus-mcp.js is a stdio-to-remote transport proxy that forwards MCP JSON-RPC to https://datanexusmcp.com/mcp. The remote hosted server returns tool results and manifest data over MCP protocol; this is standard remote MCP deployment, not per-invocation steering instruction injection.
Evidence
| 1 | #!/usr/bin/env node |
| 2 | /** |
| 3 | * DataNexus MCP โ stdio proxy for Glama and local MCP clients. |
| 4 | * |
| 5 | * Bridges stdio โ the live remote server at https://datanexusmcp.com/mcp |
| 6 | * using mcp-remote. No Python, no Redis, no PostgreSQL required. |
| 7 | * |
| 8 | * Usage: |
| 9 | * npx -y @datanexusmcp/mcp-server |
| 10 | * |
| 11 | * Claude Desktop claude_desktop_config.json: |
| 12 | * { |
| 13 | * "mcpServers": { |
| 14 | * "datanexus": { |
| 15 | * "command": "npx", |
| 16 | * "args": ["-y", "@datanexusmcp/mcp-server"] |
| 17 | * } |
| 18 | * } |
| 19 | * } |
| 20 | */ |
| 21 | |
| 22 | c |
RemediationAI
The datanexus-mcp.js file is a standard remote MCP transport proxy that forwards JSON-RPC to a remote server, which is a legitimate deployment pattern. No remediation is required; this is not a vulnerability. Confirm that the remote server URL (https://datanexusmcp.com/mcp) is hardcoded and not user-controllable, and that the proxy does not accept steering instructions in the JSON-RPC payload.
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
| 223 | try: |
| 224 | r = await get_redis() |
| 225 | if r: |
| 226 | await r.set(cache_key, tier, ex=300) |
| 227 | except Exception: |
| 228 | pass |
| 229 | return tier |
| 230 | |
| 231 | except Exception as exc: |
RemediationAI
The main.py error handler swallows exceptions with bare pass, discarding errors with no log or metric, blinding incident response. Replace the except Exception: pass block with log.warning(f'Cache set failed: {exc}') to record the failure. This enables troubleshooting and incident response. Test by triggering a Redis error and confirming that a warning is logged.
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
| 99 | # remaining allowlist domains โ any path counts |
| 100 | for allowed in _PATCH_ALLOWLIST: |
| 101 | if host == allowed or host.endswith("." + allowed): |
| 102 | return True |
| 103 | except Exception: |
| 104 | pass |
| 105 | return False |
RemediationAI
The cve_sprint7.py allowlist check swallows exceptions with bare pass, hiding real failures in domain parsing. Replace except Exception: pass with log.warning(f'Allowlist check failed: {exc}') to record the error. This enables debugging of allowlist logic. Test by triggering a parsing error and confirming that a warning is logged.
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
| 1184 | raw = r.hget(key_feedback(tool_id, rid), "data") |
| 1185 | if raw: |
| 1186 | try: |
| 1187 | records.append(json.loads(raw)) |
| 1188 | except Exception: |
| 1189 | pass |
| 1190 | except Exception: |
| 1191 | pass |
| 1192 | results[tool_id] = records |
RemediationAI
The feedback/dashboard/server.py error handler swallows exceptions with bare pass when parsing feedback records, hiding data corruption or Redis errors. Replace except Exception: pass with log.warning(f'Failed to parse feedback record {rid}: {exc}') to record the failure. This enables detection of data quality issues. Test by introducing a malformed record and confirming that a warning is logged.
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
| 59 | try: |
| 60 | r = _get_redis() |
| 61 | if r: |
| 62 | r.set(LAST_ERROR_KEY, msg[:500], ex=7 * 86400) |
| 63 | except Exception: |
| 64 | pass |
| 65 | |
| 66 | |
| 67 | async def refresh() -> bool: |
RemediationAI
The kev_refresh.py error handler swallows exceptions with bare pass when writing to Redis, hiding failures to persist error messages. Replace except Exception: pass with log.warning(f'Failed to log refresh error: {exc}') to record the failure. This enables troubleshooting of the refresh process. Test by simulating a Redis error and confirming that a warning is logged.