import assert from "node:assert/strict"; import { once } from "node:events"; import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import { createServer } from "node:net"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { spawn } from "node:child_process"; const root = await mkdtemp(join(tmpdir(), "nodedc-map-gateway-credential-smoke-")); const cacheDir = join(root, "cache"); const port = await freePort(); let child; try { await mkdir(join(cacheDir, "ion-endpoints"), { recursive: true }); await writeFile(join(cacheDir, "index.json"), `${JSON.stringify({ version: 1, entries: {} })}\n`); await writeFile(join(cacheDir, "ion-endpoints", "1.json"), `${JSON.stringify({ assetId: "1", type: "TERRAIN", url: "https://assets.ion.cesium.com/asset/1/", accessToken: "terrain-private-token", attributions: [], savedAt: Date.now(), })}\n`); await writeFile(join(cacheDir, "ion-endpoints", "2.json"), `${JSON.stringify({ assetId: "2", type: "IMAGERY", externalType: "BING", options: { url: "https://dev.virtualearth.net/", key: "bing-private-key", mapStyle: "Aerial", }, attributions: [], savedAt: Date.now(), })}\n`); const expiredBuildingsToken = fakeJwt("expired-buildings", Math.floor(Date.now() / 1000) - 60); await writeFile(join(cacheDir, "ion-endpoints", "96188.json"), `${JSON.stringify({ assetId: "96188", type: "3DTILES", url: "https://assets.ion.cesium.com/us-east-1/asset_depot/96188/OpenStreetMap/CWT/2025-04-01/tileset.json?v=expired", accessToken: expiredBuildingsToken, attributions: [], savedAt: Date.now(), })}\n`); child = spawn(process.execPath, ["src/server.mjs"], { cwd: new URL("..", import.meta.url), env: { ...process.env, PORT: String(port), MAP_CACHE_DIR: cacheDir, MAP_GATEWAY_ALLOW_ANONYMOUS: "true", MAP_CACHE_MODE: "readwrite", CESIUM_ION_TOKEN: "", CESIUM_ION_ASSET_ALLOWLIST: "1,2,96188", MAP_GATEWAY_UPSTREAM_ALLOWLIST: "assets.ion.cesium.com,dev.virtualearth.net", }, stdio: ["ignore", "pipe", "pipe"], }); await waitForGateway(port, child); for (const assetId of ["1", "2"]) { const response = await fetch(`http://127.0.0.1:${port}/api/map/ion/assets/${assetId}/endpoint`); assert.equal(response.status, 200); const body = await response.text(); assert.equal(body.includes("terrain-private-token"), false); assert.equal(body.includes("bing-private-key"), false); assert.equal(body.includes("accessToken"), false); assert.equal(body.includes('"key"'), false); const endpoint = JSON.parse(body); assert.equal(endpoint.credentialMode, "gateway"); } const expired = await fetch(`http://127.0.0.1:${port}/api/map/ion/assets/96188/endpoint`); const expiredRaw = await expired.text(); assert.equal(expired.status, 503); assert.equal(expiredRaw.includes(expiredBuildingsToken), false); assert.equal(expiredRaw.includes("accessToken"), false); console.log("ok: endpoint responses contain no credentials and a known-expired Ion JWT is never served"); } finally { if (child && !child.killed) { child.kill("SIGTERM"); await once(child, "exit").catch(() => undefined); } await rm(root, { recursive: true, force: true }); } async function freePort() { const server = createServer(); server.listen(0, "127.0.0.1"); await once(server, "listening"); const address = server.address(); assert(address && typeof address === "object"); const port = address.port; server.close(); await once(server, "close"); return port; } async function waitForGateway(port, child) { let output = ""; child.stderr.on("data", (chunk) => { output += String(chunk); }); for (let attempt = 0; attempt < 60; attempt += 1) { if (child.exitCode !== null) throw new Error(`gateway_exited:${child.exitCode}:${output}`); try { const response = await fetch(`http://127.0.0.1:${port}/healthz`); if (response.ok) return; } catch { /* service is still starting */ } await new Promise((resolve) => setTimeout(resolve, 50)); } throw new Error(`gateway_start_timeout:${output}`); } function fakeJwt(subject, exp) { const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url"); return `${encode({ alg: "HS256", typ: "JWT" })}.${encode({ sub: subject, exp })}.test-signature`; }