# Security Audit Report — consult7

- **Report ID:** `bf936f57-62fe-4773-a18a-3b04a0e3c878`
- **Generated:** 2026-07-22T02:19:56.441172+00:00
- **Signature:** unsigned (cosign keyless signing runs in CI; Req 22.4)

## 1. Executive Summary

**Badge:** Unsafe (composite)  
**Security score:** 45.05

| Scanner | Badge |
| --- | --- |
| agent-audit-kit | Unsafe |
| agentshield | unavailable |
| bearer | Verified |
| cisco-skill-scanner | Verified |
| nerlo-behavioral | Verified |
| nerlo-install-instruction | Verified |
| osv-scanner | unavailable |
| trivy | Unsafe |

| Severity | Findings |
| --- | --- |
| critical | 0 |
| high | 15 |
| medium | 13 |
| low | 8 |

consult7 is NOT recommended for integration: the scan surfaced 0 critical and 15 high-severity findings. Treat the Per-Scanner Detail section as a remediation worklist and re-scan before reconsidering.

## 2. Source Provenance

- **Repository:** https://github.com/szeider/consult7
- **Commit scanned:** `unknown`
- **License:** MIT
- **Maintainer:** Stefan Szeider
- **Version:** 3.9.0

## 3. Per-Scanner Detail

### agentshield (v1.4.0) — unavailable / n/a

No findings.

### cisco-skill-scanner (v2.0.11) — Verified / 92.5

- **[medium] Undeclared network usage** — Skill code uses network libraries but doesn't declare network requirement (/repo/repo/CLAUDE-archive.md:None)
- **[low] Vague skill description** — Skill description is too short (16 chars). Provide detailed explanation. (/repo/SKILL.md:None)
- **[informational] Skill does not specify a license** — Skill manifest does not include a 'license' field. Specifying a license helps users understand usage terms. (/repo/SKILL.md:None)

### agent-audit-kit (v0.3.26) — Unsafe / 58.0

- **[low] MCP server repo missing SECURITY.md or security_contact** — A repository whose name or pyproject keywords declare it as an MCP server ships without a top-level SECURITY.md AND without a 'security_contact' entry in marketplace.json / pyproject.toml / package.json. Anthropic's April 2026 SECURITY.md guidance makes this the baseline expectation so researchers have a channel. (SECURITY.md:None)
- **[medium] MCP tool logs caller-controlled input without CRLF/ANSI sanitization** — A '@tool'-decorated function parameter flows into logger.info / print / sys.stdout.write / console.log without stripping control characters (\r, \n, \x1b) first. CVE-2026-6494 (AAP MCP, CVSS 5.3 MEDIUM, CWE-117) lets an attacker forge log entries and inject ANSI escape sequences to socially engineer an operator. (src/consult7/server.py:172)
- **[medium] Repo depends on a third-party agent-platform SDK** — The project depends on an agent-platform SDK (context-ai, langsmith, helicone, langfuse, humanloop, MCP SDK). Informational finding so reviewers audit the vendor's OAuth-scope footprint before merging. Raised to MEDIUM because the April 19 2026 Vercel × Context.ai incident showed a single vendor compromise can turn into a production breach via transitive OAuth grants. (pyproject.toml:15)
- **[high] MCP server built on the upstream SDK without STDIO sanitizer** — Repository declares a dependency on the upstream Anthropic / ModelContextProtocol SDK (Python 'mcp' / 'modelcontextprotocol', TS '@modelcontextprotocol/sdk', Java 'io.modelcontextprotocol:*', Rust 'mcp' / 'modelcontextprotocol') and exposes a STDIO transport ('StdioServerTransport', 'stdio_server', etc.) without a sanitizer on argv assembly. Anthropic declined to CVE this as working as designed — sanitization is the developer's responsibility. The OX Security disclosure on 2026-04-15 rolled up L (src/consult7/server.py:None)
- **[high] Session token written to log sink in cleartext** — An MCP server, agent, or tool logs a session token, JWT, or Bearer credential through a generic log sink (logger.info / .warn / .error, print) without redaction. CVE-2026-20205 (splunk-mcp-server < 1.0.3) shipped this exact pattern — session tokens ended up in the Splunk '_internal' index, readable by anyone with index-read. Any token written to a log sink is also a supply-chain risk: the log file, shipper, and SIEM are now in scope for the token's blast radius. (src/consult7/server.py:37)

### bearer (v2.0.2) — Verified / 100.0

No findings.

### nerlo-behavioral (v0.1.0) — Verified / 100.0

No findings.

### nerlo-install-instruction (v0.1.0) — Verified / 100.0

No findings.

### trivy (v0.71.0) — Unsafe / 0.0

