CVE-2026-54446
NetLicensing MCP Server: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
NetLicensing MCP Server is a natural-language interface that enables agentic applications to manage the software-licensing lifecycle in Labs64 NetLicensing. Prior to 0.1.6, network-reachable HTTP transport requests to /mcp that omit x-netlicensing-api-key, Authorization: Bearer, and the apikey query parameter pass through ApiKeyMiddleware in src/netlicensing_mcp/server.py without authentication. The downstream api_key_ctx in src/netlicensing_mcp/client.py then falls back to the operator's NETLICENSING_API_KEY and authenticates upstream NetLicensing REST API calls under the operator account. An unauthenticated attacker can invoke MCP tools to enumerate products, licenses, licensees, and transactions, create or modify licensing objects, perform validations, and execute destructive delete operations. The issue affects HTTP deployments configured with a server-side key and does not require user interaction. This issue is fixed in version 0.1.6.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.1
NetLicensing MCP Server: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
NetLicensing MCP Server is a natural-language interface that enables agentic applications to manage the software-licensing lifecycle in Labs64 NetLicensing. Prior to 0.1.6, network-reachable HTTP transport requests to /mcp that omit x-netlicensing-api-key, Authorization: Bearer, and the apikey query parameter pass through ApiKeyMiddleware in src/netlicensing_mcp/server.py without authentication. The downstream api_key_ctx in src/netlicensing_mcp/client.py then falls back to the operator's NETLICENSING_API_KEY and authenticates upstream NetLicensing REST API calls under the operator account. An unauthenticated attacker can invoke MCP tools to enumerate products, licenses, licensees, and transactions, create or modify licensing objects, perform validations, and execute destructive delete operations. The issue affects HTTP deployments configured with a server-side key and does not require user interaction. This issue is fixed in version 0.1.6.
- Record
CVE-2026-54446- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-306
- Updated
- 2026-11-02
EUVDEUVD-2026-82336
8.1
NetLicensing MCP Server is a natural-language interface that enables agentic applications to manage the software-licensing lifecycle in Labs64 NetLicensing. Prior to 0.1.6, network-reachable HTTP transport requests to /mcp that omit x-netlicensing-api-key, Authorization: Bearer, and the apikey query parameter pass through ApiKeyMiddleware in src/netlicensing_mcp/server.py without authentication. The downstream api_key_ctx in src/netlicensing_mcp/client.py then falls back to the operator's NETLICENSING_API_KEY and authenticates upstream NetLicensing REST API calls under the operator account. An unauthenticated attacker can invoke MCP tools to enumerate products, licenses, licensees, and transactions, create or modify licensing objects, perform validations, and execute destructive delete operations. The issue affects HTTP deployments configured with a server-side key and does not require user interaction. This issue is fixed in version 0.1.6.
- Record
EUVD-2026-82336- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-x9vc-9ffq-p3gj
8.1
NetLicensing-MCP: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
Summary
When netlicensing-mcp is run in HTTP transport mode, the ApiKeyMiddleware fails to enforce authentication: requests that carry no client API key are unconditionally forwarded to the next handler (server.py:1427). The downstream HTTP client then falls back to the server operator's NETLICENSING_API_KEY environment variable (client.py:30) and uses it to authenticate every upstream call to the NetLicensing REST API. An unauthenticated network attacker can therefore invoke any MCP tool — including product listing, license creation/modification, and destructive delete operations — entirely under the operator's identity and account quota. CVSS 3.1 Base Score: 8.1 (High).
Details
The HTTP transport is started in src/netlicensing_mcp/server.py around line 1430 via mcp.streamable_http_app(), and ApiKeyMiddleware is registered immediately after (line 1431). The middleware implementation (lines 1412–1427) attempts to extract a per-request API key from either the x-netlicensing-api-key header or the ?apikey= query parameter. However, if neither source provides a key, the middleware takes no enforcement action and simply calls return await call_next(request) (line 1427), passing the unauthenticated request downstream.
The downstream client module (src/netlicensing_mcp/client.py) uses a Python ContextVar named api_key_ctx with a default of os.getenv("NETLICENSING_API_KEY", "") (line 30). Because the middleware never sets this context variable for unauthenticated requests, api_key_ctx.get() returns the server-level environment variable. The client then encodes this value into an HTTP Basic Authorization header (lines 62–70) and transmits it to the upstream NetLicensing REST API on every request (lines 105, 109).
The complete exploitable data flow is:
| Step | Location | Description |
|------|----------|-------------|
| 1 | server.py:1430 | HTTP app created with mcp.streamable_http_app() |
| 2 | server.py:1431 | ApiKeyMiddleware registered |
| 3 | server.py:1412–1419 | Middleware attempts (optional) key extraction from headers/query |
| 4 | server.py:1427 | Auth bypass sink: missing key → return await call_next(request) |
| 5 | server.py:155–163 | Unauthenticated caller invokes netlicensing_list_products (or any tool) |
| 6 | tools/products.py:9,17 | Tool delegates to nl_get("/product", ...) |
| 7 | client.py:30 | Source: api_key_ctx defaults to NETLICENSING_API_KEY env var |
| 8 | client.py:62–70 | Authorization: Basic base64("apiKey:<key>") constructed |
| 9 | client.py:105,109 | Upstream sink: client.get(url, headers=_headers(), ...) executed |
Critical code excerpts:
# src/netlicensing_mcp/server.py
1418: if not key:
1419: key = request.query_params.get("apikey")
1421: if key:
1422: token = api_key_ctx.set(key)
...
1427: return await call_next(request) # <-- no rejection when key is absent
# src/netlicensing_mcp/client.py
30: "api_key", default=os.getenv("NETLICENSING_API_KEY", "") # server-side fallback
...
64: auth_str = f"apiKey:{api_key}"
70: "Authorization": f"Basic {token}",
...
109: r = await client.get(url, headers=_headers(), params=params or {})
The README (README.md:90–94) documents the HTTP mode deployment pattern with -e NETLICENSING_API_KEY=your_key as a first-class production deployment option, including AWS App Runner / ELB examples (README.md:310–318). The per-client key recommendation (README.md:318) is advisory only and is not technically enforced.
A suggested patch replaces the unconditional pass-through with a 401 rejection:
--- a/src/netlicensing_mcp/server.py
+++ b/src/netlicensing_mcp/server.py
@@
class ApiKeyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):…
- Record
GHSA-x9vc-9ffq-p3gj- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-306
- Updated
- 2026-11-02
NVD
8.1
NetLicensing MCP Server is a natural-language interface that enables agentic applications to manage the software-licensing lifecycle in Labs64 NetLicensing. Prior to 0.1.6, network-reachable HTTP transport requests to /mcp that omit x-netlicensing-api-key, Authorization: Bearer, and the apikey query parameter pass through ApiKeyMiddleware in src/netlicensing_mcp/server.py without authentication. The downstream api_key_ctx in src/netlicensing_mcp/client.py then falls back to the operator's NETLICENSING_API_KEY and authenticates upstream NetLicensing REST API calls under the operator account. An unauthenticated attacker can invoke MCP tools to enumerate products, licenses, licensees, and transactions, create or modify licensing objects, perform validations, and execute destructive delete operations. The issue affects HTTP deployments configured with a server-side key and does not require user interaction. This issue is fixed in version 0.1.6.
- Record
CVE-2026-54446- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-306
- Updated
- 2026-11-02
OSV
8.1
NetLicensing MCP Server: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
NetLicensing MCP Server is a natural-language interface that enables agentic applications to manage the software-licensing lifecycle in Labs64 NetLicensing. Prior to 0.1.6, network-reachable HTTP transport requests to /mcp that omit x-netlicensing-api-key, Authorization: Bearer, and the apikey query parameter pass through ApiKeyMiddleware in src/netlicensing_mcp/server.py without authentication. The downstream api_key_ctx in src/netlicensing_mcp/client.py then falls back to the operator's NETLICENSING_API_KEY and authenticates upstream NetLicensing REST API calls under the operator account. An unauthenticated attacker can invoke MCP tools to enumerate products, licenses, licensees, and transactions, create or modify licensing objects, perform validations, and execute destructive delete operations. The issue affects HTTP deployments configured with a server-side key and does not require user interaction. This issue is fixed in version 0.1.6.
- Record
CVE-2026-54446- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-306
- Updated
- 2026-11-02
OSVGHSA-x9vc-9ffq-p3gj
8.1
NetLicensing-MCP: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
Summary
When netlicensing-mcp is run in HTTP transport mode, the ApiKeyMiddleware fails to enforce authentication: requests that carry no client API key are unconditionally forwarded to the next handler (server.py:1427). The downstream HTTP client then falls back to the server operator's NETLICENSING_API_KEY environment variable (client.py:30) and uses it to authenticate every upstream call to the NetLicensing REST API. An unauthenticated network attacker can therefore invoke any MCP tool — including product listing, license creation/modification, and destructive delete operations — entirely under the operator's identity and account quota. CVSS 3.1 Base Score: 8.1 (High).
Details
The HTTP transport is started in src/netlicensing_mcp/server.py around line 1430 via mcp.streamable_http_app(), and ApiKeyMiddleware is registered immediately after (line 1431). The middleware implementation (lines 1412–1427) attempts to extract a per-request API key from either the x-netlicensing-api-key header or the ?apikey= query parameter. However, if neither source provides a key, the middleware takes no enforcement action and simply calls return await call_next(request) (line 1427), passing the unauthenticated request downstream.
The downstream client module (src/netlicensing_mcp/client.py) uses a Python ContextVar named api_key_ctx with a default of os.getenv("NETLICENSING_API_KEY", "") (line 30). Because the middleware never sets this context variable for unauthenticated requests, api_key_ctx.get() returns the server-level environment variable. The client then encodes this value into an HTTP Basic Authorization header (lines 62–70) and transmits it to the upstream NetLicensing REST API on every request (lines 105, 109).
The complete exploitable data flow is:
| Step | Location | Description |
|------|----------|-------------|
| 1 | server.py:1430 | HTTP app created with mcp.streamable_http_app() |
| 2 | server.py:1431 | ApiKeyMiddleware registered |
| 3 | server.py:1412–1419 | Middleware attempts (optional) key extraction from headers/query |
| 4 | server.py:1427 | Auth bypass sink: missing key → return await call_next(request) |
| 5 | server.py:155–163 | Unauthenticated caller invokes netlicensing_list_products (or any tool) |
| 6 | tools/products.py:9,17 | Tool delegates to nl_get("/product", ...) |
| 7 | client.py:30 | Source: api_key_ctx defaults to NETLICENSING_API_KEY env var |
| 8 | client.py:62–70 | Authorization: Basic base64("apiKey:<key>") constructed |
| 9 | client.py:105,109 | Upstream sink: client.get(url, headers=_headers(), ...) executed |
Critical code excerpts:
# src/netlicensing_mcp/server.py
1418: if not key:
1419: key = request.query_params.get("apikey")
1421: if key:
1422: token = api_key_ctx.set(key)
...
1427: return await call_next(request) # <-- no rejection when key is absent
# src/netlicensing_mcp/client.py
30: "api_key", default=os.getenv("NETLICENSING_API_KEY", "") # server-side fallback
...
64: auth_str = f"apiKey:{api_key}"
70: "Authorization": f"Basic {token}",
...
109: r = await client.get(url, headers=_headers(), params=params or {})
The README (README.md:90–94) documents the HTTP mode deployment pattern with -e NETLICENSING_API_KEY=your_key as a first-class production deployment option, including AWS App Runner / ELB examples (README.md:310–318). The per-client key recommendation (README.md:318) is advisory only and is not technically enforced.
A suggested patch replaces the unconditional pass-through with a 401 rejection:
--- a/src/netlicensing_mcp/server.py
+++ b/src/netlicensing_mcp/server.py
@@
class ApiKeyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):…
- Record
GHSA-x9vc-9ffq-p3gj- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-306
- Updated
- 2026-11-02
OSVPYSEC-2026-3490
8.1
NetLicensing-MCP: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
Summary
When netlicensing-mcp is run in HTTP transport mode, the ApiKeyMiddleware fails to enforce authentication: requests that carry no client API key are unconditionally forwarded to the next handler (server.py:1427). The downstream HTTP client then falls back to the server operator's NETLICENSING_API_KEY environment variable (client.py:30) and uses it to authenticate every upstream call to the NetLicensing REST API. An unauthenticated network attacker can therefore invoke any MCP tool — including product listing, license creation/modification, and destructive delete operations — entirely under the operator's identity and account quota. CVSS 3.1 Base Score: 8.1 (High).
Details
The HTTP transport is started in src/netlicensing_mcp/server.py around line 1430 via mcp.streamable_http_app(), and ApiKeyMiddleware is registered immediately after (line 1431). The middleware implementation (lines 1412–1427) attempts to extract a per-request API key from either the x-netlicensing-api-key header or the ?apikey= query parameter. However, if neither source provides a key, the middleware takes no enforcement action and simply calls return await call_next(request) (line 1427), passing the unauthenticated request downstream.
The downstream client module (src/netlicensing_mcp/client.py) uses a Python ContextVar named api_key_ctx with a default of os.getenv("NETLICENSING_API_KEY", "") (line 30). Because the middleware never sets this context variable for unauthenticated requests, api_key_ctx.get() returns the server-level environment variable. The client then encodes this value into an HTTP Basic Authorization header (lines 62–70) and transmits it to the upstream NetLicensing REST API on every request (lines 105, 109).
The complete exploitable data flow is:
| Step | Location | Description |
|------|----------|-------------|
| 1 | server.py:1430 | HTTP app created with mcp.streamable_http_app() |
| 2 | server.py:1431 | ApiKeyMiddleware registered |
| 3 | server.py:1412–1419 | Middleware attempts (optional) key extraction from headers/query |
| 4 | server.py:1427 | Auth bypass sink: missing key → return await call_next(request) |
| 5 | server.py:155–163 | Unauthenticated caller invokes netlicensing_list_products (or any tool) |
| 6 | tools/products.py:9,17 | Tool delegates to nl_get("/product", ...) |
| 7 | client.py:30 | Source: api_key_ctx defaults to NETLICENSING_API_KEY env var |
| 8 | client.py:62–70 | Authorization: Basic base64("apiKey:<key>") constructed |
| 9 | client.py:105,109 | Upstream sink: client.get(url, headers=_headers(), ...) executed |
Critical code excerpts:
# src/netlicensing_mcp/server.py
1418: if not key:
1419: key = request.query_params.get("apikey")
1421: if key:
1422: token = api_key_ctx.set(key)
...
1427: return await call_next(request) # <-- no rejection when key is absent
# src/netlicensing_mcp/client.py
30: "api_key", default=os.getenv("NETLICENSING_API_KEY", "") # server-side fallback
...
64: auth_str = f"apiKey:{api_key}"
70: "Authorization": f"Basic {token}",
...
109: r = await client.get(url, headers=_headers(), params=params or {})
The README (README.md:90–94) documents the HTTP mode deployment pattern with -e NETLICENSING_API_KEY=your_key as a first-class production deployment option, including AWS App Runner / ELB examples (README.md:310–318). The per-client key recommendation (README.md:318) is advisory only and is not technically enforced.
A suggested patch replaces the unconditional pass-through with a 401 rejection:
--- a/src/netlicensing_mcp/server.py
+++ b/src/netlicensing_mcp/server.py
@@
class ApiKeyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):…
- Record
PYSEC-2026-3490- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
netlicensing-mcpPyPI |
through 0.1.5 | 0.1.6 |
netlicensing-mcpPyPI |
before 0.1.6, plus listed versions 0.0.60.1.00.1.10.1.20.1.30.1.40.1.5 |
0.1.6 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-54446 is a high vulnerability (CVSS 8.1) in Labs64 Netlicensing-mcp, published July 14, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.62%, the 49th percentile. Fixed in netlicensing-mcp 0.1.6.
References
github.com/Labs64/NetLicensing-MCP/security/advisories/GHSA-x9vc-9ffq-p3gj
github.com/Labs64/NetLicensing-MCP/commit/fbbb1d5ff88eb5400ec933a84e75601ebee48927
github.com/Labs64/NetLicensing-MCP/releases/tag/0.1.6
github.com/advisories/GHSA-x9vc-9ffq-p3gj
github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54446.json
nvd.nist.gov/vuln/detail/CVE-2026-54446
github.com/Labs64/NetLicensing-MCP
pypi.org/project/netlicensing-mcp
Left out here: the rest of 3 source descriptions. The JSON record has them.