CVE-2026-10269
Description
An improper authorization vulnerability in decolua 9router versions up to 0.4.0 allows remote attackers to bypass authentication via manipulated Host HTTP headers.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
An improper authorization vulnerability in decolua 9router versions up to 0.4.0 allows remote attackers to bypass authentication via manipulated Host HTTP headers.
Vulnerability
The vulnerability exists in the isAuthenticated function within src/dashboardGuard.js of the 9router component. The application incorrectly relied on the Host HTTP header to determine if a request originated from a local environment, failing to properly validate the source of the request. This flaw affects all versions of 9router up to and including 0.4.0 [4].
Exploitation
An attacker can exploit this vulnerability remotely by sending crafted HTTP requests to the application. By manipulating the Host header to mimic a local request (e.g., setting it to localhost or 127.0.0.1), an attacker can bypass the intended authorization checks enforced by the dashboard guard, gaining unauthorized access to protected API endpoints [4].
Impact
Successful exploitation allows an unauthorized actor to bypass authentication mechanisms, granting them access to sensitive internal API endpoints such as /api/keys and /api/settings. This compromise leads to significant confidentiality and integrity risks, as an attacker can view or modify application configurations and credentials [4].
Mitigation
Users should upgrade to 9router version 0.4.1 or later, which addresses the flaw by replacing the insecure header-based origin validation with a more robust machine-ID based token authentication mechanism [2], [3], [4].
AI Insight generated on Jun 1, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
1428e2c045cb9implement CLI token validation for enhanced security
1 file changed · +20 −11
src/dashboardGuard.js+20 −11 modified@@ -1,11 +1,27 @@ import { NextResponse } from "next/server"; import { jwtVerify } from "jose"; import { getSettings } from "@/lib/localDb"; +import { getConsistentMachineId } from "@/shared/utils/machineId"; const SECRET = new TextEncoder().encode( process.env.JWT_SECRET || "9router-default-secret-change-me" ); +const CLI_TOKEN_HEADER = "x-9r-cli-token"; +const CLI_TOKEN_SALT = "9r-cli-auth"; + +let cachedCliToken = null; +async function getCliToken() { + if (!cachedCliToken) cachedCliToken = await getConsistentMachineId(CLI_TOKEN_SALT); + return cachedCliToken; +} + +async function hasValidCliToken(request) { + const token = request.headers.get(CLI_TOKEN_HEADER); + if (!token) return false; + return token === await getCliToken(); +} + // Always require JWT token regardless of requireLogin setting const ALWAYS_PROTECTED = [ "/api/shutdown", @@ -20,12 +36,6 @@ const PROTECTED_API_PATHS = [ "/api/provider-nodes/validate", ]; -function isLocalRequest(request) { - const host = request.headers.get("host") || ""; - const hostname = host.split(":")[0]; - return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1"; -} - async function hasValidToken(request) { const token = request.cookies.get("auth_token")?.value; if (!token) return false; @@ -55,19 +65,18 @@ async function isAuthenticated(request) { export async function proxy(request) { const { pathname } = request.nextUrl; - const isLocal = isLocalRequest(request); - // Always protected - allow localhost or valid JWT only + // Always protected - require valid JWT or local CLI token (machineId-based) if (ALWAYS_PROTECTED.some((p) => pathname.startsWith(p))) { - if (isLocal || await hasValidToken(request)) + if (await hasValidCliToken(request) || await hasValidToken(request)) return NextResponse.next(); return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } - // Protect sensitive API endpoints (bypass if localhost or requireLogin = false) + // Protect sensitive API endpoints (allow CLI token, JWT, or requireLogin=false) if (PROTECTED_API_PATHS.some((p) => pathname.startsWith(p))) { if (pathname === "/api/settings/require-login") return NextResponse.next(); - if (isLocal || await isAuthenticated(request)) + if (await hasValidCliToken(request) || await isAuthenticated(request)) return NextResponse.next(); return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); }
Vulnerability mechanics
Root cause
"The application incorrectly relied on the user-controlled Host HTTP header to determine if a request originated from the local machine, allowing for authorization bypass."
Attack vector
An attacker can bypass authentication by manipulating the Host HTTP header in their request to mimic a local connection [ref_id=2]. By setting the header to localhost or 127.0.0.1, the application's `isLocalRequest` function incorrectly identifies the request as local, granting unauthorized access to sensitive API endpoints [ref_id=1].
Affected code
The vulnerability is located in the `isAuthenticated` function and the `isLocalRequest` helper function within `src/dashboardGuard.js` [ref_id=2].
What the fix does
The patch removes the `isLocalRequest` function, which relied on the untrusted Host header [ref_id=1]. It replaces this logic with a secure `hasValidCliToken` function that validates requests using a machine-specific token [patch_id=4328685]. This ensures that only authorized clients with the correct token can access protected endpoints, rather than relying on easily spoofed network headers [ref_id=2].
Preconditions
- networkThe attacker must be able to send HTTP requests to the target application.
- inputThe attacker must be able to modify the Host HTTP header in the request.
Generated on Jun 1, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7News mentions
0No linked articles in our index yet.