test: harden M3 identity wiring guards

This commit is contained in:
Code Queue Review
2026-05-23 07:13:49 +00:00
parent 039e81731a
commit 583acd86c6
6 changed files with 259 additions and 42 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+35 -12
View File
@@ -37,6 +37,7 @@ const m3FailureClassifications = Object.freeze({
targetMissing: "target_missing",
identityNotDistinct: "identity_not_distinct",
patchPanelWiringMissing: "patch_panel_wiring_missing",
patchPanelRouteOwnerInvalid: "patch_panel_route_owner_invalid",
operationFailed: "operation_failed",
evidenceMissing: "evidence_missing"
});
@@ -380,7 +381,8 @@ function serviceByName(services) {
}
async function collectReadOnlySupplementalEvidence() {
const [workloads, services] = await Promise.all([
const [deploy, workloads, services] = await Promise.all([
readFile(path.join(repoRoot, "deploy/deploy.json"), "utf8").then(JSON.parse),
readFile(path.join(repoRoot, deployWorkloadsPath), "utf8").then(JSON.parse),
readFile(path.join(repoRoot, deployServicesPath), "utf8").then(JSON.parse)
]);
@@ -403,6 +405,7 @@ async function collectReadOnlySupplementalEvidence() {
const box = observed.find((item) => item.name === "hwlab-box-simu");
const gateway = observed.find((item) => item.name === "hwlab-gateway-simu");
const patchPanel = observed.find((item) => item.name === "hwlab-patch-panel");
const patchPanelManifest = (deploy.services ?? []).find((service) => service.serviceId === "hwlab-patch-panel");
const boxIds = splitList(box?.env.HWLAB_BOX_ID);
const boxResourceIds = splitList(box?.env.HWLAB_BOX_RESOURCE_ID);
const boxGatewaySessionIds = splitList(box?.env.HWLAB_GATEWAY_SESSION_ID);
@@ -451,11 +454,11 @@ async function collectReadOnlySupplementalEvidence() {
]);
const patchPanelReadyForM3 =
patchPanel?.replicas === 1 &&
patchPanel?.m3Route?.fromResourceId === requiredM3Connection.fromResourceId &&
patchPanel?.m3Route?.fromPort === requiredM3Connection.fromPort &&
patchPanel?.m3Route?.patchPanelServiceId === "hwlab-patch-panel" &&
patchPanel?.m3Route?.toResourceId === requiredM3Connection.toResourceId &&
patchPanel?.m3Route?.toPort === requiredM3Connection.toPort &&
patchPanelManifest?.m3Route?.fromResourceId === requiredM3Connection.fromResourceId &&
patchPanelManifest?.m3Route?.fromPort === requiredM3Connection.fromPort &&
patchPanelManifest?.m3Route?.patchPanelServiceId === "hwlab-patch-panel" &&
patchPanelManifest?.m3Route?.toResourceId === requiredM3Connection.toResourceId &&
patchPanelManifest?.m3Route?.toPort === requiredM3Connection.toPort &&
patchPanelEndpointMap.res_boxsimu_2 === "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201" &&
patchPanelHasManifestRoute;
const manifestReadyForM3 = boxReadyForM3 && gatewayReadyForM3 && patchPanelReadyForM3 && hasInstanceServices;
@@ -465,7 +468,7 @@ async function collectReadOnlySupplementalEvidence() {
id: "deploy-skeleton-m3-cardinality",
status: manifestReadyForM3 ? "manifest-ready" : "gap",
blockerClass: manifestReadyForM3 ? null : "runtime_blocker",
source: [deployWorkloadsPath, deployServicesPath],
source: ["deploy/deploy.json", deployWorkloadsPath, deployServicesPath],
summary: manifestReadyForM3
? "Checked-in DEV deploy skeleton declares distinct indexed M3 identities for two box simulators, two gateway simulators, one patch panel, and the M3 DO1 -> DI1 endpoint map."
: "Read-only static comparison found the checked-in DEV deploy skeleton does not declare distinct indexed M3 simulator identities and patch-panel M3 wiring; proving live DEV still requires a separate deploy/runtime task or authorized runtime observation.",
@@ -487,6 +490,7 @@ async function collectReadOnlySupplementalEvidence() {
},
"hwlab-patch-panel": {
replicas: patchPanel?.replicas ?? null,
firstClassRoute: patchPanelManifest?.m3Route ?? null,
env: patchPanel?.env ?? null,
firstClassWiringConfig: patchPanelWiringConfig
},
@@ -991,6 +995,12 @@ function formatConnection(connection) {
function hasRequiredM3Connection({ status, wiring }) {
const activeConnections = status?.activeConnections ?? [];
const wiringConnections = wiring?.connections ?? [];
const owner = Boolean(
status?.serviceId === "hwlab-patch-panel" &&
status?.metadata?.propagation === "patch-panel-only" &&
wiring?.constraints?.propagation === "patch-panel-only" &&
wiring?.constraints?.localLoopbackSubstituteAllowed === false
);
const active = activeConnections.find(
(connection) =>
connection.fromResourceId === requiredM3Connection.fromResourceId &&
@@ -1006,7 +1016,7 @@ function hasRequiredM3Connection({ status, wiring }) {
connection.to?.port === requiredM3Connection.toPort &&
connection.mode === "exclusive"
);
return { active, configured };
return { active, configured, owner };
}
async function probeDirectCandidate(candidate) {
@@ -1105,16 +1115,21 @@ async function resolveDirectM3Targets({ environmentTargets, kubernetesDiscovery
});
}
if (patch && (!m3Connection.active || !m3Connection.configured)) {
if (patch && (!m3Connection.active || !m3Connection.configured || !m3Connection.owner)) {
const observedActive = (patch.status.json?.activeConnections ?? []).map(formatConnection).join(", ") || "none";
const observedConfigured = (patchWiring?.json?.connections ?? []).map(formatConnection).join(", ") || "none";
const ownerSummary = `serviceId=${patch.status.json?.serviceId ?? "unknown"} propagation=${patch.status.json?.metadata?.propagation ?? "unknown"} wiringPropagation=${patchWiring?.json?.constraints?.propagation ?? "unknown"} loopbackAllowed=${String(patchWiring?.json?.constraints?.localLoopbackSubstituteAllowed ?? "unknown")}`;
blockers.push({
type: "runtime_blocker",
scope: "m3-patch-panel-wiring",
status: "open",
classification: classifyDirectTargetBlockerScope("m3-patch-panel-wiring"),
classification: m3Connection.owner
? classifyDirectTargetBlockerScope("m3-patch-panel-wiring")
: m3FailureClassifications.patchPanelRouteOwnerInvalid,
sourceIssue: "pikasTech/HWLAB#64",
summary: `DEV patch-panel is callable, but live wiring does not contain ${requiredM3Connection.fromResourceId}:${requiredM3Connection.fromPort} -> ${requiredM3Connection.toResourceId}:${requiredM3Connection.toPort}; active=${observedActive}; configured=${observedConfigured}.`
summary: m3Connection.owner
? `DEV patch-panel is callable, but live wiring does not contain ${requiredM3Connection.fromResourceId}:${requiredM3Connection.fromPort} -> ${requiredM3Connection.toResourceId}:${requiredM3Connection.toPort}; active=${observedActive}; configured=${observedConfigured}.`
: `DEV patch-panel route owner is not the required hwlab-patch-panel patch-panel-only owner; ${ownerSummary}; active=${observedActive}; configured=${observedConfigured}.`
});
}
@@ -1125,7 +1140,8 @@ async function resolveDirectM3Targets({ environmentTargets, kubernetesDiscovery
selectedGateways.length >= 2 &&
patch &&
m3Connection.active &&
m3Connection.configured;
m3Connection.configured &&
m3Connection.owner;
const targets = new Map();
if (ok) {
@@ -1163,6 +1179,13 @@ async function resolveDirectM3Targets({ environmentTargets, kubernetesDiscovery
configuredObserved: (patchWiring?.json?.connections ?? []).map(formatConnection),
hasRequiredActiveConnection: Boolean(m3Connection.active),
hasRequiredConfiguredConnection: Boolean(m3Connection.configured),
hasRequiredRouteOwner: Boolean(m3Connection.owner),
routeOwnerObserved: {
serviceId: patch.status.json?.serviceId ?? null,
propagation: patch.status.json?.metadata?.propagation ?? null,
wiringPropagation: patchWiring?.json?.constraints?.propagation ?? null,
localLoopbackSubstituteAllowed: patchWiring?.json?.constraints?.localLoopbackSubstituteAllowed ?? null
},
configSource: patch.status.json?.metadata?.configSource ?? "unknown",
syncableConnectionCount: patch.status.json?.metadata?.syncableConnectionCount ?? null
}
+85 -2
View File
@@ -1,6 +1,6 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdir, readFile, rm } from "node:fs/promises";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
@@ -68,6 +68,44 @@ test("--dry-run aliases the same source/read-only no-write path", async () => {
}
});
test("M3 report validator rejects DRY-RUN evidence promoted to live pass", async () => {
await mkdir(path.join(repoRoot, testOutputDir), { recursive: true });
const output = path.join(testOutputDir, "dry-run-promoted-live.json");
const promoted = path.join(testOutputDir, "dry-run-promoted-live-tampered.json");
try {
runSmoke(["--dry-run", "--output", output]);
const report = JSON.parse(await readFile(path.join(repoRoot, output), "utf8"));
report.liveOperation = {
status: "pass",
operationId: "op_fake_dry_run",
traceId: "trc_fake_dry_run",
auditId: "aud_fake_dry_run",
evidenceId: "evd_fake_dry_run",
summary: "Incorrectly promoted DRY-RUN output to DEV-LIVE."
};
await writeFile(path.join(repoRoot, promoted), JSON.stringify(report, null, 2));
assert.throws(
() =>
execFileSync("node", ["scripts/validate-dev-gate-report.mjs", promoted], {
cwd: repoRoot,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"]
}),
(error) => {
assert.match(
`${error.message}\n${error.stderr ?? ""}`,
/liveOperation\.status cannot be pass when only a DRY-RUN plan is present/u
);
return true;
}
);
} finally {
await rm(path.join(repoRoot, output), { force: true });
await rm(path.join(repoRoot, promoted), { force: true });
}
});
test("M3 safety gates keep incomplete live commands source/read-only", () => {
assert.deepEqual(requireSafetyGates(parseArgs([])), {
mode: "dry-run",
@@ -103,12 +141,17 @@ test("M3 blocker scopes classify missing identities and patch-panel route gaps",
assert.equal(plan.liveWriteWillRun, false);
assert.ok(plan.failureClassifications.includes("identity_not_distinct"));
assert.ok(plan.failureClassifications.includes("patch_panel_wiring_missing"));
assert.ok(plan.failureClassifications.includes("patch_panel_route_owner_invalid"));
assert.match(plan.refusalPolicy.join("\n"), /source\/read-only/u);
});
test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsimu_2 DI1 wiring", () => {
test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsimu_2 DI1 wiring or wrong owner", () => {
const wrongRoute = hasRequiredM3Connection({
status: {
serviceId: "hwlab-patch-panel",
metadata: {
propagation: "patch-panel-only"
},
activeConnections: [
{
fromResourceId: "res_boxsimu_1",
@@ -119,6 +162,10 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
]
},
wiring: {
constraints: {
propagation: "patch-panel-only",
localLoopbackSubstituteAllowed: false
},
connections: [
{
from: { resourceId: "res_boxsimu_1", port: "DO1" },
@@ -130,9 +177,14 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
});
assert.equal(Boolean(wrongRoute.active), false);
assert.equal(Boolean(wrongRoute.configured), false);
assert.equal(Boolean(wrongRoute.owner), true);
const requiredRoute = hasRequiredM3Connection({
status: {
serviceId: "hwlab-patch-panel",
metadata: {
propagation: "patch-panel-only"
},
activeConnections: [
{
fromResourceId: "res_boxsimu_1",
@@ -143,6 +195,10 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
]
},
wiring: {
constraints: {
propagation: "patch-panel-only",
localLoopbackSubstituteAllowed: false
},
connections: [
{
from: { resourceId: "res_boxsimu_1", port: "DO1" },
@@ -154,6 +210,33 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
});
assert.equal(Boolean(requiredRoute.active), true);
assert.equal(Boolean(requiredRoute.configured), true);
assert.equal(Boolean(requiredRoute.owner), true);
const wrongOwner = hasRequiredM3Connection({
status: {
serviceId: "hwlab-box-simu",
metadata: {
propagation: "box-loopback"
},
activeConnections: requiredRoute.active ? [requiredRoute.active] : []
},
wiring: {
constraints: {
propagation: "patch-panel-only",
localLoopbackSubstituteAllowed: false
},
connections: [
{
from: { resourceId: "res_boxsimu_1", port: "DO1" },
to: { resourceId: "res_boxsimu_2", port: "DI1" },
mode: "exclusive"
}
]
}
});
assert.equal(Boolean(wrongOwner.active), true);
assert.equal(Boolean(wrongOwner.configured), true);
assert.equal(Boolean(wrongOwner.owner), false);
});
test("Kubernetes discovery preserves current indexed M3 service identities", () => {
+14
View File
@@ -2644,6 +2644,20 @@ async function validateDevM3Report(report, label) {
for (const field of ["operationId", "traceId", "auditId", "evidenceId", "summary"]) {
assertString(report.liveOperation[field], `${label}.liveOperation.${field}`);
}
if (Object.hasOwn(report, "dryRunPlan")) {
assert.notEqual(
report.liveOperation.status,
"pass",
`${label}.liveOperation.status cannot be pass when only a DRY-RUN plan is present`
);
for (const field of ["operationId", "traceId", "auditId", "evidenceId"]) {
assert.equal(
report.liveOperation[field],
"not_observed",
`${label}.liveOperation.${field} must remain not_observed for DRY-RUN evidence`
);
}
}
assertArray(report.blockers, `${label}.blockers`);
assertUnique(
+45 -27
View File
@@ -2,7 +2,7 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { fileURLToPath, pathToFileURL } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const namespace = "hwlab-dev";
@@ -301,32 +301,50 @@ function assertM3RouteConfig(value, label) {
assert.equal(value.constraints.localLoopbackSubstituteAllowed, false, `${label} loopback substitute`);
}
const [deploy, workloads, services, topology] = await Promise.all([
readJson("deploy/deploy.json"),
readJson("deploy/k8s/base/workloads.yaml"),
readJson("deploy/k8s/base/services.yaml"),
readJson("fixtures/mvp/m3-hardware-loop/topology.json")
]);
export async function readDevM3CardinalityInputs() {
const [deploy, workloads, services, topology] = await Promise.all([
readJson("deploy/deploy.json"),
readJson("deploy/k8s/base/workloads.yaml"),
readJson("deploy/k8s/base/services.yaml"),
readJson("fixtures/mvp/m3-hardware-loop/topology.json")
]);
const patchPanelWorkloadEnv = envMap(
workloadByName(workloads)
.get("hwlab-patch-panel")
?.spec?.template?.spec?.containers?.find((item) => item.name === "hwlab-patch-panel")
?.env
);
return { deploy, workloads, services, topology };
}
assertDeployManifestCardinality(deploy);
assertDeployManifestInstanceMappings(deploy);
assertDevK8sCardinality(workloads);
assertDevK8sInstanceServices(services);
assertM3FixtureWiring(topology);
assertM3RouteConfig(
parseEnvValue(patchPanelWorkloadEnv.HWLAB_PATCH_PANEL_WIRING_CONFIG),
"DEV workload first-class M3 patch-panel wiring config"
);
assertM3RouteConfig(
parseEnvValue(deploy.services.find((service) => service.serviceId === "hwlab-patch-panel")?.env?.HWLAB_PATCH_PANEL_WIRING_CONFIG),
"deploy manifest first-class M3 patch-panel wiring config"
);
export function validateDevM3Cardinality({ deploy, workloads, services, topology }) {
const patchPanelWorkloadEnv = envMap(
workloadByName(workloads)
.get("hwlab-patch-panel")
?.spec?.template?.spec?.containers?.find((item) => item.name === "hwlab-patch-panel")
?.env
);
console.log("validated DEV M3 manifest cardinality: 2 distinct box-simu, 2 distinct gateway-simu, 1 patch-panel, M3 DO1->DI1 wiring");
assertDeployManifestCardinality(deploy);
assertDeployManifestInstanceMappings(deploy);
assertDevK8sCardinality(workloads);
assertDevK8sInstanceServices(services);
assertM3FixtureWiring(topology);
assertM3RouteConfig(
parseEnvValue(patchPanelWorkloadEnv.HWLAB_PATCH_PANEL_WIRING_CONFIG),
"DEV workload first-class M3 patch-panel wiring config"
);
assertM3RouteConfig(
parseEnvValue(deploy.services.find((service) => service.serviceId === "hwlab-patch-panel")?.env?.HWLAB_PATCH_PANEL_WIRING_CONFIG),
"deploy manifest first-class M3 patch-panel wiring config"
);
}
export async function main() {
validateDevM3Cardinality(await readDevM3CardinalityInputs());
console.log("validated DEV M3 manifest cardinality: 2 distinct box-simu, 2 distinct gateway-simu, 1 patch-panel, M3 DO1->DI1 wiring");
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
try {
await main();
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
}
}
@@ -0,0 +1,79 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
readDevM3CardinalityInputs,
validateDevM3Cardinality
} from "./validate-dev-m3-cardinality.mjs";
const baseline = await readDevM3CardinalityInputs();
function clone(value) {
return JSON.parse(JSON.stringify(value));
}
function cloneInputs() {
return clone(baseline);
}
function deployService(inputs, serviceId) {
return inputs.deploy.services.find((service) => service.serviceId === serviceId);
}
function workloadContainer(inputs, workloadName) {
return inputs.workloads.items
.find((item) => item.metadata?.name === workloadName)
?.spec?.template?.spec?.containers?.find((container) => container.name === workloadName);
}
function setWorkloadEnv(inputs, workloadName, envName, value) {
const entry = workloadContainer(inputs, workloadName)?.env?.find((item) => item.name === envName);
assert.ok(entry, `${workloadName} ${envName} exists`);
entry.value = value;
}
function assertRejected(inputs, pattern) {
assert.throws(() => validateDevM3Cardinality(inputs), pattern);
}
test("accepts the checked-in DEV M3 minimum topology", () => {
validateDevM3Cardinality(cloneInputs());
});
test("rejects duplicate box-simu resource identity in deploy source", () => {
const inputs = cloneInputs();
deployService(inputs, "hwlab-box-simu").env.HWLAB_BOX_RESOURCE_ID = "res_boxsimu_1,res_boxsimu_1";
assertRejected(inputs, /hwlab-box-simu deploy HWLAB_BOX_RESOURCE_ID/u);
});
test("rejects duplicate gateway-simu identity in Kubernetes source", () => {
const inputs = cloneInputs();
setWorkloadEnv(inputs, "hwlab-gateway-simu", "HWLAB_GATEWAY_ID", "gwsimu_1,gwsimu_1");
assertRejected(inputs, /hwlab-gateway-simu HWLAB_GATEWAY_ID/u);
});
test("rejects duplicate gateway session assignment for box-simu resources", () => {
const inputs = cloneInputs();
deployService(inputs, "hwlab-box-simu").env.HWLAB_GATEWAY_SESSION_ID = "gws_gwsimu_1,gws_gwsimu_1";
assertRejected(inputs, /hwlab-box-simu deploy HWLAB_GATEWAY_SESSION_ID/u);
});
test("rejects missing M3 patch-panel route", () => {
const inputs = cloneInputs();
const patchPanel = deployService(inputs, "hwlab-patch-panel");
const wiringConfig = JSON.parse(patchPanel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG);
wiringConfig.connections = [];
patchPanel.env.HWLAB_PATCH_PANEL_WIRING_CONFIG = JSON.stringify(wiringConfig);
assertRejected(inputs, /hwlab-patch-panel deploy HWLAB_PATCH_PANEL_WIRING_CONFIG/u);
});
test("rejects wrong first-class route owner", () => {
const inputs = cloneInputs();
deployService(inputs, "hwlab-patch-panel").m3Route.patchPanelServiceId = "hwlab-box-simu";
assertRejected(inputs, /hwlab-patch-panel deploy first-class M3 route/u);
});