- **[high] cryptography: cryptography Subgroup Attack Due to Missing Subgroup Validation for SECT Curves** — cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. Prior to 46.0.5, the public_key_from_numbers (or EllipticCurvePublicNumbers.public_key()), EllipticCurvePublicNumbers.public_key(), load_der_public_key() and load_pem_public_key() functions do not verify that the point belongs to the expected prime-order subgroup of the curve. This missing validation allows an attacker to provide a public key point P from a small-order subgroup. This can lead  (uv.lock:None)
- **[high] Vulnerable OpenSSL included in cryptography wheels** — pyca/cryptography's wheels include a statically linked copy of OpenSSL. The versions of OpenSSL included in wheels prior to cryptograph 48.01 are vulnerable to a security issue. More details about the vulnerability itself can be found in https://openssl-library.org/news/secadv/20260609.txt.  If you are building cryptography source ("sdist") then you are responsible for upgrading your copy of OpenSSL. Only users installing from wheels built by the cryptography project (i.e., those distributed on  (uv.lock:None)
- **[medium] cryptography: Cryptography: Buffer overflow via non-contiguous buffer in API** — cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. From 45.0.0 to before 46.0.7, if a non-contiguous buffer was passed to APIs which accepted Python buffers (e.g. Hash.update()), this could lead to buffer overflows. This vulnerability is fixed in 46.0.7. (uv.lock:None)
- **[low] python-cryptography: Cryptography: Security bypass due to improper DNS name constraint validation** — cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. Prior to version 46.0.6, DNS name constraints were only validated against SANs within child certificates, and not the "peer name" presented during each validation. Consequently, cryptography would allow a peer named bar.example.com to validate against a wildcard leaf certificate for *.example.com, even if the leaf's parent certificate (or upwards) contained an excluded subtree constraint for b (uv.lock:None)
- **[medium] python-idna: idna: Denial of Service via specially crafted long inputs** — Internationalized Domain Names in Applications (IDNA) for Python provides support for Internationalized Domain Names in Applications (IDNA) and Unicode IDNA Compatibility Processing. In versions prior to 3.15, payloads such as '"\u0660" * N' or '"\u30fb" * N + "\u6f22"' utilize the 'valid_contexto' function prior to length rejection, and for high values of 'N' will take a long time to process. This is the same issue as CVE-2024-3651, however the original remediation in 2024 was not a complete fi (uv.lock:None)
- **[high] MCP Python SDK: HTTP transports serve session requests without verifying the authenticated principal** — The MCP Python SDK, called mcp on PyPI, is a Python implementation of the Model Context Protocol (MCP). Prior to 1.27.2, the SSE and stateful Streamable HTTP transports mcp.server.sse.SseServerTransport and mcp.server.streamable_http_manager.StreamableHTTPSessionManager route requests to existing sessions using only the session_id query parameter or Mcp-Session-Id header without verifying the authenticated principal that created the session, allowing a different bearer-token-authenticated client (uv.lock:None)
- **[high] MCP Python SDK: Experimental task handlers allow any client to access and cancel other clients' tasks** — The MCP Python SDK, called mcp on PyPI, is a Python implementation of the Model Context Protocol (MCP). From 1.23.0 until 1.27.2, default handlers installed by server.experimental.enable_tasks() for tasks/list, tasks/get, tasks/result, and tasks/cancel operate only on task identifiers without recording the session that created each task, allowing any connected client to enumerate, read results from, consume messages for, or cancel other clients' tasks. This issue is fixed in version 1.27.2. (uv.lock:None)
- **[high] MCP Python SDK: WebSocket server transport does not support Host/Origin validation** — The MCP Python SDK, called mcp on PyPI, is a Python implementation of the Model Context Protocol (MCP). Prior to 1.28.1, the deprecated mcp.server.websocket.websocket_server transport accepted WebSocket handshakes without applying Host or Origin header validation, leaving no SDK-level way to restrict which origins could connect to applications that exposed that transport. This issue is fixed in version 1.28.1. (uv.lock:None)
- **[high] pyjwt: PyJWT accepts unknown 'crit' header extensions (RFC 7515 §4.1.11 MUST violation)** — PyJWT is a JSON Web Token implementation in Python. Prior to 2.12.0, PyJWT does not validate the crit (Critical) Header Parameter defined in RFC 7515 §4.1.11. When a JWS token contains a crit array listing extensions that PyJWT does not understand, the library accepts the token instead of rejecting it. This violates the MUST requirement in the RFC. This vulnerability is fixed in 2.12.0. (uv.lock:None)
- **[high] python-pyjwt: PyJWT: Authentication bypass due to forged JSON Web Tokens** — PyJWT is a JSON Web Token implementation in Python. Prior to 2.13.0, when the verifier is decoding JSON Web Tokens, while supporting both asymmetric and HMAC algorithms, the library does not validate use of JSON Web Keys in HMAC algorithm, allowing attacker to use the issuer public key as the secret key for HMAC algorithm. This vulnerability is fixed in 2.13.0. (uv.lock:None)
- **[medium] python-pyjwt: PyJWT: Server-Side Request Forgery (SSRF) via uncontrolled URL fetching in PyJWKClient** — PyJWT is a JSON Web Token implementation in Python. Prior to 2.13.0, PyJWKClient passes its uri argument directly to urllib.request.urlopen() which uses Python stdlib's default OpenerDirector registering HTTPHandler, HTTPSHandler, FTPHandler, FileHandler, and DataHandler. There is currently no documented option to restrict which schemes PyJWKClient will fetch. If an application's jku URL ingestion path accepts attacker-influenced URLs (e.g., from JWT header, configuration file, OAuth flow parame (uv.lock:None)
- **[medium] python-pyjwt: PyJWT: Verifier-side algorithm bypass leads to unauthorized information access** — PyJWT is a JSON Web Token implementation in Python. From 2.9.0 to 2.12.1, there is a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, adv (uv.lock:None)
- **[medium] python-pyjwt: PyJWT: Denial of Service via processing of crafted detached JWS tokens** — PyJWT is a JSON Web Token implementation in Python. From 2.8.0 to 2.12.1, when verifying detached JWS tokens using the unencoded-payload option ("b64": false, RFC 7797), PyJWT performs Base64URL decoding of the compact-serialization payload segment before enforcing the detached-payload rules. For b64=false, PyJWT later discards that decoded payload and replaces it with the caller-provided detached_payload. In practice, this turns the middle segment into an attacker-controlled “work amplifier”: a (uv.lock:None)
- **[low] python-pyjwt: PyJWT: Denial of Service via unverified JSON Web Token key IDs** — PyJWT is a JSON Web Token implementation in Python. Prior to 2.13.0, PyJWKClient.get_signing_key() forces a fresh HTTP request to the JWKS endpoint for every JWT with an unknown kid value, with no rate limiting. Since kid comes from the unverified token header, an attacker can trigger unlimited outbound requests. The vulnerability surfaces only when a JWKS fetch fails; an attacker can attempt to provoke that with sustained unknown-kid traffic, but the outcome depends on upstream JWKS-endpoint be (uv.lock:None)
- **[medium] python-dotenv: python-dotenv: Arbitrary file overwrite via symbolic link following** — python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, 'set_key()' and 'unset_key()' in python-dotenv follow symbolic links when rewriting '.env' files, allowing a local attacker to overwrite arbitrary files via a crafted symlink when a cross-device rename fallback is triggered. Users should upgrade to v.1.2.2 or, as a workaround, apply the patch manually. (uv.lock:None)
- **[high] python-multipart: Python-Multipart: Arbitrary file write via path traversal vulnerability** — Python-Multipart is a streaming multipart parser for Python. Prior to version 0.0.22, a Path Traversal vulnerability exists when using non-default configuration options 'UPLOAD_DIR' and 'UPLOAD_KEEP_FILENAME=True'. An attacker can write uploaded files to arbitrary locations on the filesystem by crafting a malicious filename. Users should upgrade to version 0.0.22 to receive a patch or, as a workaround, avoid using 'UPLOAD_KEEP_FILENAME=True' in project configurations. (uv.lock:None)
- **[high] python-multipart: python-multipart: Denial of Service via excessive multipart part headers** — Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.27, python-multipart has a denial of service vulnerability in multipart part header parsing. When parsing multipart/form-data, MultipartParser previously had no limit on the number of part headers or the size of an individual part header. An attacker could send a request with either many repeated headers without terminating the header block or a single very large header value, causing excessive CPU work before request reje (uv.lock:None)
- **[high] python-multipart: Python-Multipart: Denial of Service via crafted form-urlencoded bodies** — Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.30, when parsing application/x-www-form-urlencoded bodies, QuerystringParser located the field separator with a two step lookup: it first scanned the entire remaining buffer for &, and only when no & existed anywhere ahead did it fall back to scanning for ;. For a body that uses ; as the separator and contains no &, every field iteration performed a full failed & scan over the entire remaining buffer before locating the ne (uv.lock:None)
- **[medium] python-multipart: Python-Multipart: Denial of Service via crafted multipart/form-data requests** — Python-Multipart is a streaming multipart parser for Python. Versions prior to 0.0.26 have a denial of service vulnerability when parsing crafted 'multipart/form-data' requests with large preamble or epilogue sections. Upgrade to version 0.0.26 or later, which skips ahead to the next boundary candidate when processing leading CR/LF data and immediately discards epilogue data after the closing boundary. (uv.lock:None)
- **[low] multipart: Python-Multipart: Information disclosure via header parsing discrepancy** — Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.30, parse_options_header parsed Content-Disposition (and Content-Type) headers with email.message.Message, which transparently applies RFC 2231/5987 decoding. The extended parameter syntax (filename*=charset'lang'value, name*=..., and the filename*0/filename*1 continuation form) is decoded and surfaced under the bare filename/name key, and overrides the plain parameter when both are present. RFC 7578 §4.2 explicitly forbid (uv.lock:None)
- **[low] python-multipart: Python-Multipart: Information disclosure due to parser differential in form data handling** — Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.30, QuerystringParser treated ; as a field separator in application/x-www-form-urlencoded bodies, in addition to &. The WHATWG URL standard, modern browsers, and Python's urllib.parse (since the CVE-2021-23336 fix) treat only & as a separator. This creates a parser differential: the same bytes are tokenized into different fields than a WHATWG compliant intermediary would produce, allowing an attacker to smuggle extra form  (uv.lock:None)
- **[low] python-multipart: Python-Multipart: Negative Content-Length in parse_form buffers the entire body in memory** — Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.31, parse_form() did not validate the Content-Length header before using it to bound its chunked read of the request body. A negative Content-Length turned the bounded read into a read-until-EOF, so the entire body was loaded into memory in a single read instead of in fixed-size chunks. This vulnerability is fixed in 0.0.31. (uv.lock:None)
- **[high] starlette: Starlette DoS via Range header merging** — Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1. (uv.lock:None)
- **[high] starlette: Starlette: SSRF and NTLM credential theft via UNC paths in StaticFiles on Windows** — Starlette is a lightweight ASGI framework/toolkit. In versions 1.0.1 and earlier, StaticFiles on Windows is vulnerable to SSRF. An UNC path such as \\attacker.com\share can cause os.path.realpath to initiate an outbound SMB connection before the path is rejected, exposing the service account’s NTLMv2 credentials for offline cracking or relay even though the HTTP response is only a 404. The issue affects default follow_symlink=False deployments, including frameworks built on Starlette such as Fas (uv.lock:None)
- **[high] starlette: Starlette: request.form() limits silently ignored for application/x-www-form-urlencoded enable DoS** — Starlette is a lightweight ASGI framework/toolkit. From 0.4.1 until 1.3.1, request.form() accepts max_fields and max_part_size to bound resource consumption while parsing form data. These limits are enforced for multipart/form-data, but silently ignored for application/x-www-form-urlencoded. An unauthenticated attacker can therefore send a urlencoded body with an arbitrarily large number of fields or an arbitrarily large field, even when the application configured limits it believed would apply. (uv.lock:None)
- **[medium] starlette: Starlette denial-of-service** — Starlette is a lightweight ASGI (Asynchronous Server Gateway Interface) framework/toolkit, designed for building async web services in Python. In versions 0.47.1 and below, when parsing a multi-part form with large files (greater than the default max spool size) starlette will block the main thread to roll the file over to disk. This blocks the event thread which means the application can't accept new connections. The UploadFile code has a minor bug where instead of just checking for self._in_me (uv.lock:None)
- **[medium] starlette: Starlette: Security restriction bypass via malformed HTTP Host header** — Starlette is a lightweight ASGI framework/toolkit. Prior to version 1.0.1, the HTTP 'Host' request header was not validated before being used to reconstruct 'request.url'. Because the routing algorithm relies on the raw HTTP path while 'request.url' is rebuilt from the 'Host' header, a malformed header could make 'request.url.path' differ from the path that was actually requested. Middleware and endpoints that apply security restrictions based on 'request.url' (rather than the raw 'scope' path)  (uv.lock:None)
- **[medium] starlette: Starlette: Information disclosure and unintended method execution via non-standard HTTP methods** — Starlette is a lightweight ASGI framework/toolkit. In versions 1.0.1 and below, when dispatching a request, HTTPEndpoint selects the handler by lowercasing the HTTP method and looking it up as an attribute with getattr, without restricting the lookup to a known set of HTTP verbs. When an HTTPEndpoint subclass is registered through Route(...) without an explicit methods= argument, the route does not constrain the method and every method reaches the endpoint. If a non-standard HTTP method whose lo (uv.lock:None)
- **[low] starlette: Starlette: Information disclosure due to improper HTTP request path validation** — Starlette is a lightweight ASGI framework/toolkit. Prior to 1.3.0, the HTTP request path is not validated before being used to reconstruct request.url. Because request.url is rebuilt by concatenating {scheme}://{host}{path} and re-parsing the result, a path that does not begin with / (for example @google.com) moves the authority boundary during re-parsing, so request.url.hostname and request.url.netloc become attacker-controlled. Code that reads request.url.hostname (rather than the Host header  (uv.lock:None)

### osv-scanner (v2.3.8) — unavailable / n/a

No findings.

## 4. Threat Model

Threat model synthesis has not yet run for this scan. This section is generated by the registry's LLM pipeline (Req 22.3) and will appear in the next regeneration of this report.

## 5. Audit Chain

- **Scan job:** `c6d527f6-d9a0-4c76-9038-5cd70e3bb0c2`
- **Completed:** 2026-07-21T01:31:04.609589+00:00
- **Scanner base image:** `us-central1-docker.pkg.dev/nerlo-vsk-prod/nerlo/scanner-base@sha256:d40988d18b88a65a7f0868915e1c7b2a3c6cdf062b2f3bdac6e9d0920f9670e1`
- **AI decision log entries:** 1
  - `131e6af3-d623-4648-aa0b-249c18b7f322`

## 6. Appendix — Raw Scanner Output

```json
[
  {
    "scanner_name": "agentshield",
    "scanner_version": "1.4.0",
    "score": 100.0,
    "scanner_badge": "Verified",
    "findings": [],
    "execution_duration_seconds": 1.046703643994988,
    "status": "not_applicable",
    "metadata": {
      "source": "npm",
      "source_url": "https://www.npmjs.com/package/ecc-agentshield",
      "install_command": "npm install -g ecc-agentshield@1.4.0",
      "scans_performed": []
    },
    "display_score": null,
    "display_badge": "unavailable"
  },
  {
    "scanner_name": "cisco-skill-scanner",
    "scanner_version": "2.0.11",
    "score": 92.5,
    "scanner_badge": "Verified",
    "findings": [
      {
        "tool_name": "cisco-skill-scanner",
        "severity": "medium",
        "category": "unauthorized_tool_use",
        "file_path": "/repo/repo/CLAUDE-archive.md",
        "line_number": null,
        "rule_identifier": "TOOL_ABUSE_UNDECLARED_NETWORK",
        "title": "Undeclared network usage",
        "description": "Skill code uses network libraries but doesn't declare network requirement",
        "remediation": "Declare network usage in compatibility field or remove network calls"
      },
      {
        "tool_name": "cisco-skill-scanner",
        "severity": "low",
        "category": "social_engineering",
        "file_path": "/repo/SKILL.md",
        "line_number": null,
        "rule_identifier": "SOCIAL_ENG_VAGUE_DESCRIPTION",
        "title": "Vague skill description",
        "description": "Skill description is too short (16 chars). Provide detailed explanation.",
        "remediation": "Provide a clear, detailed description of what the skill does and when to use it"
      },
      {
        "tool_name": "cisco-skill-scanner",
        "severity": "informational",
        "category": "policy_violation",
        "file_path": "/repo/SKILL.md",
        "line_number": null,
        "rule_identifier": "MANIFEST_MISSING_LICENSE",
        "title": "Skill does not specify a license",
        "description": "Skill manifest does not include a 'license' field. Specifying a license helps users understand usage terms.",
        "remediation": "Add 'license' field to SKILL.md frontmatter (e.g., MIT, Apache-2.0)"
      }
    ],
    "execution_duration_seconds": 44.26566707699385,
    "status": "complete",
    "metadata": {
      "source": "pypi",
      "source_url": "https://pypi.org/project/cisco-ai-skill-scanner/2.0.11/",
      "report_type": "cisco-skill-sast",
      "analyzers_used": [
        "bytecode",
        "pipeline",
        "static_analyzer"
      ],
      "skills_scanned": [
        "repo"
      ],
      "install_command": "pip install --require-hashes -r docker/scanner-base/cisco-skill-scanner/requirements.txt",
      "severity_counts": {
        "low": 1,
        "high": 0,
        "medium": 1,
        "critical": 0,
        "informational": 1
      }
    },
    "display_score": 92.5,
    "display_badge": "Verified"
  },
  {
    "scanner_name": "agent-audit-kit",
    "scanner_version": "0.3.26",
    "score": 58.0,
    "scanner_badge": "Unsafe",
    "findings": [
      {
        "tool_name": "agent-audit-kit",
        "severity": "low",
        "category": "supply-chain",
        "file_path": "SECURITY.md",
        "line_number": null,
        "rule_identifier": "AAK-SEC-MD-001",
        "title": "MCP server repo missing SECURITY.md or security_contact",
        "description": "A repository whose name or pyproject keywords declare it as an MCP server ships without a top-level SECURITY.md AND without a `security_contact` entry in marketplace.json / pyproject.toml / package.json. Anthropic's April 2026 SECURITY.md guidance makes this the baseline expectation so researchers have a channel.",
        "remediation": "Add SECURITY.md at the repo root with a disclosure email and response SLA; OR add `security_contact` to the project manifest."
      },
      {
        "tool_name": "agent-audit-kit",
        "severity": "medium",
        "category": "taint-analysis",
        "file_path": "src/consult7/server.py",
        "line_number": 172,
        "rule_identifier": "AAK-LOGINJ-001",
        "title": "MCP tool logs caller-controlled input without CRLF/ANSI sanitization",
        "description": "A `@tool`-decorated function parameter flows into logger.info / print / sys.stdout.write / console.log without stripping control characters (\\r, \\n, \\x1b) first. CVE-2026-6494 (AAP MCP, CVSS 5.3 MEDIUM, CWE-117) lets an attacker forge log entries and inject ANSI escape sequences to socially engineer an operator.",
        "remediation": "Strip \\r\\n\\x1b (or accept only printable ASCII) before logging anything derived from tool input. Prefer structured logging (JSON/logfmt) so log consumers aren't confused by forged lines."
      },
      {
        "tool_name": "agent-audit-kit",
        "severity": "medium",
        "category": "supply-chain",
        "file_path": "pyproject.toml",
        "line_number": 15,
        "rule_identifier": "AAK-OAUTH-3P-001",
        "title": "Repo depends on a third-party agent-platform SDK",
        "description": "The project depends on an agent-platform SDK (context-ai, langsmith, helicone, langfuse, humanloop, MCP SDK). Informational finding so reviewers audit the vendor's OAuth-scope footprint before merging. Raised to MEDIUM because the April 19 2026 Vercel \u00d7 Context.ai incident showed a single vendor compromise can turn into a production breach via transitive OAuth grants.",
        "remediation": "Pin the SDK to an exact version, audit the OAuth scopes it requests, and keep any deployment-level grants (Vercel, GCP, Workspace) in a secrets vault \u2014 never in a committed env file. See Vercel's bulletin for sensitive-env-var guidance: https://vercel.com/kb/bulletin/vercel-april-2026-security-incident"
      },
      {
        "tool_name": "agent-audit-kit",
        "severity": "high",
        "category": "supply-chain",
        "file_path": "src/consult7/server.py",
        "line_number": null,
        "rule_identifier": "AAK-ANTHROPIC-SDK-001",
        "title": "MCP server built on the upstream SDK without STDIO sanitizer",
        "description": "Repository declares a dependency on the upstream Anthropic / ModelContextProtocol SDK (Python `mcp` / `modelcontextprotocol`, TS `@modelcontextprotocol/sdk`, Java `io.modelcontextprotocol:*`, Rust `mcp` / `modelcontextprotocol`) and exposes a STDIO transport (`StdioServerTransport`, `stdio_server`, etc.) without a sanitizer on argv assembly. Anthropic declined to CVE this as working as designed \u2014 sanitization is the developer's responsibility. The OX Security disclosure on 2026-04-15 rolled up L",
        "remediation": "Wrap every argv the STDIO transport builds in an allow-list sanitizer \u2014 `shlex.quote` in Python, `execFile` with an explicit argv array in Node, equivalent in Java/Rust. OR switch the transport off STDIO (`transports=['http']` / `['sse']`). If you have deliberately accepted the risk, add `accepts_stdio_risk: true` plus a `justification:` field in `.agent-audit-kit.yml`."
      },
      {
        "tool_name": "agent-audit-kit",
        "severity": "high",
        "category": "secret-exposure",
        "file_path": "src/consult7/server.py",
        "line_number": 37,
        "rule_identifier": "AAK-SPLUNK-TOKLOG-001",
        "title": "Session token written to log sink in cleartext",
        "description": "An MCP server, agent, or tool logs a session token, JWT, or Bearer credential through a generic log sink (logger.info / .warn / .error, print) without redaction. CVE-2026-20205 (splunk-mcp-server < 1.0.3) shipped this exact pattern \u2014 session tokens ended up in the Splunk `_internal` index, readable by anyone with index-read. Any token written to a log sink is also a supply-chain risk: the log file, shipper, and SIEM are now in scope for the token's blast radius.",
        "remediation": "Redact token-shaped values before logging. Never interpolate a raw `Authorization`, `Bearer`, JWT, `splunkd_session`, or `st-` credential into a log message. Pin `splunk-mcp-server >= 1.0.3`."
      }
    ],
    "execution_duration_seconds": 16.2359174949961,
    "status": "complete",
    "metadata": {
      "source": "pypi",
      "source_url": "https://pypi.org/project/agent-audit-kit/0.3.26/",
      "report_type": "agent-audit-kit-sast",
      "files_scanned": 17,
      "install_command": "pip install --require-hashes -r docker/scanner-base/agent-audit-kit/requirements.txt",
      "rules_evaluated": 211,
      "severity_counts": {
        "low": 1,
        "high": 2,
        "medium": 2,
        "critical": 0,
        "informational": 0
      }
    },
    "display_score": 58.0,
    "display_badge": "Unsafe"
  },
  {
    "scanner_name": "bearer",
    "scanner_version": "2.0.2",
    "score": 100.0,
    "scanner_badge": "Verified",
    "findings": [],
    "execution_duration_seconds": 15.921211291002692,
    "status": "complete",
    "metadata": {
      "source": "github-releases",
      "source_url": "https://github.com/Bearer/bearer",
      "report_type": "security",
      "rules_loaded": 1,
      "install_command": "curl -sfL https://raw.githubusercontent.com/Bearer/bearer/main/contrib/install.sh | sh -s -- -b /usr/local/bin \"v2.0.2\""
    },
    "display_score": 100.0,
    "display_badge": "Verified"
  },
  {
    "scanner_name": "nerlo-behavioral",
    "scanner_version": "0.1.0",
    "score": 100.0,
    "scanner_badge": "Verified",
    "findings": [],
    "execution_duration_seconds": 53.88212680599827,
    "status": "complete",
    "metadata": {
      "source": "nerlo-original",
      "source_url": "https://github.com/nerlo-ai/nerlo",
      "report_type": "nerlo-behavioral",
      "ruleset_path": "/opt/nerlo-rules/exfiltration.yaml",
      "files_scanned": 11,
      "install_command": "pip install 'semgrep==1.97.0'"
    },
    "display_score": 100.0,
    "display_badge": "Verified"
  },
  {
    "scanner_name": "nerlo-install-instruction",
    "scanner_version": "0.1.0",
    "score": 100.0,
    "scanner_badge": "Verified",
    "findings": [],
    "execution_duration_seconds": 53.76218281799811,
    "status": "complete",
    "metadata": {
      "source": "nerlo-original",
      "source_url": "https://github.com/nerlo-ai/nerlo",
      "report_type": "nerlo-install-instruction",
      "ruleset_path": "/opt/nerlo-rules/install_instructions.yaml",
      "files_scanned": 3,
      "install_command": "pip install 'semgrep==1.97.0'"
    },
    "display_score": 100.0,
    "display_badge": "Verified"
  },
  {
    "scanner_name": "trivy",
    "scanner_version": "0.71.0",
    "score": 0.0,
    "scanner_badge": "Unsafe",
    "findings": [
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-26007",
        "title": "cryptography: cryptography Subgroup Attack Due to Missing Subgroup Validation for SECT Curves",
        "description": "cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. Prior to 46.0.5, the public_key_from_numbers (or EllipticCurvePublicNumbers.public_key()), EllipticCurvePublicNumbers.public_key(), load_der_public_key() and load_pem_public_key() functions do not verify that the point belongs to the expected prime-order subgroup of the curve. This missing validation allows an attacker to provide a public key point P from a small-order subgroup. This can lead ",
        "remediation": "Upgrade cryptography from 46.0.3 to 46.0.5 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "GHSA-537c-gmf6-5ccf",
        "title": "Vulnerable OpenSSL included in cryptography wheels",
        "description": "pyca/cryptography's wheels include a statically linked copy of OpenSSL. The versions of OpenSSL included in wheels prior to cryptograph 48.01 are vulnerable to a security issue. More details about the vulnerability itself can be found in https://openssl-library.org/news/secadv/20260609.txt.\n\nIf you are building cryptography source (\"sdist\") then you are responsible for upgrading your copy of OpenSSL. Only users installing from wheels built by the cryptography project (i.e., those distributed on ",
        "remediation": "Upgrade cryptography from 46.0.3 to 48.0.1 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-39892",
        "title": "cryptography: Cryptography: Buffer overflow via non-contiguous buffer in API",
        "description": "cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. From 45.0.0 to before 46.0.7, if a non-contiguous buffer was passed to APIs which accepted Python buffers (e.g. Hash.update()), this could lead to buffer overflows. This vulnerability is fixed in 46.0.7.",
        "remediation": "Upgrade cryptography from 46.0.3 to 46.0.7 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "low",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-34073",
        "title": "python-cryptography: Cryptography: Security bypass due to improper DNS name constraint validation",
        "description": "cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. Prior to version 46.0.6, DNS name constraints were only validated against SANs within child certificates, and not the \"peer name\" presented during each validation. Consequently, cryptography would allow a peer named bar.example.com to validate against a wildcard leaf certificate for *.example.com, even if the leaf's parent certificate (or upwards) contained an excluded subtree constraint for b",
        "remediation": "Upgrade cryptography from 46.0.3 to 46.0.6 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-45409",
        "title": "python-idna: idna: Denial of Service via specially crafted long inputs",
        "description": "Internationalized Domain Names in Applications (IDNA) for Python provides support for Internationalized Domain Names in Applications (IDNA) and Unicode IDNA Compatibility Processing. In versions prior to 3.15, payloads such as `\"\\u0660\" * N` or `\"\\u30fb\" * N + \"\\u6f22\"` utilize the `valid_contexto` function prior to length rejection, and for high values of `N` will take a long time to process. This is the same issue as CVE-2024-3651, however the original remediation in 2024 was not a complete fi",
        "remediation": "Upgrade idna from 3.10 to 3.15 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-52869",
        "title": "MCP Python SDK: HTTP transports serve session requests without verifying the authenticated principal",
        "description": "The MCP Python SDK, called mcp on PyPI, is a Python implementation of the Model Context Protocol (MCP). Prior to 1.27.2, the SSE and stateful Streamable HTTP transports mcp.server.sse.SseServerTransport and mcp.server.streamable_http_manager.StreamableHTTPSessionManager route requests to existing sessions using only the session_id query parameter or Mcp-Session-Id header without verifying the authenticated principal that created the session, allowing a different bearer-token-authenticated client",
        "remediation": "Upgrade mcp from 1.27.0 to 1.27.2 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-52870",
        "title": "MCP Python SDK: Experimental task handlers allow any client to access and cancel other clients' tasks",
        "description": "The MCP Python SDK, called mcp on PyPI, is a Python implementation of the Model Context Protocol (MCP). From 1.23.0 until 1.27.2, default handlers installed by server.experimental.enable_tasks() for tasks/list, tasks/get, tasks/result, and tasks/cancel operate only on task identifiers without recording the session that created each task, allowing any connected client to enumerate, read results from, consume messages for, or cancel other clients' tasks. This issue is fixed in version 1.27.2.",
        "remediation": "Upgrade mcp from 1.27.0 to 1.27.2 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-59950",
        "title": "MCP Python SDK: WebSocket server transport does not support Host/Origin validation",
        "description": "The MCP Python SDK, called mcp on PyPI, is a Python implementation of the Model Context Protocol (MCP). Prior to 1.28.1, the deprecated mcp.server.websocket.websocket_server transport accepted WebSocket handshakes without applying Host or Origin header validation, leaving no SDK-level way to restrict which origins could connect to applications that exposed that transport. This issue is fixed in version 1.28.1.",
        "remediation": "Upgrade mcp from 1.27.0 to 1.28.1 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-32597",
        "title": "pyjwt: PyJWT accepts unknown `crit` header extensions (RFC 7515 \u00a74.1.11 MUST violation)",
        "description": "PyJWT is a JSON Web Token implementation in Python. Prior to 2.12.0, PyJWT does not validate the crit (Critical) Header Parameter defined in RFC 7515 \u00a74.1.11. When a JWS token contains a crit array listing extensions that PyJWT does not understand, the library accepts the token instead of rejecting it. This violates the MUST requirement in the RFC. This vulnerability is fixed in 2.12.0.",
        "remediation": "Upgrade pyjwt from 2.10.1 to 2.12.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48526",
        "title": "python-pyjwt: PyJWT: Authentication bypass due to forged JSON Web Tokens",
        "description": "PyJWT is a JSON Web Token implementation in Python. Prior to 2.13.0, when the verifier is decoding JSON Web Tokens, while supporting both asymmetric and HMAC algorithms, the library does not validate use of JSON Web Keys in HMAC algorithm, allowing attacker to use the issuer public key as the secret key for HMAC algorithm. This vulnerability is fixed in 2.13.0.",
        "remediation": "Upgrade pyjwt from 2.10.1 to 2.13.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48522",
        "title": "python-pyjwt: PyJWT: Server-Side Request Forgery (SSRF) via uncontrolled URL fetching in PyJWKClient",
        "description": "PyJWT is a JSON Web Token implementation in Python. Prior to 2.13.0, PyJWKClient passes its uri argument directly to urllib.request.urlopen() which uses Python stdlib's default OpenerDirector registering HTTPHandler, HTTPSHandler, FTPHandler, FileHandler, and DataHandler. There is currently no documented option to restrict which schemes PyJWKClient will fetch. If an application's jku URL ingestion path accepts attacker-influenced URLs (e.g., from JWT header, configuration file, OAuth flow parame",
        "remediation": "Upgrade pyjwt from 2.10.1 to 2.13.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48523",
        "title": "python-pyjwt: PyJWT: Verifier-side algorithm bypass leads to unauthorized information access",
        "description": "PyJWT is a JSON Web Token implementation in Python. From 2.9.0 to 2.12.1, there is a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, adv",
        "remediation": "Upgrade pyjwt from 2.10.1 to 2.13.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48525",
        "title": "python-pyjwt: PyJWT: Denial of Service via processing of crafted detached JWS tokens",
        "description": "PyJWT is a JSON Web Token implementation in Python. From 2.8.0 to 2.12.1, when verifying detached JWS tokens using the unencoded-payload option (\"b64\": false, RFC 7797), PyJWT performs Base64URL decoding of the compact-serialization payload segment before enforcing the detached-payload rules. For b64=false, PyJWT later discards that decoded payload and replaces it with the caller-provided detached_payload. In practice, this turns the middle segment into an attacker-controlled \u201cwork amplifier\u201d: a",
        "remediation": "Upgrade pyjwt from 2.10.1 to 2.13.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "low",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48524",
        "title": "python-pyjwt: PyJWT: Denial of Service via unverified JSON Web Token key IDs",
        "description": "PyJWT is a JSON Web Token implementation in Python. Prior to 2.13.0, PyJWKClient.get_signing_key() forces a fresh HTTP request to the JWKS endpoint for every JWT with an unknown kid value, with no rate limiting. Since kid comes from the unverified token header, an attacker can trigger unlimited outbound requests. The vulnerability surfaces only when a JWKS fetch fails; an attacker can attempt to provoke that with sustained unknown-kid traffic, but the outcome depends on upstream JWKS-endpoint be",
        "remediation": "Upgrade pyjwt from 2.10.1 to 2.13.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-28684",
        "title": "python-dotenv: python-dotenv: Arbitrary file overwrite via symbolic link following",
        "description": "python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, `set_key()` and `unset_key()` in python-dotenv follow symbolic links when rewriting `.env` files, allowing a local attacker to overwrite arbitrary files via a crafted symlink when a cross-device rename fallback is triggered. Users should upgrade to v.1.2.2 or, as a workaround, apply the patch manually.",
        "remediation": "Upgrade python-dotenv from 1.1.0 to 1.2.2 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-24486",
        "title": "python-multipart: Python-Multipart: Arbitrary file write via path traversal vulnerability",
        "description": "Python-Multipart is a streaming multipart parser for Python. Prior to version 0.0.22, a Path Traversal vulnerability exists when using non-default configuration options `UPLOAD_DIR` and `UPLOAD_KEEP_FILENAME=True`. An attacker can write uploaded files to arbitrary locations on the filesystem by crafting a malicious filename. Users should upgrade to version 0.0.22 to receive a patch or, as a workaround, avoid using `UPLOAD_KEEP_FILENAME=True` in project configurations.",
        "remediation": "Upgrade python-multipart from 0.0.20 to 0.0.22 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-42561",
        "title": "python-multipart: python-multipart: Denial of Service via excessive multipart part headers",
        "description": "Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.27, python-multipart has a denial of service vulnerability in multipart part header parsing. When parsing multipart/form-data, MultipartParser previously had no limit on the number of part headers or the size of an individual part header. An attacker could send a request with either many repeated headers without terminating the header block or a single very large header value, causing excessive CPU work before request reje",
        "remediation": "Upgrade python-multipart from 0.0.20 to 0.0.27 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-53539",
        "title": "python-multipart: Python-Multipart: Denial of Service via crafted form-urlencoded bodies",
        "description": "Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.30, when parsing application/x-www-form-urlencoded bodies, QuerystringParser located the field separator with a two step lookup: it first scanned the entire remaining buffer for &, and only when no & existed anywhere ahead did it fall back to scanning for ;. For a body that uses ; as the separator and contains no &, every field iteration performed a full failed & scan over the entire remaining buffer before locating the ne",
        "remediation": "Upgrade python-multipart from 0.0.20 to 0.0.30 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-40347",
        "title": "python-multipart: Python-Multipart: Denial of Service via crafted multipart/form-data requests",
        "description": "Python-Multipart is a streaming multipart parser for Python. Versions prior to 0.0.26 have a denial of service vulnerability when parsing crafted `multipart/form-data` requests with large preamble or epilogue sections. Upgrade to version 0.0.26 or later, which skips ahead to the next boundary candidate when processing leading CR/LF data and immediately discards epilogue data after the closing boundary.",
        "remediation": "Upgrade python-multipart from 0.0.20 to 0.0.26 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "low",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-53537",
        "title": "multipart: Python-Multipart: Information disclosure via header parsing discrepancy",
        "description": "Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.30, parse_options_header parsed Content-Disposition (and Content-Type) headers with email.message.Message, which transparently applies RFC 2231/5987 decoding. The extended parameter syntax (filename*=charset'lang'value, name*=..., and the filename*0/filename*1 continuation form) is decoded and surfaced under the bare filename/name key, and overrides the plain parameter when both are present. RFC 7578 \u00a74.2 explicitly forbid",
        "remediation": "Upgrade python-multipart from 0.0.20 to 0.0.30 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "low",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-53538",
        "title": "python-multipart: Python-Multipart: Information disclosure due to parser differential in form data handling",
        "description": "Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.30, QuerystringParser treated ; as a field separator in application/x-www-form-urlencoded bodies, in addition to &. The WHATWG URL standard, modern browsers, and Python's urllib.parse (since the CVE-2021-23336 fix) treat only & as a separator. This creates a parser differential: the same bytes are tokenized into different fields than a WHATWG compliant intermediary would produce, allowing an attacker to smuggle extra form ",
        "remediation": "Upgrade python-multipart from 0.0.20 to 0.0.30 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "low",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-53540",
        "title": "python-multipart: Python-Multipart: Negative Content-Length in parse_form buffers the entire body in memory",
        "description": "Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.31, parse_form() did not validate the Content-Length header before using it to bound its chunked read of the request body. A negative Content-Length turned the bounded read into a read-until-EOF, so the entire body was loaded into memory in a single read instead of in fixed-size chunks. This vulnerability is fixed in 0.0.31.",
        "remediation": "Upgrade python-multipart from 0.0.20 to 0.0.31 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2025-62727",
        "title": "starlette: Starlette DoS via Range header merging",
        "description": "Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial\u2011of\u2011service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.",
        "remediation": "Upgrade starlette from 0.47.0 to 0.49.1 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48818",
        "title": "starlette: Starlette: SSRF and NTLM credential theft via UNC paths in StaticFiles on Windows",
        "description": "Starlette is a lightweight ASGI framework/toolkit. In versions 1.0.1 and earlier, StaticFiles on Windows is vulnerable to SSRF. An UNC path such as \\\\attacker.com\\share can cause os.path.realpath to initiate an outbound SMB connection before the path is rejected, exposing the service account\u2019s NTLMv2 credentials for offline cracking or relay even though the HTTP response is only a 404. The issue affects default follow_symlink=False deployments, including frameworks built on Starlette such as Fas",
        "remediation": "Upgrade starlette from 0.47.0 to 1.1.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "high",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-54283",
        "title": "starlette: Starlette: request.form() limits silently ignored for application/x-www-form-urlencoded enable DoS",
        "description": "Starlette is a lightweight ASGI framework/toolkit. From 0.4.1 until 1.3.1, request.form() accepts max_fields and max_part_size to bound resource consumption while parsing form data. These limits are enforced for multipart/form-data, but silently ignored for application/x-www-form-urlencoded. An unauthenticated attacker can therefore send a urlencoded body with an arbitrarily large number of fields or an arbitrarily large field, even when the application configured limits it believed would apply.",
        "remediation": "Upgrade starlette from 0.47.0 to 1.3.1 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2025-54121",
        "title": "starlette: Starlette denial-of-service",
        "description": "Starlette is a lightweight ASGI (Asynchronous Server Gateway Interface) framework/toolkit, designed for building async web services in Python. In versions 0.47.1 and below, when parsing a multi-part form with large files (greater than the default max spool size) starlette will block the main thread to roll the file over to disk. This blocks the event thread which means the application can't accept new connections. The UploadFile code has a minor bug where instead of just checking for self._in_me",
        "remediation": "Upgrade starlette from 0.47.0 to 0.47.2 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48710",
        "title": "starlette: Starlette: Security restriction bypass via malformed HTTP Host header",
        "description": "Starlette is a lightweight ASGI framework/toolkit. Prior to version 1.0.1, the HTTP `Host` request header was not validated before being used to reconstruct `request.url`. Because the routing algorithm relies on the raw HTTP path while `request.url` is rebuilt from the `Host` header, a malformed header could make `request.url.path` differ from the path that was actually requested. Middleware and endpoints that apply security restrictions based on `request.url` (rather than the raw `scope` path) ",
        "remediation": "Upgrade starlette from 0.47.0 to 1.0.1 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "medium",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-48817",
        "title": "starlette: Starlette: Information disclosure and unintended method execution via non-standard HTTP methods",
        "description": "Starlette is a lightweight ASGI framework/toolkit. In versions 1.0.1 and below, when dispatching a request, HTTPEndpoint selects the handler by lowercasing the HTTP method and looking it up as an attribute with getattr, without restricting the lookup to a known set of HTTP verbs. When an HTTPEndpoint subclass is registered through Route(...) without an explicit methods= argument, the route does not constrain the method and every method reaches the endpoint. If a non-standard HTTP method whose lo",
        "remediation": "Upgrade starlette from 0.47.0 to 1.1.0 or later"
      },
      {
        "tool_name": "trivy",
        "severity": "low",
        "category": "uv",
        "file_path": "uv.lock",
        "line_number": null,
        "rule_identifier": "CVE-2026-54282",
        "title": "starlette: Starlette: Information disclosure due to improper HTTP request path validation",
        "description": "Starlette is a lightweight ASGI framework/toolkit. Prior to 1.3.0, the HTTP request path is not validated before being used to reconstruct request.url. Because request.url is rebuilt by concatenating {scheme}://{host}{path} and re-parsing the result, a path that does not begin with / (for example @google.com) moves the authority boundary during re-parsing, so request.url.hostname and request.url.netloc become attacker-controlled. Code that reads request.url.hostname (rather than the Host header ",
        "remediation": "Upgrade starlette from 0.47.0 to 1.3.0 or later"
      }
    ],
    "execution_duration_seconds": 0.926919972000178,
    "status": "complete",
    "metadata": {
      "source": "github-releases",
      "source_url": "https://github.com/aquasecurity/trivy/releases/tag/v0.71.0",
      "report_type": "filesystem-vulnerability",
      "install_command": "curl -sfL -o /tmp/trivy.deb https://github.com/aquasecurity/trivy/releases/download/v0.71.0/trivy_0.71.0_Linux-64bit.deb && echo '<sha256>  /tmp/trivy.deb' | sha256sum -c - && dpkg -i /tmp/trivy.deb",
      "severity_counts": {
        "low": 6,
        "high": 13,
        "medium": 10,
        "critical": 0,
        "informational": 0
      },
      "manifests_scanned": [
        "uv.lock"
      ]
    },
    "display_score": 0.0,
    "display_badge": "Unsafe"
  },
  {
    "scanner_name": "osv-scanner",
    "scanner_version": "2.3.8",
    "score": 100.0,
    "scanner_badge": "Verified",
    "findings": [],
    "execution_duration_seconds": 0.5755322949989932,
    "status": "not_applicable",
    "metadata": {
      "source": "github-releases",
      "source_url": "https://github.com/google/osv-scanner/releases/tag/v2.3.8",
      "report_type": "osv-vulnerability",
      "ecosystems_seen": [],
      "install_command": "curl -sfL -o /usr/local/bin/osv-scanner https://github.com/google/osv-scanner/releases/download/v2.3.8/osv-scanner_linux_amd64 && echo '<sha256>  /usr/local/bin/osv-scanner' | sha256sum -c - && chmod +x /usr/local/bin/osv-scanner",
      "manifests_scanned": [],
      "finding_id_aliases": {},
      "cross_scanner_correlation": {
        "only_osv": [],
        "only_trivy": [
          "CVE-2025-54121",
          "CVE-2025-62727",
          "CVE-2026-24486",
          "CVE-2026-26007",
          "CVE-2026-28684",
          "CVE-2026-32597",
          "CVE-2026-34073",
          "CVE-2026-39892",
          "CVE-2026-40347",
          "CVE-2026-42561",
          "CVE-2026-45409",
          "CVE-2026-48522",
          "CVE-2026-48523",
          "CVE-2026-48524",
          "CVE-2026-48525",
          "CVE-2026-48526",
          "CVE-2026-48710",
          "CVE-2026-48817",
          "CVE-2026-48818",
          "CVE-2026-52869",
          "CVE-2026-52870",
          "CVE-2026-53537",
          "CVE-2026-53538",
          "CVE-2026-53539",
          "CVE-2026-53540",
          "CVE-2026-54282",
          "CVE-2026-54283",
          "CVE-2026-59950",
          "GHSA-537c-gmf6-5ccf"
        ],
        "intersection_ids": []
      }
    },
    "display_score": null,
    "display_badge": "unavailable"
  }
]
```
