fix: require internal token for device pod executor

This commit is contained in:
Codex
2026-05-29 16:28:34 +08:00
parent b974372ab6
commit 5ebadee19f
7 changed files with 84 additions and 24 deletions
+24 -9
View File
@@ -4,6 +4,7 @@ import { createServer } from "node:http";
import test from "node:test";
const bunCommand = process.env.HWLAB_TEST_BUN_COMMAND || process.env.HWLAB_BUN_COMMAND || (process.versions.bun ? process.execPath : "bun");
const INTERNAL_TOKEN = "test-internal-token";
test("device pod executor exposes cloud-api authority boundary and method guards", async () => {
const service = await startDevicePod("device-pod-test");
@@ -42,9 +43,17 @@ test("device pod executor exposes cloud-api authority boundary and method guards
assert.equal(externalJob.status, 403);
assert.equal((await externalJob.json()).error.code, "cloud_api_authority_required");
const internalJob = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, {
const headerOnlyJob = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, {
method: "POST",
headers: { "content-type": "application/json", "x-hwlab-internal-service": "hwlab-cloud-api" },
body: JSON.stringify({ intent: "workspace.ls" })
});
assert.equal(headerOnlyJob.status, 403);
assert.equal((await headerOnlyJob.json()).error.code, "cloud_api_authority_required");
const internalJob = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, {
method: "POST",
headers: internalHeaders({ "content-type": "application/json" }),
body: JSON.stringify({ jobId: "job_devicepod_test", intent: "workspace.ls", traceId: "trc_device_pod_test", operationId: "op_device_pod_test" })
});
assert.equal(internalJob.status, 409);
@@ -54,13 +63,13 @@ test("device pod executor exposes cloud-api authority boundary and method guards
assert.equal(internalJobPayload.outputUrl, "/v1/device-pods/device-pod-test/jobs/job_devicepod_test/output");
const storedJob = await fetchJson(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs/job_devicepod_test`, {
headers: { "x-hwlab-internal-service": "hwlab-cloud-api" }
headers: internalHeaders()
});
assert.equal(storedJob.job.id, "job_devicepod_test");
assert.equal(storedJob.status, "blocked");
const storedOutput = await fetchJson(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs/job_devicepod_test/output`, {
headers: { "x-hwlab-internal-service": "hwlab-cloud-api" }
headers: internalHeaders()
});
assert.equal(storedOutput.job.id, "job_devicepod_test");
assert.equal(storedOutput.output.summary, "HWLAB_CLOUD_API_INTERNAL_URL is not configured for gateway dispatch");
@@ -73,7 +82,7 @@ test("device pod executor exposes cloud-api authority boundary and method guards
const cancelStored = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs/job_devicepod_test/cancel`, {
method: "POST",
headers: { "x-hwlab-internal-service": "hwlab-cloud-api" }
headers: internalHeaders()
});
assert.equal(cancelStored.status, 200);
assert.equal((await cancelStored.json()).job.id, "job_devicepod_test");
@@ -114,7 +123,7 @@ test("device-pod executor bounds gateway output text", async () => {
try {
const jobResponse = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, {
method: "POST",
headers: { "content-type": "application/json", "x-hwlab-internal-service": "hwlab-cloud-api" },
headers: internalHeaders({ "content-type": "application/json" }),
body: JSON.stringify({
jobId: "job_bounded_output_test",
intent: "workspace.ls",
@@ -149,7 +158,7 @@ test("device pod executor dispatches internal jobs through cloud-api gateway ada
const dispatches = [];
const cloudApi = createServer(async (request, response) => {
const body = await requestJson(request);
dispatches.push({ method: request.method, url: request.url, internalService: request.headers["x-hwlab-internal-service"], body });
dispatches.push({ method: request.method, url: request.url, internalService: request.headers["x-hwlab-internal-service"], internalToken: request.headers["x-hwlab-internal-token"], body });
response.writeHead(200, { "content-type": "application/json; charset=utf-8" });
response.end(JSON.stringify({
jsonrpc: "2.0",
@@ -183,7 +192,7 @@ test("device pod executor dispatches internal jobs through cloud-api gateway ada
try {
const jobResponse = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, {
method: "POST",
headers: { "content-type": "application/json", "x-hwlab-internal-service": "hwlab-cloud-api" },
headers: internalHeaders({ "content-type": "application/json" }),
body: JSON.stringify({
jobId: "job_devicepod_gateway_test",
intent: "workspace.ls",
@@ -220,6 +229,7 @@ test("device pod executor dispatches internal jobs through cloud-api gateway ada
assert.equal(output.output.auditId, "aud_devicepod_test");
assert.equal(dispatches.length, 1);
assert.equal(dispatches[0].internalService, "hwlab-device-pod");
assert.equal(dispatches[0].internalToken, INTERNAL_TOKEN);
assert.equal(dispatches[0].body.params.gatewaySessionId, "gws_devicepod_test");
assert.equal(dispatches[0].body.params.input.cwd, "F:\\Work\\Project");
assert.match(dispatches[0].body.params.input.command, /^node tools\\device-host-cli\.mjs --profile-json-b64 /u);
@@ -254,7 +264,7 @@ test("device-pod executor maps v0.1 CLI options to device-host-cli argv", async
try {
const jobResponse = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, {
method: "POST",
headers: { "content-type": "application/json", "x-hwlab-internal-service": "hwlab-cloud-api" },
headers: internalHeaders({ "content-type": "application/json" }),
body: JSON.stringify({
jobId: "job_options_test",
intent: "debug.download",
@@ -293,6 +303,7 @@ async function startDevicePod(devicePodId, extraEnv = {}) {
HWLAB_DEVICE_POD_PORT: String(port),
HWLAB_DEVICE_POD_ID: devicePodId,
HWLAB_ENVIRONMENT: "v02",
HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN,
...extraEnv
},
stdio: ["ignore", "pipe", "pipe"]
@@ -314,7 +325,7 @@ async function waitForJobStatus(port, devicePodId, jobId, status) {
let last;
for (let attempt = 0; attempt < 30; attempt += 1) {
last = await fetchJson(`http://127.0.0.1:${port}/v1/device-pods/${devicePodId}/jobs/${jobId}/output`, {
headers: { "x-hwlab-internal-service": "hwlab-cloud-api" }
headers: internalHeaders()
});
if (last.status === status) return last;
await new Promise((resolve) => setTimeout(resolve, 50));
@@ -323,6 +334,10 @@ async function waitForJobStatus(port, devicePodId, jobId, status) {
return last;
}
function internalHeaders(extra = {}) {
return { ...extra, "x-hwlab-internal-service": "hwlab-cloud-api", "x-hwlab-internal-token": INTERNAL_TOKEN };
}
async function requestJson(request) {
const chunks = [];
for await (const chunk of request) chunks.push(chunk);
+7 -2
View File
@@ -11,6 +11,7 @@ const port = parsePort(process.env.HWLAB_DEVICE_POD_PORT, parsePort(process.env.
const devicePodId = process.env.HWLAB_DEVICE_POD_ID || DEFAULT_DEVICE_POD_ID;
const environment = process.env.HWLAB_ENVIRONMENT || process.env.HWLAB_GITOPS_PROFILE || "dev";
const cloudApiInternalUrl = normalizeBaseUrl(process.env.HWLAB_CLOUD_API_INTERNAL_URL || process.env.HWLAB_CLOUD_API_URL);
const internalToken = textOr(process.env.HWLAB_DEVICE_POD_INTERNAL_TOKEN, "");
const dispatchTimeoutMs = numberOr(process.env.HWLAB_DEVICE_POD_GATEWAY_DISPATCH_TIMEOUT_MS, 120000);
const DEVICE_JOB_OUTPUT_MAX_BYTES = 12000;
const jobs = new Map();
@@ -236,6 +237,7 @@ async function dispatchGatewayJob({ job, route }) {
accept: "application/json",
"content-type": "application/json",
"x-hwlab-internal-service": SERVICE_ID,
"x-hwlab-internal-token": internalToken,
"x-trace-id": job.traceId,
"x-request-id": `req_${job.id}`
},
@@ -318,8 +320,11 @@ function parseDevicePodPath(pathname) {
}
function isInternalCaller(request) {
const header = request.headers["x-hwlab-internal-service"];
return String(Array.isArray(header) ? header[0] : header ?? "").trim() === "hwlab-cloud-api";
if (!internalToken) return false;
const serviceHeader = request.headers["x-hwlab-internal-service"];
const tokenHeader = request.headers["x-hwlab-internal-token"];
return String(Array.isArray(serviceHeader) ? serviceHeader[0] : serviceHeader ?? "").trim() === "hwlab-cloud-api"
&& String(Array.isArray(tokenHeader) ? tokenHeader[0] : tokenHeader ?? "").trim() === internalToken;
}
function sourcePayload() {
+8 -1
View File
@@ -151,7 +151,14 @@
"HWLAB_BOOTSTRAP_ADMIN_ID": "usr_v02_admin",
"HWLAB_BOOTSTRAP_ADMIN_USERNAME": "admin",
"HWLAB_BOOTSTRAP_ADMIN_DISPLAY_NAME": "HWLAB v0.2 Admin",
"HWLAB_BOOTSTRAP_ADMIN_PASSWORD_HASH": "secretRef:hwlab-v02-bootstrap-admin/password-hash"
"HWLAB_BOOTSTRAP_ADMIN_PASSWORD_HASH": "secretRef:hwlab-v02-bootstrap-admin/password-hash",
"HWLAB_DEVICE_POD_INTERNAL_TOKEN": "secretRef:hwlab-v02-device-pod-internal/token"
}
},
{
"serviceId": "hwlab-device-pod",
"env": {
"HWLAB_DEVICE_POD_INTERNAL_TOKEN": "secretRef:hwlab-v02-device-pod-internal/token"
}
}
]
@@ -14,7 +14,7 @@
- `cmd/hwlab-device-pod/main.ts` 返回 `contractVersion=device-pod-executor-v1`,并声明 `authority=hwlab-cloud-api``fake=false`
- `internal/device-pod/fake-data.mjs` 只保留为 legacy 前端/smoke fixture,不是 `hwlab-device-pod` 服务运行契约。
- `HWLAB_DEVICE_POD_ID` 可指定默认 executor devicePodId;正式 profile 仍由 `hwlab-cloud-api``device_pods.profile_json` 管理。
- `HWLAB_CLOUD_API_INTERNAL_URL` 配置后,executor 可用内部服务凭据回调 `hwlab-cloud-api` 的 gateway dispatch route,把已授权 job 下发到 gateway/device-host-cli未配置、profile route 缺失或 gateway 不在线时返回 blocker。
- `HWLAB_CLOUD_API_INTERNAL_URL` 配置后,executor 可用内部服务凭据回调 `hwlab-cloud-api` 的 gateway dispatch route,把已授权 job 下发到 gateway/device-host-cli内部 job 和 dispatch 必须同时携带 `x-hwlab-internal-service``HWLAB_DEVICE_POD_INTERNAL_TOKEN` 对应的 `x-hwlab-internal-token`。未配置内部 token、profile route 缺失或 gateway 不在线时返回 blocker/拒绝执行
## API 接口说明
@@ -24,8 +24,8 @@
| `GET /v1/device-pods` | 返回 executor boundary 摘要,不表达用户可见 device pod authority。 |
| `GET /v1/device-pods/{devicePodId}/status` | 返回 blocked executor status;真实 profile/status 以 cloud-api `/v1/device-pods/{devicePodId}/status` 为准。 |
| `GET /v1/device-pods/{devicePodId}/events` | 返回 bounded executor boundary event,不伪造硬件事件。 |
| `POST /v1/device-pods/{devicePodId}/jobs` | 只接受 `hwlab-cloud-api` 内部调用;创建内部 executor job 并返回 job、freshness、output/cancel URL;有可用 profile route 和 `HWLAB_CLOUD_API_INTERNAL_URL` 时通过 cloud-api gateway dispatch 下发到 device-host-cli,否则返回 `gateway_dispatch_unavailable`。 |
| `GET /v1/device-pods/{devicePodId}/jobs/{jobId}``GET /output``POST /cancel` | 只接受 `hwlab-cloud-api` 内部调用,用于查询内部 job、最大 12000 bytes 的 bounded output 和取消非终态 job;普通用户仍必须走 cloud-api 用户态 API。 |
| `POST /v1/device-pods/{devicePodId}/jobs` | 只接受带内部 token 的 `hwlab-cloud-api` 调用;创建内部 executor job 并返回 job、freshness、output/cancel URL;有可用 profile route 和 `HWLAB_CLOUD_API_INTERNAL_URL` 时通过 cloud-api gateway dispatch 下发到 device-host-cli,否则返回 `gateway_dispatch_unavailable`。 |
| `GET /v1/device-pods/{devicePodId}/jobs/{jobId}``GET /output``POST /cancel` | 只接受带内部 token 的 `hwlab-cloud-api` 调用,用于查询内部 job、最大 12000 bytes 的 bounded output 和取消非终态 job;普通用户仍必须走 cloud-api 用户态 API。 |
用户态 `POST /jobs`、job output/cancel、admin profile/grant API 和正式 `device-pod-cli` REST 调用由 `hwlab-cloud-api` 实现,应以 [spec-device-pod.md](spec-device-pod.md) 为目标。`hwlab-device-pod` 不接受 CLI、浏览器或 Code Agent 直接上传 profile snapshot。
+29 -5
View File
@@ -4,6 +4,8 @@ import { test } from "bun:test";
import { createCloudApiServer } from "./server.ts";
const INTERNAL_TOKEN = "test-internal-token";
test("cloud api access control grants visible device pods and blocks unavailable gateway jobs", async () => {
const server = createCloudApiServer({
env: {
@@ -209,7 +211,7 @@ test("cloud api dispatches authorized device jobs to the internal device-pod exe
const executorRequests = [];
const executor = createServer(async (request, response) => {
const body = await requestJson(request);
executorRequests.push({ method: request.method, url: request.url, internalService: request.headers["x-hwlab-internal-service"], body });
executorRequests.push({ method: request.method, url: request.url, internalService: request.headers["x-hwlab-internal-service"], internalToken: request.headers["x-hwlab-internal-token"], body });
if (request.method === "POST" && body.args?.path === "src") {
response.writeHead(202, { "content-type": "application/json; charset=utf-8" });
response.end(JSON.stringify({
@@ -259,6 +261,7 @@ test("cloud api dispatches authorized device jobs to the internal device-pod exe
HWLAB_ACCESS_CONTROL_REQUIRED: "1",
HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin",
HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass",
HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN,
HWLAB_DEVICE_POD_URL: `http://127.0.0.1:${executorPort}`
},
now: () => "2026-05-28T00:00:00.000Z"
@@ -282,6 +285,7 @@ test("cloud api dispatches authorized device jobs to the internal device-pod exe
assert.equal(job.body.job.status, "blocked");
assert.equal(executorRequests.length, 1);
assert.equal(executorRequests[0].internalService, "hwlab-cloud-api");
assert.equal(executorRequests[0].internalToken, INTERNAL_TOKEN);
assert.equal(executorRequests[0].body.profileHash, job.body.profileHash);
assert.equal(executorRequests[0].body.ownerUserId, userCreate.body.user.id);
assert.equal(executorRequests[0].body.intent, "workspace.ls");
@@ -330,6 +334,7 @@ test("cloud api bounds device-pod job output payloads", async () => {
HWLAB_ACCESS_CONTROL_REQUIRED: "1",
HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin",
HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass",
HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN,
HWLAB_DEVICE_POD_URL: `http://127.0.0.1:${executorPort}`
},
now: () => "2026-05-28T00:00:00.000Z"
@@ -390,6 +395,7 @@ test("cloud api routes device-pod probe GET requests through executor jobs", asy
HWLAB_ACCESS_CONTROL_REQUIRED: "1",
HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin",
HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass",
HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN,
HWLAB_DEVICE_POD_URL: `http://127.0.0.1:${executorPort}`
},
now: () => "2026-05-28T00:00:00.000Z"
@@ -441,7 +447,7 @@ test("cloud api routes device-pod probe GET requests through executor jobs", asy
test("cloud api internal device-pod gateway dispatch is service-only and fail-closed without online gateway", async () => {
const server = createCloudApiServer({
env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" },
env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN },
now: () => "2026-05-28T00:00:00.000Z"
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
@@ -452,6 +458,20 @@ test("cloud api internal device-pod gateway dispatch is service-only and fail-cl
assert.equal(external.status, 403);
assert.equal(external.body.error.code, "device_pod_internal_authority_required");
const headerOnly = await postJson(port, "/v1/internal/device-pod/gateway-dispatch", {
id: "req_devicepod_dispatch_test",
params: {
gatewaySessionId: "gws_missing",
resourceId: "res_devicepod_test",
capabilityId: "cap_device_host_cli",
operationId: "op_devicepod_dispatch_test",
traceId: "trc_devicepod_dispatch_test",
input: { command: "node tools/device-host-cli.mjs health" }
}
}, null, { "x-hwlab-internal-service": "hwlab-device-pod" });
assert.equal(headerOnly.status, 403);
assert.equal(headerOnly.body.error.code, "device_pod_internal_authority_required");
const blocked = await postJson(port, "/v1/internal/device-pod/gateway-dispatch", {
id: "req_devicepod_dispatch_test",
params: {
@@ -462,7 +482,7 @@ test("cloud api internal device-pod gateway dispatch is service-only and fail-cl
traceId: "trc_devicepod_dispatch_test",
input: { command: "node tools/device-host-cli.mjs health" }
}
}, null, { "x-hwlab-internal-service": "hwlab-device-pod" });
}, null, devicePodInternalHeaders());
assert.equal(blocked.status, 409);
assert.equal(blocked.body.blocker.code, "gateway_dispatch_unavailable");
} finally {
@@ -472,7 +492,7 @@ test("cloud api internal device-pod gateway dispatch is service-only and fail-cl
test("cloud api internal device-pod gateway dispatch uses gateway poll result", async () => {
const server = createCloudApiServer({
env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_ENVIRONMENT: "v02" },
env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_ENVIRONMENT: "v02", HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN },
now: () => "2026-05-28T00:00:00.000Z"
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
@@ -498,7 +518,7 @@ test("cloud api internal device-pod gateway dispatch uses gateway poll result",
traceId: "trc_devicepod_dispatch_test",
input: { command: "node tools/device-host-cli.mjs health", timeoutMs: 1000 }
}
}, null, { "x-hwlab-internal-service": "hwlab-device-pod" });
}, null, devicePodInternalHeaders());
const queued = await postJson(port, "/v1/gateway/poll", { gatewayId: "gtw_devicepod_test", gatewaySessionId: "gws_devicepod_test" });
assert.equal(queued.status, 200);
@@ -717,6 +737,10 @@ async function postJson(port, path, body, cookie = null, extraHeaders = {}) {
};
}
function devicePodInternalHeaders(extra = {}) {
return { ...extra, "x-hwlab-internal-service": "hwlab-device-pod", "x-hwlab-internal-token": INTERNAL_TOKEN };
}
async function getJson(port, path, cookie = null) {
const response = await fetch(`http://127.0.0.1:${port}${path}`, {
headers: cookie ? { cookie } : {}
+5 -3
View File
@@ -140,6 +140,7 @@ class AccessController {
this.gatewayRegistry = gatewayRegistry;
this.fetchImpl = fetchImpl;
this.devicePodExecutorUrl = normalizeBaseUrl(devicePodExecutorUrl);
this.devicePodInternalToken = textOr(env.HWLAB_DEVICE_POD_INTERNAL_TOKEN, "");
this.devicePodExecutorTimeoutMs = Number.parseInt(String(devicePodExecutorTimeoutMs), 10) || 1200;
this.now = now;
this.required = required;
@@ -783,7 +784,7 @@ class AccessController {
headers: {
accept: "application/json",
"content-type": "application/json",
"x-hwlab-internal-service": CLOUD_API_SERVICE_ID
...devicePodInternalHeaders(CLOUD_API_SERVICE_ID, this.devicePodInternalToken)
},
body: stableJson({
jobId: job.id,
@@ -852,7 +853,7 @@ class AccessController {
try {
const response = await fetchJsonWithTimeout(this.fetchImpl, target, {
method: "GET",
headers: { accept: "application/json", "x-hwlab-internal-service": CLOUD_API_SERVICE_ID }
headers: { accept: "application/json", ...devicePodInternalHeaders(CLOUD_API_SERVICE_ID, this.devicePodInternalToken) }
}, this.devicePodExecutorTimeoutMs);
return await this.updateDevicePodJobFromExecutorResponse({ job, pod, response });
} catch (error) {
@@ -865,7 +866,7 @@ class AccessController {
try {
const response = await fetchJsonWithTimeout(this.fetchImpl, target, {
method: "POST",
headers: { accept: "application/json", "x-hwlab-internal-service": CLOUD_API_SERVICE_ID }
headers: { accept: "application/json", ...devicePodInternalHeaders(CLOUD_API_SERVICE_ID, this.devicePodInternalToken) }
}, this.devicePodExecutorTimeoutMs);
return await this.updateDevicePodJobFromExecutorResponse({ job, pod, response });
} catch (error) {
@@ -1180,6 +1181,7 @@ function boundedDurationMs(value, fallback = 1000) { const parsed = Number.parse
function uartTailArgs(searchParams, uartId) { return { uartId, durationMs: boundedDurationMs(searchParams.get("durationMs") ?? searchParams.get("duration-ms")), maxBytes: boundedOutputMaxBytes(searchParams) }; }
function safeAgentSessionId(value) { const text = textOr(value, ""); return /^ses_[A-Za-z0-9_.:-]+$/u.test(text) ? text : ""; }
function leaseTokenFromRequest(request, body) { return textOr(body?.leaseToken, "") || textOr(getHeader(request, "x-hwlab-device-lease-token"), ""); }
function devicePodInternalHeaders(serviceId, token) { return token ? { "x-hwlab-internal-service": serviceId, "x-hwlab-internal-token": token } : { "x-hwlab-internal-service": serviceId }; }
function hashPassword(password) { const salt = randomBytes(16).toString("hex"); return `sha256:${salt}:${sha256(`${salt}:${password}`)}`; }
function verifyPassword(stored, password) { const [, salt, digest] = String(stored ?? "").split(":"); return Boolean(salt && digest && sha256(`${salt}:${password}`) === digest); }
function sha256(value) { return createHash("sha256").update(String(value)).digest("hex"); }
+8 -1
View File
@@ -475,7 +475,7 @@ async function handleInternalDevicePodGatewayDispatch(request, response, options
sendJson(response, 405, { error: { code: "method_not_allowed", message: "Internal device-pod gateway dispatch only supports POST" } });
return;
}
if (internalServiceHeader(request) !== "hwlab-device-pod") {
if (!isDevicePodInternalCaller(request, options.env ?? process.env)) {
sendJson(response, 403, { error: { code: "device_pod_internal_authority_required", message: "Only hwlab-device-pod may use the internal gateway dispatch route" } });
return;
}
@@ -581,6 +581,13 @@ function internalServiceHeader(request) {
return String(getHeader(request, "x-hwlab-internal-service") ?? "").trim();
}
function isDevicePodInternalCaller(request, env = process.env) {
const token = String(env.HWLAB_DEVICE_POD_INTERNAL_TOKEN ?? "").trim();
return Boolean(token)
&& internalServiceHeader(request) === "hwlab-device-pod"
&& String(getHeader(request, "x-hwlab-internal-token") ?? "").trim() === token;
}
async function handleGatewayPollHttp(request, response, options) {
const body = await readBody(request, options.bodyLimitBytes);
let params = {};