From 3d6d4b92064a74e600a3613991753179a9532509 Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 20:14:48 +0700 Subject: [PATCH 1/2] fix(evaos): expose Mac-control canary blockers --- .github/workflows/evaos-live-canary-proof.yml | 8 +- scripts/evaosBrokerLiveCanary.js | 173 ++++++++- scripts/evaosLiveCanaryReadiness.js | 2 + scripts/evaosProvisionLiveCanaryFixtures.js | 35 +- scripts/evaosScanMacControlProofs.js | 91 +++++ .../unit/evaos/evaosBrokerLiveCanary.test.ts | 346 ++++++++++++++++-- .../evaos/evaosLiveCanaryReadiness.test.ts | 3 + .../evaos/evaosLiveCanaryWorkflow.test.ts | 64 ++++ .../evaosProvisionLiveCanaryFixtures.test.ts | 60 ++- 9 files changed, 744 insertions(+), 38 deletions(-) diff --git a/.github/workflows/evaos-live-canary-proof.yml b/.github/workflows/evaos-live-canary-proof.yml index a5523ec9b59..7193b7b4553 100644 --- a/.github/workflows/evaos-live-canary-proof.yml +++ b/.github/workflows/evaos-live-canary-proof.yml @@ -258,7 +258,13 @@ jobs: if: always() && github.event.inputs.run_live_canaries == 'true' && github.event.inputs.run_mac_control_canary == 'true' && github.event.inputs.provision_fixtures == 'true' run: | set -euo pipefail - node scripts/evaosProvisionLiveCanaryFixtures.js cleanup-mac-control > "$PROOF_DIR/mac-control-session-cleanup.stdout.json" + cleanup_output="$PROOF_DIR/mac-control-session-cleanup.stdout.json" + cleanup_temp="${cleanup_output}.tmp" + rm -f "$cleanup_output" "$cleanup_temp" + trap 'rm -f "$cleanup_temp"' EXIT + node scripts/evaosProvisionLiveCanaryFixtures.js cleanup-mac-control > "$cleanup_temp" + mv "$cleanup_temp" "$cleanup_output" + trap - EXIT - name: Scan existing Mac-control proofs for safe diagnostic upload id: mac-control-partial-proof-scan diff --git a/scripts/evaosBrokerLiveCanary.js b/scripts/evaosBrokerLiveCanary.js index b8b345608fb..d894df43f58 100644 --- a/scripts/evaosBrokerLiveCanary.js +++ b/scripts/evaosBrokerLiveCanary.js @@ -20,7 +20,18 @@ const MAC_CONTROL_RUNTIME = 'openclaw'; const MAC_CONTROL_RUNTIME_RECEIPT_PATH = '/api/v1/evaos/mac-control/runtime-receipt'; const MAC_CONTROL_DEPLOYED_NEGATIVE_PROBE_SCHEMA = 'evaos.mac_control.deployed_negative_probe.v1'; const MAC_CONTROL_DEPLOYED_NEGATIVE_PROOF_MODE = 'deployed-staging'; +const MAC_CONTROL_FAILURE_PROOF_SCHEMA = 'evaos.mac_control.live_canary_failure.v1'; const MAC_CONTROL_LAUNCH_PRIVATE = Symbol('mac-control-launch-private'); +const MAC_CONTROL_FAILURE_PHASES = new Set([ + 'configuration', + 'network_readiness', + 'launch_http', + 'launch_contract', + 'callback', + 'runtime_receipt', + 'route_probe', + 'negative_probe', +]); const MAC_CONTROL_REQUIRED_CAPABILITY_GROUPS = Object.freeze([ Object.freeze(['customer_mac_status']), Object.freeze(['customer_mac_snapshot', 'desktop_see']), @@ -28,6 +39,8 @@ const MAC_CONTROL_REQUIRED_CAPABILITY_GROUPS = Object.freeze([ ]); const MAC_CONTROL_SAFE_REASON_CODES = new Set([ 'acl_stale', + 'authority_stale', + 'authority_unavailable', 'binding_ambiguous', 'binding_authority_unavailable', 'binding_expired', @@ -37,18 +50,29 @@ const MAC_CONTROL_SAFE_REASON_CODES = new Set([ 'connector_secret_missing', 'device_revoked', 'grant_revoked', + 'grant_binding_mismatch', 'headscale_acl_stale', 'invalid_configuration', 'invalid_response', 'mac_connector_material_missing', 'mac_node_offline', + 'mac_node_expired', + 'mac_node_missing', 'mac_node_unpaired', 'missing_required_capability', + 'multiple_active_customer_grants', + 'no_active_customer_grant', + 'policy_hash_mismatch', + 'policy_unavailable', 'runtime_launch_blocked', 'runtime_receipt_rejected', 'selected_customer_scope_mismatch', 'selected_scope_not_ready', + 'vm_node_expired', + 'vm_node_missing', + 'vm_node_offline', ]); +const UUID_PATTERN = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i; const DENIED_RUNTIME_PATTERN = /(denied|blocked|forbidden|unauthorized|expired|revoked|permission|mac_connector_material_missing|internal server error|internal_server_error|server_error)/i; @@ -167,6 +191,8 @@ function resolveMacControlCanaryConfig(env) { 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION', 'AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID', 'AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT', + 'AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT', + 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER', 'AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST', 'EVAOS_LIVE_CANARY_RECEIPT_KEY_ID', 'EVAOS_LIVE_CANARY_RECEIPT_PUBLIC_KEY', @@ -180,14 +206,33 @@ function resolveMacControlCanaryConfig(env) { const customerId = envText(env, 'AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID'); assertAllowedBrokerCanaryCustomerId(customerId); const endpoint = envText(env, 'AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT'); + const networkEndpoint = envText(env, 'AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT'); + const deviceIdentifier = envText(env, 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER'); let endpointUrl; + let networkEndpointUrl; try { endpointUrl = new URL(endpoint); + networkEndpointUrl = new URL(networkEndpoint); } catch { - throw new Error('Dedicated Mac-control canary endpoint must be an absolute HTTPS URL.'); + throw new Error('Dedicated Mac-control canary endpoints must be absolute HTTPS URLs.'); + } + if ( + endpointUrl.protocol !== 'https:' || + networkEndpointUrl.protocol !== 'https:' || + endpointUrl.username || + endpointUrl.password || + endpointUrl.hash || + networkEndpointUrl.username || + networkEndpointUrl.password || + networkEndpointUrl.search || + networkEndpointUrl.hash || + networkEndpointUrl.origin !== endpointUrl.origin || + networkEndpointUrl.pathname !== '/functions/v1/customer-mac-control' + ) { + throw new Error('Dedicated Mac-control canary endpoints must be same-origin absolute HTTPS URLs.'); } - if (endpointUrl.protocol !== 'https:') { - throw new Error('Dedicated Mac-control canary endpoint must be an absolute HTTPS URL.'); + if (!/^[^\s]{1,256}$/u.test(deviceIdentifier)) { + throw new Error('Dedicated Mac-control canary device identity is invalid.'); } const expectedCallbackHostInput = envText( @@ -229,6 +274,8 @@ function resolveMacControlCanaryConfig(env) { desktopSession: envText(env, 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION'), customerId, endpoint: endpointUrl.toString(), + networkEndpoint: networkEndpointUrl.toString(), + deviceIdentifier, expectedCallbackHost, receiptKeyId, receiptPublicKey, @@ -991,18 +1038,120 @@ function sanitizeMacControlRuntimeProof( } function failedMacControlProof(env, reason, extras = {}) { + const source = macControlProofSource(env); + const phase = MAC_CONTROL_FAILURE_PHASES.has(extras.phase) ? extras.phase : 'configuration'; return { - schema: 'evaos-mac-control-live-canary/v1', + schema: MAC_CONTROL_FAILURE_PROOF_SCHEMA, ok: false, runtime: MAC_CONTROL_RUNTIME, launchMode: 'mac_control_tools', reason: macControlReason(reason, 'invalid_response'), - ...macControlProofSource(env), - ...(Number.isInteger(extras.httpStatus) ? { httpStatus: extras.httpStatus } : {}), + phase, + httpStatus: Number.isInteger(extras.httpStatus) ? extras.httpStatus : null, + sourceHeadSha: source.sourceHeadSha ?? null, + sourceRunId: source.sourceRunId ?? null, secretScan: 'passed', }; } +async function refreshMacControlPrivateNetworkReadiness(fetchImpl, config, now) { + const response = await fetchImpl(config.networkEndpoint, { + method: 'POST', + headers: { + Authorization: `Bearer ${config.desktopSession}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + action: 'get_private_network_readiness', + customer_id: config.customerId, + device_identifier: config.deviceIdentifier, + }), + }); + let body; + try { + body = await response.json(); + } catch { + throw macControlFailure('invalid_response', 'Mac-control private-network readiness was unreadable.', { + httpStatus: response.status, + }); + } + const record = asPlainRecord(body); + if (!response.ok) { + throw macControlFailure( + record?.code ?? record?.error_code ?? record?.reason ?? record?.error, + 'Mac-control private-network readiness refresh was blocked.', + { httpStatus: response.status } + ); + } + try { + assertNoSecretMaterial(record); + } catch { + throw macControlFailure('invalid_response', 'Mac-control private-network readiness exposed unsafe material.', { + httpStatus: response.status, + }); + } + const fields = [ + 'ok', + 'customer_id', + 'device_id', + 'device_identifier', + 'enrollment_id', + 'grant_id', + 'correct_control_plane', + 'acl_allowed', + 'online', + 'reason', + 'observed_at', + 'expires_at', + 'audit_id', + ]; + const observedAt = Date.parse(record?.observed_at); + const expiresAt = Date.parse(record?.expires_at); + const nowMs = now(); + if ( + !hasExactRecordKeys(record, fields) || + record.ok !== true || + record.customer_id !== config.customerId || + record.device_identifier !== config.deviceIdentifier || + typeof record.correct_control_plane !== 'boolean' || + typeof record.acl_allowed !== 'boolean' || + typeof record.online !== 'boolean' || + typeof record.reason !== 'string' || + !Number.isFinite(observedAt) || + !Number.isFinite(expiresAt) || + observedAt > nowMs + 5_000 || + observedAt < nowMs - 60_000 || + expiresAt <= nowMs || + expiresAt - nowMs > 60_000 || + expiresAt <= observedAt || + expiresAt > observedAt + 31_000 + ) { + throw macControlFailure('invalid_response', 'Mac-control private-network readiness contract was invalid.', { + httpStatus: response.status, + }); + } + if ( + record.reason !== 'ready' || + record.correct_control_plane !== true || + record.acl_allowed !== true || + record.online !== true + ) { + throw macControlFailure(record.reason, 'Mac-control private-network authority is not ready.', { + httpStatus: response.status, + }); + } + if ( + !UUID_PATTERN.test(record.device_id) || + !UUID_PATTERN.test(record.enrollment_id) || + !UUID_PATTERN.test(record.grant_id) || + !UUID_PATTERN.test(record.audit_id) + ) { + throw macControlFailure('invalid_response', 'Mac-control ready authority omitted canonical lineage.', { + httpStatus: response.status, + }); + } +} + async function postMacControlRuntimeLaunch(fetchImpl, config) { const response = await fetchImpl(config.endpoint, { method: 'POST', @@ -1276,9 +1425,14 @@ async function runMacControlLiveCanary(options = {}) { const fetchImpl = options.fetchImpl ?? fetch; const now = typeof options.now === 'function' ? options.now : Date.now; let config; + let phase = 'configuration'; try { config = resolveMacControlCanaryConfig(env); + phase = 'network_readiness'; + await refreshMacControlPrivateNetworkReadiness(fetchImpl, config, now); + phase = 'launch_http'; const raw = await postMacControlRuntimeLaunch(fetchImpl, config); + phase = 'launch_contract'; const launch = sanitizeMacControlRuntimeLaunchCanaryResponse( raw, { @@ -1289,6 +1443,7 @@ async function runMacControlLiveCanary(options = {}) { now() ); const launchPrivate = launch[MAC_CONTROL_LAUNCH_PRIVATE]; + phase = 'callback'; const callback = await fetchImpl(launchPrivate.launchUrl, { method: 'GET', redirect: 'manual' }); const postCallbackNow = now(); const callbackAccepted = callback.status === 302 && callback.headers.get('location') === '/ui/'; @@ -1305,6 +1460,7 @@ async function runMacControlLiveCanary(options = {}) { }); } + phase = 'runtime_receipt'; const expectedCandidate = expectedMacControlCandidate(env); const randomBytesImpl = typeof options.randomBytes === 'function' ? options.randomBytes : randomBytes; const challengeBytes = randomBytesImpl(32); @@ -1353,6 +1509,7 @@ async function runMacControlLiveCanary(options = {}) { contextKeyId: config.contextKeyId, }); + phase = 'route_probe'; const deployedProbe = await runMacControlDeployedRouteProbes({ fetchImpl, launchUrl: launchPrivate.launchUrl, @@ -1364,6 +1521,7 @@ async function runMacControlLiveCanary(options = {}) { await options.onDeployedProbe(deployedProbe); } + phase = 'negative_probe'; const deployedNegativeProbe = await runMacControlDeployedNegativeProbe({ fetchImpl, launchUrl: launchPrivate.launchUrl, @@ -1377,11 +1535,10 @@ async function runMacControlLiveCanary(options = {}) { return runtimeProof; } catch (error) { - if (error instanceof MacControlCanaryError) throw error; const reason = macControlReason(error?.reason, config ? 'invalid_response' : 'invalid_configuration'); throw new MacControlCanaryError( 'Mac-control live canary failed.', - failedMacControlProof(env, reason, { httpStatus: error?.httpStatus }), + failedMacControlProof(env, reason, { httpStatus: error?.httpStatus, phase }), reason ); } diff --git a/scripts/evaosLiveCanaryReadiness.js b/scripts/evaosLiveCanaryReadiness.js index ca2b46f4ca0..acd3f78a456 100644 --- a/scripts/evaosLiveCanaryReadiness.js +++ b/scripts/evaosLiveCanaryReadiness.js @@ -52,6 +52,8 @@ const CANARIES = [ 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION', 'AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID', 'AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT', + 'AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT', + 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER', 'AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST', ], exact: { diff --git a/scripts/evaosProvisionLiveCanaryFixtures.js b/scripts/evaosProvisionLiveCanaryFixtures.js index b5002a1df7e..d800a817f70 100644 --- a/scripts/evaosProvisionLiveCanaryFixtures.js +++ b/scripts/evaosProvisionLiveCanaryFixtures.js @@ -16,6 +16,7 @@ const FIXTURE_PROVIDER_METADATA_SOURCE = 'aionui_live_canary_fixture'; const FIXTURE_VM_DISPLAY_NAME = 'AionUi Live Canary Support VM'; const INTERNAL_BROKER_CANARY_CUSTOMER_IDS = new Set(['evaos-support', 'golden', 'internal', 'support', 'support-vm']); const MAC_CONTROL_CANARY_TARGET_SCHEMA = 'evaos.mac_control_canary_target.v2'; +const MAC_CONTROL_DEVICE_IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/; const EXPLICIT_CANARY_TARGET_ID = /(^|[-_])(staging|canary|synthetic)([-_]|$)/i; const SECRET_OUTPUT_PATTERNS = [ /\beds_[A-Za-z0-9_-]{8,}\b/i, @@ -915,11 +916,16 @@ function coreBrokerFixtureEnvFromProvision(state) { }; } -function macControlCanaryEnvFromProvision(state, desktopSession) { +function macControlCanaryEnvFromProvision(state, desktopSession, deviceIdentifier, supabaseUrl) { return { AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: desktopSession, AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: state.customerId, AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: state.endpoint, + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: new URL( + '/functions/v1/customer-mac-control', + supabaseUrl + ).toString(), + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: deviceIdentifier, AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: state.expectedCallbackHost, }; } @@ -956,6 +962,7 @@ function maskSecretsForGithub(env) { 'AIONUI_EVAOS_COMPANY_BRAIN_DENIED_SESSION', 'AIONUI_EVAOS_BUSINESS_BROWSER_DENIED_SESSION', 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION', + 'AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER', ]) { const value = env[key]; if (value) process.stdout.write(`::add-mask::${value}\n`); @@ -1165,6 +1172,29 @@ function sanitizedMacControlCanaryProvisionReport() { return report; } +async function loadUniqueMacControlCanaryDeviceIdentifier(admin, ownerId, customerId) { + const rows = await admin.select('customer_devices', { + customer_id: `eq.${customerId}`, + owner_id: `eq.${ownerId}`, + status: 'neq.revoked', + select: 'id,device_identifier,status', + limit: 3, + }); + const current = Array.isArray(rows) + ? rows.filter( + (row) => + safeText(row?.id, 160) && + typeof row?.device_identifier === 'string' && + MAC_CONTROL_DEVICE_IDENTIFIER_PATTERN.test(row.device_identifier) && + ['pending', 'active', 'needs_attention'].includes(safeText(row?.status, 80)) + ) + : []; + if (current.length !== 1) { + throw new Error('Mac-control canary requires exactly one current staging Mac for the configured owner.'); + } + return current[0].device_identifier; +} + async function provisionMacControlCanarySessionWithAdmin(admin, options) { const cleanupKey = safeText(options.cleanupKey, 160) ?? `local-${crypto.randomUUID()}`; const cleanupKeySource = ['explicit', 'github', 'generated'].includes(options.cleanupKeySource) @@ -1174,6 +1204,7 @@ async function provisionMacControlCanarySessionWithAdmin(admin, options) { const customerAccount = await loadCustomerAccount(admin, options.customerId); assertDatabaseBackedMacControlCanaryTarget(customerAccount, options); const membership = await loadAdminMembership(admin, ownerProfile.id, customerAccount.id); + const deviceIdentifier = await loadUniqueMacControlCanaryDeviceIdentifier(admin, ownerProfile.id, options.customerId); const desktopSession = await createDesktopSession(admin, { userId: ownerProfile.id, email: ownerProfile.email, @@ -1203,7 +1234,7 @@ async function provisionMacControlCanarySessionWithAdmin(admin, options) { }; return { state, - env: macControlCanaryEnvFromProvision(state, desktopSession.raw), + env: macControlCanaryEnvFromProvision(state, desktopSession.raw, deviceIdentifier, options.supabaseUrl), report: sanitizedMacControlCanaryProvisionReport(), }; } diff --git a/scripts/evaosScanMacControlProofs.js b/scripts/evaosScanMacControlProofs.js index d2c24762f37..896081811ff 100644 --- a/scripts/evaosScanMacControlProofs.js +++ b/scripts/evaosScanMacControlProofs.js @@ -43,6 +43,66 @@ const MAC_CONTROL_RUNTIME_NEGATIVE_PROOF_CONTRACT = Object.freeze({ }, }, }); +const MAC_CONTROL_FAILURE_PROOF_CONTRACT = Object.freeze({ + schema: 'evaos.mac_control.live_canary_failure.v1', + fields: [ + 'schema', + 'ok', + 'runtime', + 'launchMode', + 'reason', + 'phase', + 'httpStatus', + 'sourceHeadSha', + 'sourceRunId', + 'secretScan', + ], +}); +const MAC_CONTROL_FAILURE_PHASES = new Set([ + 'configuration', + 'network_readiness', + 'launch_http', + 'launch_contract', + 'callback', + 'runtime_receipt', + 'route_probe', + 'negative_probe', +]); +const MAC_CONTROL_FAILURE_REASONS = new Set([ + 'acl_stale', + 'authority_stale', + 'authority_unavailable', + 'binding_ambiguous', + 'binding_authority_unavailable', + 'binding_expired', + 'binding_missing', + 'binding_replay_conflict', + 'callback_rejected', + 'connector_secret_missing', + 'device_revoked', + 'grant_revoked', + 'grant_binding_mismatch', + 'headscale_acl_stale', + 'invalid_configuration', + 'invalid_response', + 'mac_connector_material_missing', + 'mac_node_expired', + 'mac_node_missing', + 'mac_node_offline', + 'mac_node_unpaired', + 'missing_required_capability', + 'multiple_active_customer_grants', + 'no_active_customer_grant', + 'policy_hash_mismatch', + 'policy_unavailable', + 'runtime_launch_blocked', + 'runtime_receipt_rejected', + 'selected_customer_scope_mismatch', + 'selected_scope_not_ready', + 'vm_node_expired', + 'vm_node_missing', + 'vm_node_offline', +]); const MAC_CONTROL_PROOF_CONTRACTS = Object.freeze({ 'mac-control-runtime.json': { schema: 'evaos.mac_control.public_runtime_attestation_envelope.v1', @@ -170,9 +230,11 @@ function assertMacControlProofSanitized(value, location = '$') { entry !== null && typeof entry === 'object' && !Array.isArray(entry); + const safeSecretScan = normalizedKey === 'secretscan' && entry === 'passed'; if ( FORBIDDEN_NORMALIZED_FIELDS.has(normalizedKey) || (!safeClassificationLabel && + !safeSecretScan && FORBIDDEN_NORMALIZED_FIELD_FRAGMENTS.some((fragment) => normalizedKey.includes(fragment))) || (normalizedKey.includes('key') && !ALLOWED_PUBLIC_KEY_IDENTIFIER_FIELDS.has(normalizedKey)) || /private.*key/.test(normalizedKey) @@ -279,6 +341,26 @@ function assertPublicAttestationEnvelopeSanitized(value, location) { } } +function assertMacControlFailureProof(value, location) { + assertMacControlProofSanitized(value, location); + assertExactProofContract(value, MAC_CONTROL_FAILURE_PROOF_CONTRACT, location); + if ( + value.schema !== MAC_CONTROL_FAILURE_PROOF_CONTRACT.schema || + value.ok !== false || + value.runtime !== 'openclaw' || + value.launchMode !== 'mac_control_tools' || + value.secretScan !== 'passed' || + !MAC_CONTROL_FAILURE_REASONS.has(value.reason) || + !MAC_CONTROL_FAILURE_PHASES.has(value.phase) || + (value.httpStatus !== null && + (!Number.isInteger(value.httpStatus) || value.httpStatus < 100 || value.httpStatus > 599)) || + (value.sourceHeadSha !== null && !/^[0-9a-f]{40}$/i.test(value.sourceHeadSha)) || + (value.sourceRunId !== null && !/^\d+$/.test(value.sourceRunId)) + ) { + throw new Error(`Mac-control failure proof contract is invalid at ${location}.`); + } +} + function scanMacControlProofDirectory(proofDir, options = {}) { const resolvedProofDir = path.resolve(String(proofDir || '')); const allowPartial = options.allowPartial === true; @@ -298,6 +380,15 @@ function scanMacControlProofDirectory(proofDir, options = {}) { for (const proofName of existing) { const proofPath = path.join(resolvedProofDir, proofName); const proof = JSON.parse(fs.readFileSync(proofPath, 'utf8')); + if (proofName === 'mac-control-runtime.json' && proof.schema === MAC_CONTROL_FAILURE_PROOF_CONTRACT.schema) { + if (!allowPartial) { + throw new Error('Mac-control failure envelope cannot satisfy complete proof.'); + } + assertMacControlFailureProof(proof, proofName); + parsedProofs.set(proofName, proof); + scanned += 1; + continue; + } const contract = MAC_CONTROL_PROOF_CONTRACTS[proofName]; if (!contract || proof.schema !== contract.schema) { throw new Error(`Mac-control proof has an unexpected schema in ${proofName}.`); diff --git a/tests/unit/evaos/evaosBrokerLiveCanary.test.ts b/tests/unit/evaos/evaosBrokerLiveCanary.test.ts index 083a7eb365e..0d4ec2bac9e 100644 --- a/tests/unit/evaos/evaosBrokerLiveCanary.test.ts +++ b/tests/unit/evaos/evaosBrokerLiveCanary.test.ts @@ -60,6 +60,8 @@ const liveCanary = require('../../../scripts/evaosBrokerLiveCanary.js') as { desktopSession: string; customerId: string; endpoint: string; + networkEndpoint: string; + deviceIdentifier: string; expectedCallbackHost: string; receiptKeyId: string; receiptPublicKey: string; @@ -608,6 +610,9 @@ describe('evaOS broker live canary', () => { AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', }; expect( @@ -692,6 +697,23 @@ describe('evaOS broker live canary', () => { }; const fetchImpl = vi .fn() + .mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: '22222222-2222-4222-8222-222222222222', + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: true, + acl_allowed: true, + online: true, + reason: 'ready', + observed_at: new Date(now - 1_000).toISOString(), + expires_at: new Date(now + 20_000).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ) .mockResolvedValueOnce( jsonResponse({ status: 'attached', @@ -743,6 +765,9 @@ describe('evaOS broker live canary', () => { AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', ...TRUST_ENV, GITHUB_SHA: sourceHeadSha, @@ -759,18 +784,24 @@ describe('evaOS broker live canary', () => { }, }); - expect(fetchImpl).toHaveBeenCalledTimes(9); + expect(fetchImpl).toHaveBeenCalledTimes(10); + expect(fetchImpl.mock.calls[0][0]).toBe('https://dashboard-staging.example.test/functions/v1/customer-mac-control'); expect(JSON.parse(String(fetchImpl.mock.calls[0][1]?.body))).toEqual({ + action: 'get_private_network_readiness', + customer_id: 'staging-mac-owner', + device_identifier: 'staging-device-for-test', + }); + expect(JSON.parse(String(fetchImpl.mock.calls[1][1]?.body))).toEqual({ action: 'runtime_launch', customer_id: 'staging-mac-owner', runtime: 'openclaw', launch_mode: 'mac_control_tools', }); - expect(fetchImpl.mock.calls[1][1]).toMatchObject({ method: 'GET', redirect: 'manual' }); - expect(fetchImpl.mock.calls[2][0]).toBe( + expect(fetchImpl.mock.calls[2][1]).toMatchObject({ method: 'GET', redirect: 'manual' }); + expect(fetchImpl.mock.calls[3][0]).toBe( 'https://openclaw-staging.example.test/api/v1/evaos/mac-control/runtime-receipt' ); - expect(fetchImpl.mock.calls[2][1]).toMatchObject({ + expect(fetchImpl.mock.calls[3][1]).toMatchObject({ method: 'POST', redirect: 'manual', headers: { @@ -779,7 +810,7 @@ describe('evaOS broker live canary', () => { 'Content-Type': 'application/json', }, }); - expect(JSON.parse(String(fetchImpl.mock.calls[2][1]?.body))).toEqual({ challenge, runRef }); + expect(JSON.parse(String(fetchImpl.mock.calls[3][1]?.body))).toEqual({ challenge, runRef }); expect(proof).toEqual(signedProof.envelope); expect(deployedProbe).toMatchObject({ schema: 'evaos.mac_control.deployed_route_probe.v1', @@ -794,7 +825,7 @@ describe('evaOS broker live canary', () => { sensitiveOutputAbsent: true, }, }); - expect(JSON.parse(String(fetchImpl.mock.calls[8][1]?.body))).toEqual({ + expect(JSON.parse(String(fetchImpl.mock.calls[9][1]?.body))).toEqual({ proofMode: 'deployed-staging', expectedCandidate, }); @@ -1304,6 +1335,211 @@ describe('evaOS broker live canary', () => { ).toThrow(/omitted selected-binding readiness/i); }); + it('refreshes live private-network authority and preserves its safe blocker and phase', async () => { + const now = Date.parse('2026-07-14T05:00:00.000Z'); + const fetchImpl = vi.fn().mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: '22222222-2222-4222-8222-222222222222', + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: false, + acl_allowed: false, + online: false, + reason: 'mac_node_missing', + observed_at: new Date(now - 1_000).toISOString(), + expires_at: new Date(now + 20_000).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ); + + await expect( + liveCanary.runMacControlLiveCanary({ + env: { + AIONUI_EVAOS_MAC_CONTROL_CANARY_ACK: 'evaos-mac-control-canary', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', + AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', + ...TRUST_ENV, + GITHUB_SHA: 'a'.repeat(40), + GITHUB_RUN_ID: '123456789', + }, + fetchImpl, + now: () => now, + }) + ).rejects.toMatchObject({ + proof: { + schema: 'evaos.mac_control.live_canary_failure.v1', + ok: false, + runtime: 'openclaw', + launchMode: 'mac_control_tools', + reason: 'mac_node_missing', + phase: 'network_readiness', + httpStatus: 200, + sourceHeadSha: 'a'.repeat(40), + sourceRunId: '123456789', + secretScan: 'passed', + }, + }); + expect(fetchImpl).toHaveBeenCalledTimes(1); + }); + + it('rejects ready private-network authority without canonical lineage identifiers', async () => { + const now = Date.parse('2026-07-14T05:00:00.000Z'); + const fetchImpl = vi.fn().mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: null, + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: true, + acl_allowed: true, + online: true, + reason: 'ready', + observed_at: new Date(now - 1_000).toISOString(), + expires_at: new Date(now + 20_000).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ); + + await expect( + liveCanary.runMacControlLiveCanary({ + env: { + AIONUI_EVAOS_MAC_CONTROL_CANARY_ACK: 'evaos-mac-control-canary', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', + AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', + ...TRUST_ENV, + }, + fetchImpl, + now: () => now, + }) + ).rejects.toMatchObject({ + proof: expect.objectContaining({ + reason: 'invalid_response', + phase: 'network_readiness', + httpStatus: 200, + }), + }); + expect(fetchImpl).toHaveBeenCalledTimes(1); + }); + + it.each([ + { label: 'expired', observedOffset: -120_000, expiresOffset: -90_000 }, + { label: 'future-dated', observedOffset: 6_000, expiresOffset: 30_000 }, + ])('rejects $label private-network authority before runtime launch', async ({ observedOffset, expiresOffset }) => { + const now = Date.parse('2026-07-14T05:00:00.000Z'); + const fetchImpl = vi.fn().mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: '22222222-2222-4222-8222-222222222222', + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: true, + acl_allowed: true, + online: true, + reason: 'ready', + observed_at: new Date(now + observedOffset).toISOString(), + expires_at: new Date(now + expiresOffset).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ); + + await expect( + liveCanary.runMacControlLiveCanary({ + env: { + AIONUI_EVAOS_MAC_CONTROL_CANARY_ACK: 'evaos-mac-control-canary', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', + AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', + ...TRUST_ENV, + }, + fetchImpl, + now: () => now, + }) + ).rejects.toMatchObject({ + proof: expect.objectContaining({ + reason: 'invalid_response', + phase: 'network_readiness', + httpStatus: 200, + }), + }); + expect(fetchImpl).toHaveBeenCalledTimes(1); + }); + + it.each(['no_active_customer_grant', 'multiple_active_customer_grants'])( + 'preserves broker blocker %s at the launch HTTP phase', + async (blocker) => { + const now = Date.parse('2026-07-14T05:00:00.000Z'); + const fetchImpl = vi + .fn() + .mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: '22222222-2222-4222-8222-222222222222', + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: true, + acl_allowed: true, + online: true, + reason: 'ready', + observed_at: new Date(now - 1_000).toISOString(), + expires_at: new Date(now + 20_000).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ) + .mockResolvedValueOnce(jsonResponse({ reason: blocker }, { status: 409 })); + + await expect( + liveCanary.runMacControlLiveCanary({ + env: { + AIONUI_EVAOS_MAC_CONTROL_CANARY_ACK: 'evaos-mac-control-canary', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', + AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', + AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', + ...TRUST_ENV, + GITHUB_SHA: 'a'.repeat(40), + GITHUB_RUN_ID: '123456789', + }, + fetchImpl, + now: () => now, + }) + ).rejects.toMatchObject({ + proof: expect.objectContaining({ + schema: 'evaos.mac_control.live_canary_failure.v1', + reason: blocker, + phase: 'launch_http', + httpStatus: 409, + }), + }); + expect(fetchImpl).toHaveBeenCalledTimes(2); + } + ); + it('fails closed when the selected binding is expired or the callback exchange is rejected', async () => { const now = Date.parse('2026-07-14T05:00:00.000Z'); const binding = { @@ -1318,20 +1554,39 @@ describe('evaOS broker live canary', () => { binding_expires_at: new Date(now - 1_000).toISOString(), allowed_capabilities: ['customer_mac_status', 'desktop_see', 'desktop_control'], }; - const fetchImpl = vi.fn().mockResolvedValueOnce( - jsonResponse({ - status: 'attached', - customer_id: 'staging-mac-owner', - runtime: 'openclaw', - launch_mode: 'mac_control_tools', - launch_url: - 'https://openclaw-staging.example.test/auth/callback?customer_id=staging-mac-owner&session=callback_secret_for_test', - source_pointer: 'broker:runtime_launch:openclaw', - audit_id: 'broker:runtime_launch:staging-mac-owner:openclaw', - mac_control: binding, - runtime_status: { tools_ready: true, mac_control: { ...binding } }, - }) - ); + const fetchImpl = vi + .fn() + .mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: '22222222-2222-4222-8222-222222222222', + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: true, + acl_allowed: true, + online: true, + reason: 'ready', + observed_at: new Date(now - 1_000).toISOString(), + expires_at: new Date(now + 20_000).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ) + .mockResolvedValueOnce( + jsonResponse({ + status: 'attached', + customer_id: 'staging-mac-owner', + runtime: 'openclaw', + launch_mode: 'mac_control_tools', + launch_url: + 'https://openclaw-staging.example.test/auth/callback?customer_id=staging-mac-owner&session=callback_secret_for_test', + source_pointer: 'broker:runtime_launch:openclaw', + audit_id: 'broker:runtime_launch:staging-mac-owner:openclaw', + mac_control: binding, + runtime_status: { tools_ready: true, mac_control: { ...binding } }, + }) + ); await expect( liveCanary.runMacControlLiveCanary({ @@ -1340,6 +1595,9 @@ describe('evaOS broker live canary', () => { AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', ...TRUST_ENV, }, @@ -1348,13 +1606,13 @@ describe('evaOS broker live canary', () => { }) ).rejects.toMatchObject({ proof: expect.objectContaining({ - schema: 'evaos-mac-control-live-canary/v1', + schema: 'evaos.mac_control.live_canary_failure.v1', ok: false, reason: 'binding_expired', secretScan: 'passed', }), }); - expect(fetchImpl).toHaveBeenCalledTimes(1); + expect(fetchImpl).toHaveBeenCalledTimes(2); }); it('fails when the selected binding expires while the callback exchange is in flight', async () => { @@ -1374,6 +1632,23 @@ describe('evaOS broker live canary', () => { }; const fetchImpl = vi .fn() + .mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: '22222222-2222-4222-8222-222222222222', + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: true, + acl_allowed: true, + online: true, + reason: 'ready', + observed_at: new Date(launchNow - 1_000).toISOString(), + expires_at: new Date(launchNow + 20_000).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ) .mockResolvedValueOnce( jsonResponse({ status: 'attached', @@ -1397,7 +1672,7 @@ describe('evaOS broker live canary', () => { }, }) ); - const times = [launchNow, callbackNow]; + const times = [launchNow, launchNow, callbackNow]; await expect( liveCanary.runMacControlLiveCanary({ @@ -1406,6 +1681,9 @@ describe('evaOS broker live canary', () => { AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', ...TRUST_ENV, }, @@ -1415,7 +1693,7 @@ describe('evaOS broker live canary', () => { ).rejects.toMatchObject({ proof: expect.objectContaining({ ok: false, reason: 'binding_expired' }), }); - expect(fetchImpl).toHaveBeenCalledTimes(2); + expect(fetchImpl).toHaveBeenCalledTimes(3); }); it('rejects unusable or non-host-only proxy session cookies', async () => { @@ -1460,6 +1738,23 @@ describe('evaOS broker live canary', () => { invalidCookies.map((setCookie) => { const fetchImpl = vi .fn() + .mockResolvedValueOnce( + jsonResponse({ + ok: true, + customer_id: 'staging-mac-owner', + device_id: '22222222-2222-4222-8222-222222222222', + device_identifier: 'staging-device-for-test', + enrollment_id: '33333333-3333-4333-8333-333333333333', + grant_id: '44444444-4444-4444-8444-444444444444', + correct_control_plane: true, + acl_allowed: true, + online: true, + reason: 'ready', + observed_at: new Date(now - 1_000).toISOString(), + expires_at: new Date(now + 20_000).toISOString(), + audit_id: '55555555-5555-4555-8555-555555555555', + }) + ) .mockResolvedValueOnce(jsonResponse(launchResponse)) .mockResolvedValueOnce( new Response(null, { @@ -1475,6 +1770,9 @@ describe('evaOS broker live canary', () => { AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_canary_session_for_test', AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', ...TRUST_ENV, }, diff --git a/tests/unit/evaos/evaosLiveCanaryReadiness.test.ts b/tests/unit/evaos/evaosLiveCanaryReadiness.test.ts index 4aa5c13c06d..745a8d07d8e 100644 --- a/tests/unit/evaos/evaosLiveCanaryReadiness.test.ts +++ b/tests/unit/evaos/evaosLiveCanaryReadiness.test.ts @@ -149,6 +149,9 @@ describe('evaOS live canary readiness', () => { AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION: 'eds_mac_session_for_test', AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', }); diff --git a/tests/unit/evaos/evaosLiveCanaryWorkflow.test.ts b/tests/unit/evaos/evaosLiveCanaryWorkflow.test.ts index e48d1a7ac72..cfcda42940c 100644 --- a/tests/unit/evaos/evaosLiveCanaryWorkflow.test.ts +++ b/tests/unit/evaos/evaosLiveCanaryWorkflow.test.ts @@ -34,6 +34,21 @@ function validMacControlRuntimeProof(): Record { }).envelope; } +function validMacControlFailureProof(): Record { + return { + schema: 'evaos.mac_control.live_canary_failure.v1', + ok: false, + runtime: 'openclaw', + launchMode: 'mac_control_tools', + reason: 'authority_stale', + phase: 'network_readiness', + httpStatus: 200, + sourceHeadSha: 'd'.repeat(40), + sourceRunId: '12345', + secretScan: 'passed', + }; +} + function writeCompleteMacControlProofSet(proofDir: string): void { const provision = { schema: 'evaos-mac-control-canary-session-provision/v1', @@ -263,9 +278,17 @@ describe('evaOS live canary proof workflow', () => { expect(summaryStart).toBeGreaterThan(strictStart); const producerBlock = workflow.slice(producerStart, partialStart); + const cleanupStart = workflow.indexOf('- name: Cleanup Mac-control canary session'); + const cleanupBlock = workflow.slice(cleanupStart, partialStart); const partialStep = workflow.slice(partialStart, strictStart); const strictStep = workflow.slice(strictStart, summaryStart); expect(producerBlock).not.toContain('continue-on-error'); + expect(cleanupStart).toBeGreaterThan(producerStart); + expect(cleanupBlock).toContain('cleanup_temp="${cleanup_output}.tmp"'); + expect(cleanupBlock).toContain('trap \'rm -f "$cleanup_temp"\' EXIT'); + expect(cleanupBlock).toContain('cleanup-mac-control > "$cleanup_temp"'); + expect(cleanupBlock).toContain('mv "$cleanup_temp" "$cleanup_output"'); + expect(cleanupBlock).not.toContain('cleanup-mac-control > "$PROOF_DIR/mac-control-session-cleanup.stdout.json"'); expect(partialStep).toContain('id: mac-control-partial-proof-scan'); expect(partialStep).toContain("if: always() && github.event.inputs.run_mac_control_canary == 'true'"); expect(partialStep).toContain('node scripts/evaosScanMacControlProofs.js --allow-partial "$PROOF_DIR"'); @@ -337,6 +360,47 @@ describe('evaOS live canary proof workflow', () => { } }); + it('uploads a strictly shaped failure envelope in partial mode but rejects it as complete proof', () => { + const proofDir = fs.mkdtempSync(path.join(os.tmpdir(), 'evaos-proof-partial-failure-')); + try { + fs.writeFileSync( + path.join(proofDir, 'mac-control-runtime.json'), + `${JSON.stringify(validMacControlFailureProof())}\n` + ); + + expect(proofScanner.scanMacControlProofDirectory(proofDir, { allowPartial: true })).toMatchObject({ + ok: true, + scanned: 1, + }); + expect(() => proofScanner.scanMacControlProofDirectory(proofDir)).toThrow(/failure envelope.*complete proof/i); + + fs.writeFileSync( + path.join(proofDir, 'mac-control-runtime.json'), + `${JSON.stringify({ ...validMacControlFailureProof(), reason: 'multiple_active_customer_grants' })}\n` + ); + expect(proofScanner.scanMacControlProofDirectory(proofDir, { allowPartial: true })).toMatchObject({ + ok: true, + scanned: 1, + }); + + fs.writeFileSync( + path.join(proofDir, 'mac-control-runtime.json'), + `${JSON.stringify({ ...validMacControlFailureProof(), connectorUrl: 'https://unsafe.example.test' })}\n` + ); + expect(() => proofScanner.scanMacControlProofDirectory(proofDir, { allowPartial: true })).toThrow(/forbidden/i); + + fs.writeFileSync( + path.join(proofDir, 'mac-control-runtime.json'), + `${JSON.stringify({ ...validMacControlFailureProof(), reason: 'invented_safe_looking_reason' })}\n` + ); + expect(() => proofScanner.scanMacControlProofDirectory(proofDir, { allowPartial: true })).toThrow( + /failure proof contract/i + ); + } finally { + fs.rmSync(proofDir, { recursive: true, force: true }); + } + }); + it('rejects an unsafe existing artifact in partial mode', () => { const proofDir = fs.mkdtempSync(path.join(os.tmpdir(), 'evaos-proof-partial-unsafe-')); try { diff --git a/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts b/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts index 5fed1c9ba60..63fe66fb7c7 100644 --- a/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts +++ b/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts @@ -296,7 +296,14 @@ class FakeMacControlCanaryAdmin { supabase_origin: 'https://dashboard-staging.example.test', endpoint_origin: 'https://dashboard-staging.example.test', expected_callback_host: 'openclaw-staging.example.test', - } + }, + private readonly deviceRows: ProviderRow[] = [ + { + id: 'staging-device-id', + device_identifier: 'staging-device-for-test', + status: 'active', + }, + ] ) {} async single(table: string, query: Record) { @@ -321,6 +328,12 @@ class FakeMacControlCanaryAdmin { return { id: 'temporary-session-id', expires_at: body.expires_at }; } + async select(table: string, query: Record) { + this.reads.push({ table, query }); + if (table === 'customer_devices') return this.deviceRows.map((row) => ({ ...row })); + throw new Error(`unexpected select table ${table}`); + } + async patch(table: string, query: Record, body: ProviderRow, _options?: Record) { this.patches.push({ table, query, body }); const sessionId = query.id?.startsWith('eq.') ? query.id.slice(3) : 'temporary-session-id'; @@ -344,7 +357,8 @@ class NoEvidenceMacControlCanaryAdmin extends FakeMacControlCanaryAdmin { return []; } - async select() { + override async select(table: string, query: Record) { + if (table === 'customer_devices') return super.select(table, query); return []; } } @@ -727,7 +741,7 @@ describe('evaOS live canary fixture provisioner', () => { for (const admin of mismatchedAdmins) expect(admin.inserts).toHaveLength(0); }); - it('mints only a fresh desktop session for an existing staging Mac owner and emits sanitized proof', async () => { + it('mints only a fresh desktop session for the unique current staging Mac and emits sanitized proof', async () => { const admin = new FakeMacControlCanaryAdmin(); const { state, env, report } = await provisioner.provisionMacControlCanarySessionWithAdmin(admin, { supabaseUrl: 'https://dashboard-staging.example.test', @@ -742,6 +756,7 @@ describe('evaOS live canary fixture provisioner', () => { 'profiles', 'customer_accounts', 'customer_account_memberships', + 'customer_devices', ]); expect(admin.inserts).toHaveLength(1); expect(admin.inserts[0]).toMatchObject({ @@ -758,6 +773,9 @@ describe('evaOS live canary fixture provisioner', () => { expect(env).toMatchObject({ AIONUI_EVAOS_MAC_CONTROL_CANARY_CUSTOMER_ID: 'staging-mac-owner', AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime', + AIONUI_EVAOS_MAC_CONTROL_CANARY_NETWORK_ENDPOINT: + 'https://dashboard-staging.example.test/functions/v1/customer-mac-control', + AIONUI_EVAOS_MAC_CONTROL_CANARY_DEVICE_IDENTIFIER: 'staging-device-for-test', AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', }); expect(env.AIONUI_EVAOS_MAC_CONTROL_CANARY_DESKTOP_SESSION).toMatch(/^eds_/); @@ -790,6 +808,42 @@ describe('evaOS live canary fixture provisioner', () => { }); }); + it('fails closed before session minting when the staging Mac target is missing or ambiguous', async () => { + const options = { + supabaseUrl: 'https://dashboard-staging.example.test', + accountEmail: 'owner@staging.invalid', + customerId: 'staging-mac-owner', + endpoint: 'https://dashboard-staging.example.test/runtime', + expectedCallbackHost: 'openclaw-staging.example.test', + ttlMinutes: 10, + }; + const missing = new FakeMacControlCanaryAdmin(undefined, []); + const ambiguous = new FakeMacControlCanaryAdmin(undefined, [ + { id: 'staging-device-id-1', device_identifier: 'staging-device-one', status: 'active' }, + { id: 'staging-device-id-2', device_identifier: 'staging-device-two', status: 'needs_attention' }, + ]); + const workflowCommand = new FakeMacControlCanaryAdmin(undefined, [ + { + id: 'staging-device-id', + device_identifier: 'staging-device\n::warning::workflow-command', + status: 'active', + }, + ]); + + await expect(provisioner.provisionMacControlCanarySessionWithAdmin(missing, options)).rejects.toThrow( + /exactly one current staging Mac/i + ); + await expect(provisioner.provisionMacControlCanarySessionWithAdmin(ambiguous, options)).rejects.toThrow( + /exactly one current staging Mac/i + ); + await expect(provisioner.provisionMacControlCanarySessionWithAdmin(workflowCommand, options)).rejects.toThrow( + /exactly one current staging Mac/i + ); + expect(missing.inserts).toHaveLength(0); + expect(ambiguous.inserts).toHaveLength(0); + expect(workflowCommand.inserts).toHaveLength(0); + }); + it('generates a unique run-scoped cleanup key for each local canary session', async () => { const options = { supabaseUrl: 'https://dashboard-staging.example.test', From 6d47115ea5955b66e616b3c9d4dbdf2fd98b84ef Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 21:04:25 +0700 Subject: [PATCH 2/2] fix(evaos): address canary review findings --- scripts/evaosBrokerLiveCanary.js | 1 + scripts/evaosProvisionLiveCanaryFixtures.js | 23 +++++++++---------- .../unit/evaos/evaosBrokerLiveCanary.test.ts | 8 +++++++ .../evaosProvisionLiveCanaryFixtures.test.ts | 15 +++++++++++- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/scripts/evaosBrokerLiveCanary.js b/scripts/evaosBrokerLiveCanary.js index d894df43f58..e3e8db5c76c 100644 --- a/scripts/evaosBrokerLiveCanary.js +++ b/scripts/evaosBrokerLiveCanary.js @@ -221,6 +221,7 @@ function resolveMacControlCanaryConfig(env) { networkEndpointUrl.protocol !== 'https:' || endpointUrl.username || endpointUrl.password || + endpointUrl.search || endpointUrl.hash || networkEndpointUrl.username || networkEndpointUrl.password || diff --git a/scripts/evaosProvisionLiveCanaryFixtures.js b/scripts/evaosProvisionLiveCanaryFixtures.js index d800a817f70..bb7d6f93fcf 100644 --- a/scripts/evaosProvisionLiveCanaryFixtures.js +++ b/scripts/evaosProvisionLiveCanaryFixtures.js @@ -1178,21 +1178,20 @@ async function loadUniqueMacControlCanaryDeviceIdentifier(admin, ownerId, custom owner_id: `eq.${ownerId}`, status: 'neq.revoked', select: 'id,device_identifier,status', - limit: 3, + limit: 2, }); - const current = Array.isArray(rows) - ? rows.filter( - (row) => - safeText(row?.id, 160) && - typeof row?.device_identifier === 'string' && - MAC_CONTROL_DEVICE_IDENTIFIER_PATTERN.test(row.device_identifier) && - ['pending', 'active', 'needs_attention'].includes(safeText(row?.status, 80)) - ) - : []; - if (current.length !== 1) { + const current = Array.isArray(rows) ? rows : []; + const device = current[0]; + if ( + current.length !== 1 || + !safeText(device?.id, 160) || + typeof device?.device_identifier !== 'string' || + !MAC_CONTROL_DEVICE_IDENTIFIER_PATTERN.test(device.device_identifier) || + !['pending', 'active', 'needs_attention'].includes(safeText(device?.status, 80)) + ) { throw new Error('Mac-control canary requires exactly one current staging Mac for the configured owner.'); } - return current[0].device_identifier; + return device.device_identifier; } async function provisionMacControlCanarySessionWithAdmin(admin, options) { diff --git a/tests/unit/evaos/evaosBrokerLiveCanary.test.ts b/tests/unit/evaos/evaosBrokerLiveCanary.test.ts index 0d4ec2bac9e..3e55fac4f79 100644 --- a/tests/unit/evaos/evaosBrokerLiveCanary.test.ts +++ b/tests/unit/evaos/evaosBrokerLiveCanary.test.ts @@ -622,6 +622,14 @@ describe('evaOS broker live canary', () => { }).expectedCallbackHost ).toBe('openclaw-staging.example.test'); + expect(() => + liveCanary.resolveMacControlCanaryConfig({ + ...env, + AIONUI_EVAOS_MAC_CONTROL_CANARY_ENDPOINT: 'https://dashboard-staging.example.test/runtime?mode=unexpected', + AIONUI_EVAOS_MAC_CONTROL_CANARY_EXPECTED_CALLBACK_HOST: 'openclaw-staging.example.test', + }) + ).toThrow(/same-origin absolute HTTPS URLs/i); + for (const callbackHost of [ '.openclaw-staging.example.test', 'openclaw-staging.example.test.', diff --git a/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts b/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts index 63fe66fb7c7..94cc59bed5b 100644 --- a/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts +++ b/tests/unit/evaos/evaosProvisionLiveCanaryFixtures.test.ts @@ -330,7 +330,10 @@ class FakeMacControlCanaryAdmin { async select(table: string, query: Record) { this.reads.push({ table, query }); - if (table === 'customer_devices') return this.deviceRows.map((row) => ({ ...row })); + if (table === 'customer_devices') { + const limit = Number(query.limit); + return this.deviceRows.slice(0, Number.isSafeInteger(limit) ? limit : undefined).map((row) => ({ ...row })); + } throw new Error(`unexpected select table ${table}`); } @@ -829,6 +832,12 @@ describe('evaOS live canary fixture provisioner', () => { status: 'active', }, ]); + const truncatedAmbiguous = new FakeMacControlCanaryAdmin(undefined, [ + { id: 'pending-device-1', device_identifier: null, status: 'pending' }, + { id: 'pending-device-2', device_identifier: null, status: 'pending' }, + { id: 'staging-device-id-1', device_identifier: 'staging-device-one', status: 'active' }, + { id: 'staging-device-id-2', device_identifier: 'staging-device-two', status: 'active' }, + ]); await expect(provisioner.provisionMacControlCanarySessionWithAdmin(missing, options)).rejects.toThrow( /exactly one current staging Mac/i @@ -839,9 +848,13 @@ describe('evaOS live canary fixture provisioner', () => { await expect(provisioner.provisionMacControlCanarySessionWithAdmin(workflowCommand, options)).rejects.toThrow( /exactly one current staging Mac/i ); + await expect(provisioner.provisionMacControlCanarySessionWithAdmin(truncatedAmbiguous, options)).rejects.toThrow( + /exactly one current staging Mac/i + ); expect(missing.inserts).toHaveLength(0); expect(ambiguous.inserts).toHaveLength(0); expect(workflowCommand.inserts).toHaveLength(0); + expect(truncatedAmbiguous.inserts).toHaveLength(0); }); it('generates a unique run-scoped cleanup key for each local canary session', async () => {