fix: lock verified Runtime public keys in settings

This commit is contained in:
2026-09-13 00:23:25 +09:00
parent 33a2b5d702
commit 2528312142
9 changed files with 515 additions and 15 deletions
@@ -129,8 +129,29 @@ Deno.test("Runtime detail keeps trust controls owner-only and conflict-safe", as
const ownerGate = page.indexOf("data.workspace.permissions.manage_runtimes");
const reveal = page.indexOf("Reveal public key");
const verifiedKey = page.indexOf("{#if verifiedTrust}");
const mutation = page.indexOf('id="runtime-public-key-input"');
const metadataStart = page.indexOf("async function saveRuntimeMetadata");
const trustStart = page.indexOf("async function saveTrustKey");
const metadataMutation = page.slice(metadataStart, trustStart);
assert(ownerGate >= 0, "Runtime trust controls should use manage_runtimes");
assert(
verifiedKey >= 0 && verifiedKey < mutation &&
page.slice(verifiedKey, mutation).includes("{:else}"),
"verified Runtime public key must be read-only while unverified trust keeps key input",
);
assert(
metadataMutation.includes("updateRemoteRuntime(") &&
metadataMutation.includes("display_name: normalizedDisplayName") &&
metadataMutation.includes("endpoint: normalizedEndpoint"),
"mutable Runtime settings should use the metadata-only update request",
);
assert(
!metadataMutation.includes("publicKey") &&
!metadataMutation.includes("public_bundle") &&
!metadataMutation.includes("public_key"),
"verified Runtime metadata updates must not carry public key authority",
);
assert(
page.includes("Current fingerprint"),
"current fingerprint must be explicit",
@@ -168,6 +189,10 @@ Deno.test("Runtime detail keeps trust controls owner-only and conflict-safe", as
"routeFence.enter(data.runtimeId)",
"showPublicKey = false",
"revealedPublicKey = null",
"saveRuntimeMetadata",
"Runtime settings",
"Edit Runtime",
"This verified key is read-only",
"publicKey = ''",
"requestError = null",
"successMessage = null",
@@ -13,6 +13,7 @@ import {
revokeRuntimeTrustKey,
RuntimeTrustConflictError,
RuntimeTrustRouteFence,
updateRemoteRuntime,
} from "../src/lib/workspace/api/runtime-management.ts";
function assert(condition: unknown, message: string): asserts condition {
@@ -273,6 +274,45 @@ Deno.test("mismatched revoke fingerprint never sends a request", async () => {
assert(requests === 0, "mismatched fingerprint sent a revoke request");
});
Deno.test("Runtime metadata update never sends public key authority", async () => {
let requestedUrl = "";
let requestedMethod = "";
let requestedBody: unknown = null;
const fetchImpl = ((input: string | URL | Request, init?: RequestInit) => {
requestedUrl = String(input);
requestedMethod = init?.method ?? "GET";
requestedBody = JSON.parse(String(init?.body));
return Promise.resolve(Response.json(detail()));
}) as typeof fetch;
await updateRemoteRuntime(
"workspace-a",
"arcadia",
{
display_name: "Updated Runtime",
endpoint: "https://runtime.example.test/v2",
},
fetchImpl,
);
assert(
requestedUrl === "/api/w/workspace-a/runtimes/arcadia",
`unexpected update URL: ${requestedUrl}`,
);
assert(requestedMethod === "POST", "Runtime update must use POST");
assert(
JSON.stringify(requestedBody) ===
JSON.stringify({
display_name: "Updated Runtime",
endpoint: "https://runtime.example.test/v2",
}),
`unexpected update body: ${JSON.stringify(requestedBody)}`,
);
const body = requestedBody as Record<string, unknown>;
assert(!("public_bundle" in body), "metadata update sent public_bundle");
assert(!("public_key" in body), "metadata update sent public_key");
});
Deno.test("Runtime registration delete uses the Workspace-scoped resource route", async () => {
let requestedUrl = "";
let requestedMethod = "";