#!/usr/bin/env node const assert = require("assert"); const fs = require("fs"); const path = require("path"); const repo = path.resolve(__dirname, ".."); function read(relativePath) { return fs.readFileSync(path.join(repo, relativePath), "utf8"); } function maybeRead(segments) { const fullPath = path.join(repo, ...segments); if (!fs.existsSync(fullPath)) return null; return fs.readFileSync(fullPath, "utf8"); } function expect(source, name, pattern) { assert.match(source, pattern, `missing tenant-isolation evidence: ${name}`); } const auth = read("crates/clusterflux-core/src/auth.rs"); const artifact = read("crates/clusterflux-core/src/artifact.rs"); const operatorPanel = read("crates/clusterflux-core/src/operator_panel.rs"); const source = read("crates/clusterflux-core/src/source.rs"); const coordinatorService = [ read("crates/clusterflux-coordinator/src/service.rs"), read("crates/clusterflux-coordinator/src/service/routing.rs"), read("crates/clusterflux-coordinator/src/service/nodes.rs"), read("crates/clusterflux-coordinator/src/service/keys.rs"), read("crates/clusterflux-coordinator/src/service/artifacts.rs"), read("crates/clusterflux-coordinator/src/service/processes.rs"), read("crates/clusterflux-coordinator/src/service/process_launch.rs"), read("crates/clusterflux-coordinator/src/service/tests.rs"), ].join("\n"); const artifactDownloadSmoke = read("scripts/artifact-download-smoke.js"); const operatorPanelSmoke = read("scripts/operator-panel-smoke.js"); const schedulerSmoke = read("scripts/scheduler-placement-smoke.js"); const sourcePreparationSmoke = read("scripts/source-preparation-smoke.js"); const liveSmoke = read("scripts/cli-happy-path-live-smoke.js"); for (const [name, pattern] of [ ["auth contexts carry tenant and project", /pub struct AuthContext[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId/], ["enrollment grants carry tenant and project", /pub struct EnrollmentGrant[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId/], ["node credentials carry tenant and project", /pub struct NodeCredential[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId/], ["same tenant/project denies tenant mismatch", /pub fn same_tenant_project[\s\S]*tenant mismatch/], ["same tenant/project denies project mismatch", /pub fn same_tenant_project[\s\S]*project mismatch/], ["auth unit denies cross-tenant access", /fn tenant_project_scope_denies_cross_tenant_access\(\)/], ]) { expect(auth, name, pattern); } for (const [name, pattern] of [ ["artifact metadata carries tenant and project", /pub struct ArtifactMetadata[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId/], ["download links carry tenant project process and actor", /pub struct DownloadLink[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId[\s\S]*pub process: ProcessId[\s\S]*pub actor: Actor/], ["downloads authorize against scoped metadata", /let authz = same_tenant_project\(context, &scope\)/], ["artifact test denies cross-tenant download", /fn cross_tenant_download_is_denied_even_with_known_artifact_id\(\)/], ["artifact test denies cross-project download", /fn cross_project_download_is_denied_even_with_known_artifact_id\(\)/], ]) { expect(artifact, name, pattern); } for (const [name, pattern] of [ ["panel state carries tenant project and process", /pub struct PanelState[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId[\s\S]*pub process: ProcessId/], ["panel events carry tenant project and process", /pub struct PanelEvent[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId[\s\S]*pub process: ProcessId/], ["panel events reject scope mismatch", /PanelError::ScopeMismatch/], ["panel scope validation compares tenant project process", /self\.tenant != event\.tenant[\s\S]*self\.project != event\.project[\s\S]*self\.process != event\.process/], ]) { expect(operatorPanel, name, pattern); } expect( source, "source preparation carries tenant and project", /pub struct SourcePreparation[\s\S]*pub tenant: TenantId[\s\S]*pub project: ProjectId/ ); for (const [name, pattern] of [ ["project creation rejects foreign tenant reuse", /project id is outside the signed-in tenant scope/], ["project selection rejects foreign tenant", /project is outside the signed-in tenant scope/], ["project listing uses tenant-scoped context", /CoordinatorRequest::ListProjects[\s\S]*list_projects\(&context\)/], [ "node capability reports resolve the full enrolled scope", /NodeScopeKey::from_refs\(&tenant, &project, &node\)[\s\S]*node_identity\(&tenant, &project, &node\)/, ], ["operator panel stop state is tenant/project/process keyed", /type PanelStopKey = \(TenantId, ProjectId, ProcessId\)/], ["download service hides cross-tenant artifact existence", /cross_tenant\.to_string\(\)\.contains\("does not exist"\)/], ["download service hides cross-project artifact existence", /cross_project\.to_string\(\)\.contains\("does not exist"\)/], ["node capability test rejects cross-scope report", /fn service_rejects_node_capability_report_outside_enrollment_scope\(\)/], ["source preparation test rejects cross-scope completion", /fn service_rejects_source_preparation_completion_outside_node_scope\(\)/], ]) { expect(coordinatorService, name, pattern); } for (const [name, sourceText, patterns] of [ [ "artifact download smoke", artifactDownloadSmoke, [ /const crossTenant = await send/, /const crossProject = await send/, /const crossTenantOpen = await send/, /const crossProjectOpen = await send/, /artifact does not exist/, /token is invalid/, ], ], [ "operator panel smoke", operatorPanelSmoke, [/const crossTenant = await send/, /render_operator_panel/, /scope\|tenant\|project/], ], [ "scheduler and capability smoke", schedulerSmoke, [ /const crossScopeInspection = await send/, /assert\.strictEqual\(crossScopeInspection\.descriptors\.length, 0\)/, /const crossTenantReport = await send/, /tenant\\\/project scope/, ], ], [ "source preparation smoke", sourcePreparationSmoke, [/const crossTenantCompletion = await send/, /complete_source_preparation/, /tenant\\\/project scope/i], ], ]) { for (const pattern of patterns) { expect(sourceText, name, pattern); } } for (const [name, pattern] of [ [ "live same-ID collision refreshes first-tenant metadata before the second build", /const firstCollisionHelloBuild = await runHostedHelloBuild[\s\S]*secondHelloBuild = await runHostedHelloBuild/, ], [ "live same-ID collision re-reads the fresh first-tenant process", /"--process",[\s\S]*firstCollisionHelloBuild\.process[\s\S]*candidate\.artifact === firstCollisionHelloBuild\.artifact/, ], ]) { expect(liveSmoke, name, pattern); } const hostedLibRoot = maybeRead(["private", "hosted-policy", "src", "lib.rs"]); const hostedServiceSource = maybeRead([ "private", "hosted-policy", "src", "bin", "clusterflux-hosted-service.rs", ]); const hostedLib = hostedLibRoot; const hostedSmoke = maybeRead([ "private", "hosted-policy", "scripts", "hosted-client-compat-smoke.js", ]); if (hostedLib && hostedServiceSource && hostedSmoke) { for (const [name, pattern] of [ ["hosted private layer is a compact identity broker", /pub struct HostedIdentityBroker[\s\S]*cli_session_ttl_seconds/], ["hosted private layer has no parallel coordinator", /Runtime, persistence, nodes, processes,[\s\S]*remain owned by public CoordinatorService/], ["hosted service delegates Client state to Core", /core_coordinator: CoordinatorService/], ["hosted login creates project through Core", /issue_cli_session[\s\S]*AuthenticatedCoordinatorRequest::CreateProject/], ]) { expect(`${hostedLib}\n${hostedServiceSource}`, name, pattern); } for (const forbidden of [ "HostedCommunityControlPlane", "HostedObservabilitySnapshot", "agent_public_keys: BTreeMap", "node_statuses: BTreeMap", "process_statuses: BTreeMap", "debug_sessions: BTreeMap", ]) { assert( !hostedLib.includes(forbidden), `private hosted policy must not reimplement Core state: ${forbidden}` ); } for (const [name, pattern] of [ ["client-supplied identity is denied", /const forged = await sendHostedControl[\s\S]*authenticated CLI session/], ["second OIDC subject receives a different tenant", /assert\.notStrictEqual\(victimLogin\.session\.tenant, session\.tenant\)/], ["cross-tenant process events are denied", /const crossTenantTaskEventsDenied = await sendHostedControl[\s\S]*vp-victim[\s\S]*scope\|denied\|unauthorized/], ]) { expect(hostedSmoke, name, pattern); } } console.log("Tenant isolation contract smoke passed");