From 74eceb5b360a962cdc697f1024205f593291e93a Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 23:37:26 +0700 Subject: [PATCH 1/7] fix(release): ignore removed Spotlight canary paths --- .github/workflows/evaos-beta-rc-canary.yml | 55 ++++++++++++++++++- .../src/evaos_desktop_bridge/pre_canary.py | 25 ++++++++- .../prepareEvaosDesktopBridgeResource.test.ts | 39 +++++++++++++ 3 files changed, 115 insertions(+), 4 deletions(-) diff --git a/.github/workflows/evaos-beta-rc-canary.yml b/.github/workflows/evaos-beta-rc-canary.yml index f207034b269..11d82373027 100644 --- a/.github/workflows/evaos-beta-rc-canary.yml +++ b/.github/workflows/evaos-beta-rc-canary.yml @@ -656,16 +656,69 @@ jobs: PRE_CANARY_DIR="$PROOF_DIR/installed-candidate-pre-canary" CONNECTOR_CANARY_DIR="$PROOF_DIR/installed-candidate-connector" rm -rf "$PRE_CANARY_DIR" "$CONNECTOR_CANARY_DIR" + set +e "$BRIDGE_COMMAND" pre-canary \ --json \ --control-surface bridge-peekaboo \ --expected-version "$SHORT_VERSION" \ --expected-build "$BUNDLE_VERSION" \ --expected-source-commit "$TAG_COMMIT" \ + --canary-artifact-root "$RUNNER_TEMP" \ --artifact-dir "$PRE_CANARY_DIR" \ > "$PROOF_DIR/installed-candidate-pre-canary.stdout.json" \ 2> "$PROOF_DIR/installed-candidate-pre-canary.stderr.txt" - cp "$PRE_CANARY_DIR/qa-report.json" "$PROOF_DIR/installed-candidate-pre-canary.json" + PRE_CANARY_EXIT=$? + set -e + if [ -f "$PRE_CANARY_DIR/qa-report.json" ]; then + cp "$PRE_CANARY_DIR/qa-report.json" "$PROOF_DIR/installed-candidate-pre-canary.json" + fi + if [ "$PRE_CANARY_EXIT" -ne 0 ]; then + echo "Pre-canary exit code: $PRE_CANARY_EXIT" + if [ -f "$PROOF_DIR/installed-candidate-pre-canary.json" ]; then + set +e + node - "$PROOF_DIR/installed-candidate-pre-canary.json" <<'NODE' + const fs = require('fs'); + const reportPath = process.argv[2]; + const report = JSON.parse(fs.readFileSync(reportPath, 'utf8')); + const sanitizeMessage = (value) => + String(value ?? '') + .replace(/https?:\/\/\S+/gi, '[redacted]') + .replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, '[redacted]') + .replace(/\/(?:[^/\s]+\/)*[^\s]*/g, '[redacted]') + .replace(/\b(token|secret|key|authorization|bearer)\b(?:\s*[:=]\s*|\s+)\S+/gi, '$1=[redacted]') + .replace(/[^\x20-\x7e]/g, '?') + .replace(/\s+/g, ' ') + .trim() + .slice(0, 240); + const checks = Array.isArray(report.checks) ? report.checks : []; + const failedChecks = checks.filter((check) => check && check.status === 'fail'); + const sanitizedChecks = failedChecks.map((check) => ({ + code: + typeof check.code === 'string' && /^[a-z0-9_.-]{1,96}$/i.test(check.code) + ? check.code + : 'invalid_check_code', + status: ['fail', 'warn'].includes(check.status) ? check.status : 'unknown', + message: sanitizeMessage(check.message), + })); + if (sanitizedChecks.length === 0) { + console.error('Pre-canary failed without a non-passing sanitized check.'); + } else { + for (const check of sanitizedChecks) { + console.error(`Pre-canary sanitized check: ${JSON.stringify(check)}`); + } + } + NODE + PRE_CANARY_SANITIZER_EXIT=$? + set -e + if [ "$PRE_CANARY_SANITIZER_EXIT" -ne 0 ]; then + echo "Pre-canary report could not be reduced to a sanitized check summary." + fi + else + echo "Pre-canary failed without a report that can be summarized safely." + fi + echo "::error::Installed candidate pre-canary failed; see the sanitized check summary above." + exit "$PRE_CANARY_EXIT" + fi TOKEN_FILE="$HOME/Library/Application Support/evaos-desktop-bridge/connector.token" for _attempt in $(seq 1 30); do diff --git a/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py b/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py index 8580ddbdbca..da88749af17 100644 --- a/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py +++ b/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py @@ -260,10 +260,17 @@ def gather_inventory( bundle_id: str = DEFAULT_BUNDLE_ID, artifact_roots: Sequence[str] | None = None, ) -> WorkbenchInventory: - registered_paths = _unique_paths( + registered_paths = tuple( path - for candidate_bundle_id in (bundle_id, *sorted(LEGACY_BUNDLE_IDS)) - for path in _mdfind_bundle_paths(candidate_bundle_id) + for path in _unique_paths( + path + for candidate_bundle_id in (bundle_id, *sorted(LEGACY_BUNDLE_IDS)) + for path in _mdfind_bundle_paths(candidate_bundle_id) + ) + # Spotlight can retain paths from updater/fallback extraction after the + # temporary app has been removed. Discard only definitively missing + # targets; existing or unverifiable paths remain fail-closed. + if _registered_path_exists_or_is_unverifiable(path) ) artifact_paths = tuple(_artifact_workbench_bundle_paths(artifact_roots=artifact_roots)) bundle_paths = _unique_paths((*registered_paths, *artifact_paths, canonical_path)) @@ -410,6 +417,18 @@ def _unique_paths(paths: Iterable[str]) -> tuple[str, ...]: return tuple(ordered) +def _registered_path_exists_or_is_unverifiable(path: str) -> bool: + try: + Path(path).stat() + except (FileNotFoundError, NotADirectoryError): + return False + except OSError: + # Keep permission and other inspection failures fail-closed. Only a + # definitively removed Spotlight target is safe to ignore. + return True + return True + + def _run(command: Sequence[str]) -> str: try: completed = subprocess.run(command, check=False, capture_output=True, text=True, timeout=10) diff --git a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts index dfad127c372..72e64d119d3 100644 --- a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts +++ b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts @@ -266,6 +266,21 @@ describe('prepareEvaosDesktopBridgeResource', () => { } }); + it('captures pre-canary failures as sanitized check summaries before preserving the exit code', () => { + const workflow = readFileSync(join(process.cwd(), '.github', 'workflows', 'evaos-beta-rc-canary.yml'), 'utf8'); + const failureBlock = workflow.slice( + workflow.indexOf('PRE_CANARY_EXIT=$?'), + workflow.indexOf('TOKEN_FILE="$HOME/Library/Application Support/evaos-desktop-bridge/connector.token"') + ); + + expect(failureBlock).toContain('PRE_CANARY_EXIT=$?'); + expect(workflow).toContain('--canary-artifact-root "$RUNNER_TEMP"'); + expect(failureBlock).toContain('Pre-canary exit code: $PRE_CANARY_EXIT'); + expect(failureBlock).toContain('Pre-canary sanitized check: ${JSON.stringify(check)}'); + expect(failureBlock).toContain('exit "$PRE_CANARY_EXIT"'); + expect(failureBlock).not.toMatch(/\.evidence|\.inventory/); + }); + it('rejects dirty or untracked vendored bridge bytes in strict provenance checks', () => { expect(() => bridgeResource.assertVendoredBridgeSourceMatchesHead( @@ -621,6 +636,30 @@ describe('prepareEvaosDesktopBridgeResource', () => { 'report = pre_canary.evaluate_inventory(inventory, expected_version="2.1.36", expected_build="2.1.36")', 'assert report.ok, report.to_dict()', 'assert pre_canary._workbench_app_path_from_command(current_process.command) == pre_canary.DEFAULT_CANONICAL_PATH', + 'with TemporaryDirectory() as inventory_root:', + ' canonical = Path(inventory_root) / "evaOS Workbench.app"', + ' canonical.mkdir()', + ' removed = Path(inventory_root) / "removed-updater-extract" / "evaOS Workbench.app"', + ' removed_parent = Path(inventory_root) / "removed-parent"', + ' removed_parent.write_text("not a directory", encoding="utf-8")', + ' removed_below_file = removed_parent / "evaOS Workbench.app"', + ' existing_duplicate = Path(inventory_root) / "fallback-extract" / "evaOS Workbench.app"', + ' pre_canary._read_app_bundle = lambda path: pre_canary.AppBundle(path=path, bundle_id=pre_canary.DEFAULT_BUNDLE_ID, version="2.1.36", build="2.1.36", team_id=pre_canary.DEFAULT_TEAM_ID)', + ' pre_canary._process_inventory = lambda: (pre_canary.ProcessInfo(pid=2, command=f"{canonical}/Contents/MacOS/evaOS Workbench", path=str(canonical), kind="workbench"),)', + ' pre_canary._mdfind_bundle_paths = lambda _bundle_id: (str(canonical), str(removed), str(removed_below_file))', + ' filtered_inventory = pre_canary.gather_inventory(canonical_path=str(canonical), artifact_roots=())', + ' assert filtered_inventory.registered_paths == (str(canonical),), filtered_inventory.to_dict()', + ' filtered_report = pre_canary.evaluate_inventory(filtered_inventory, canonical_path=str(canonical), expected_version="2.1.36", expected_build="2.1.36")', + ' assert filtered_report.ok, filtered_report.to_dict()', + ' existing_duplicate.mkdir(parents=True)', + ' pre_canary._mdfind_bundle_paths = lambda _bundle_id: (str(canonical), str(removed), str(removed_below_file), str(existing_duplicate), str(existing_duplicate))', + ' duplicate_inventory = pre_canary.gather_inventory(canonical_path=str(canonical), artifact_roots=())', + ' assert str(removed) not in duplicate_inventory.registered_paths, duplicate_inventory.to_dict()', + ' assert str(removed_below_file) not in duplicate_inventory.registered_paths, duplicate_inventory.to_dict()', + ' assert duplicate_inventory.registered_paths == (str(canonical), str(existing_duplicate)), duplicate_inventory.to_dict()', + ' duplicate_report = pre_canary.evaluate_inventory(duplicate_inventory, canonical_path=str(canonical))', + ' assert not duplicate_report.ok, duplicate_report.to_dict()', + ' assert "duplicate_registered_workbench_app" in {check.code for check in duplicate_report.checks}', 'with TemporaryDirectory() as artifact_dir:', ' report_path = pre_canary._write_report(report.to_dict(), Path(artifact_dir))', ' assert report_path.name == "qa-report.json" and report_path.is_file()', From 7c1c50299fcb375c5ca081eeca1094ac6a3574c8 Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 23:42:46 +0700 Subject: [PATCH 2/7] fix(release): keep malformed RC reports out of logs --- .github/workflows/evaos-beta-rc-canary.yml | 58 ++++++++++--------- .../prepareEvaosDesktopBridgeResource.test.ts | 49 ++++++++++++++++ 2 files changed, 81 insertions(+), 26 deletions(-) diff --git a/.github/workflows/evaos-beta-rc-canary.yml b/.github/workflows/evaos-beta-rc-canary.yml index 11d82373027..209cbe9d7cc 100644 --- a/.github/workflows/evaos-beta-rc-canary.yml +++ b/.github/workflows/evaos-beta-rc-canary.yml @@ -679,33 +679,39 @@ jobs: node - "$PROOF_DIR/installed-candidate-pre-canary.json" <<'NODE' const fs = require('fs'); const reportPath = process.argv[2]; - const report = JSON.parse(fs.readFileSync(reportPath, 'utf8')); - const sanitizeMessage = (value) => - String(value ?? '') - .replace(/https?:\/\/\S+/gi, '[redacted]') - .replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, '[redacted]') - .replace(/\/(?:[^/\s]+\/)*[^\s]*/g, '[redacted]') - .replace(/\b(token|secret|key|authorization|bearer)\b(?:\s*[:=]\s*|\s+)\S+/gi, '$1=[redacted]') - .replace(/[^\x20-\x7e]/g, '?') - .replace(/\s+/g, ' ') - .trim() - .slice(0, 240); - const checks = Array.isArray(report.checks) ? report.checks : []; - const failedChecks = checks.filter((check) => check && check.status === 'fail'); - const sanitizedChecks = failedChecks.map((check) => ({ - code: - typeof check.code === 'string' && /^[a-z0-9_.-]{1,96}$/i.test(check.code) - ? check.code - : 'invalid_check_code', - status: ['fail', 'warn'].includes(check.status) ? check.status : 'unknown', - message: sanitizeMessage(check.message), - })); - if (sanitizedChecks.length === 0) { - console.error('Pre-canary failed without a non-passing sanitized check.'); - } else { - for (const check of sanitizedChecks) { - console.error(`Pre-canary sanitized check: ${JSON.stringify(check)}`); + try { + const report = JSON.parse(fs.readFileSync(reportPath, 'utf8')); + const sanitizeMessage = (value) => + String(value ?? '') + .replace(/https?:\/\/\S+/gi, '[redacted]') + .replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, '[redacted]') + .replace(/\/(?:[^/\s]+\/)*[^\s]*/g, '[redacted]') + .replace(/\b(token|secret|key|authorization|bearer)\b(?:\s*[:=]\s*|\s+)\S+/gi, '$1=[redacted]') + .replace(/[^\x20-\x7e]/g, '?') + .replace(/\s+/g, ' ') + .trim() + .slice(0, 240); + const checks = Array.isArray(report.checks) ? report.checks : []; + const failedChecks = checks.filter((check) => check && check.status === 'fail'); + const sanitizedChecks = failedChecks.map((check) => ({ + code: + typeof check.code === 'string' && /^[a-z0-9_.-]{1,96}$/i.test(check.code) + ? check.code + : 'invalid_check_code', + status: ['fail', 'warn'].includes(check.status) ? check.status : 'unknown', + message: sanitizeMessage(check.message), + })); + if (sanitizedChecks.length === 0) { + console.error('Pre-canary failed without a non-passing sanitized check.'); + } else { + for (const check of sanitizedChecks) { + console.error(`Pre-canary sanitized check: ${JSON.stringify(check)}`); + } } + } catch { + // The shell emits one fixed fallback message. Never let a parser + // exception echo malformed report bytes into the Actions log. + process.exitCode = 1; } NODE PRE_CANARY_SANITIZER_EXIT=$? diff --git a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts index 72e64d119d3..0e278f755fc 100644 --- a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts +++ b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts @@ -268,6 +268,15 @@ describe('prepareEvaosDesktopBridgeResource', () => { it('captures pre-canary failures as sanitized check summaries before preserving the exit code', () => { const workflow = readFileSync(join(process.cwd(), '.github', 'workflows', 'evaos-beta-rc-canary.yml'), 'utf8'); + const sanitizerCommand = 'node - "$PROOF_DIR/installed-candidate-pre-canary.json" <<\'NODE\''; + const sanitizerStart = workflow.indexOf(sanitizerCommand); + const sanitizerBodyStart = workflow.indexOf('\n', sanitizerStart) + 1; + const sanitizerBodyEnd = workflow.indexOf('\n NODE', sanitizerBodyStart); + const sanitizerScript = workflow + .slice(sanitizerBodyStart, sanitizerBodyEnd) + .split('\n') + .map((line) => line.replace(/^ {10}/, '')) + .join('\n'); const failureBlock = workflow.slice( workflow.indexOf('PRE_CANARY_EXIT=$?'), workflow.indexOf('TOKEN_FILE="$HOME/Library/Application Support/evaos-desktop-bridge/connector.token"') @@ -279,6 +288,46 @@ describe('prepareEvaosDesktopBridgeResource', () => { expect(failureBlock).toContain('Pre-canary sanitized check: ${JSON.stringify(check)}'); expect(failureBlock).toContain('exit "$PRE_CANARY_EXIT"'); expect(failureBlock).not.toMatch(/\.evidence|\.inventory/); + expect(sanitizerStart).toBeGreaterThan(-1); + expect(sanitizerBodyEnd).toBeGreaterThan(sanitizerBodyStart); + + const reportDir = mkdtempSync(join(tmpdir(), 'evaos-pre-canary-sanitizer-')); + try { + const reportPath = join(reportDir, 'qa-report.json'); + writeFileSync( + reportPath, + JSON.stringify({ + checks: [ + { + code: 'unsafe/code', + status: 'fail', + message: 'token fixture-secret https://private.invalid /tmp/private 10.0.0.1', + evidence: 'raw-evidence', + }, + ], + inventory: { registered_paths: ['/tmp/private'] }, + }) + ); + const sanitized = spawnSync(process.execPath, ['-', reportPath], { + encoding: 'utf8', + input: sanitizerScript, + }); + expect(sanitized.status).toBe(0); + expect(sanitized.stderr).toContain('invalid_check_code'); + expect(sanitized.stderr).toContain('token=[redacted]'); + expect(sanitized.stderr).not.toMatch(/fixture-secret|private\.invalid|\/tmp\/private|10\.0\.0\.1|raw-evidence/); + + writeFileSync(reportPath, '{"checks":[{"message":"token malformed-secret"}'); + const malformed = spawnSync(process.execPath, ['-', reportPath], { + encoding: 'utf8', + input: sanitizerScript, + }); + expect(malformed.status).toBe(1); + expect(malformed.stderr).toBe(''); + expect(malformed.stdout).toBe(''); + } finally { + rmSync(reportDir, { recursive: true, force: true }); + } }); it('rejects dirty or untracked vendored bridge bytes in strict provenance checks', () => { From 87387f440b2d73464fd4dc637d198b4ed1f1dda8 Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 23:46:29 +0700 Subject: [PATCH 3/7] fix(release): never log pre-canary report messages --- .github/workflows/evaos-beta-rc-canary.yml | 14 +++----------- .../prepareEvaosDesktopBridgeResource.test.ts | 10 +++++++--- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/evaos-beta-rc-canary.yml b/.github/workflows/evaos-beta-rc-canary.yml index 209cbe9d7cc..6dba977abdc 100644 --- a/.github/workflows/evaos-beta-rc-canary.yml +++ b/.github/workflows/evaos-beta-rc-canary.yml @@ -681,16 +681,6 @@ jobs: const reportPath = process.argv[2]; try { const report = JSON.parse(fs.readFileSync(reportPath, 'utf8')); - const sanitizeMessage = (value) => - String(value ?? '') - .replace(/https?:\/\/\S+/gi, '[redacted]') - .replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, '[redacted]') - .replace(/\/(?:[^/\s]+\/)*[^\s]*/g, '[redacted]') - .replace(/\b(token|secret|key|authorization|bearer)\b(?:\s*[:=]\s*|\s+)\S+/gi, '$1=[redacted]') - .replace(/[^\x20-\x7e]/g, '?') - .replace(/\s+/g, ' ') - .trim() - .slice(0, 240); const checks = Array.isArray(report.checks) ? report.checks : []; const failedChecks = checks.filter((check) => check && check.status === 'fail'); const sanitizedChecks = failedChecks.map((check) => ({ @@ -699,7 +689,9 @@ jobs: ? check.code : 'invalid_check_code', status: ['fail', 'warn'].includes(check.status) ? check.status : 'unknown', - message: sanitizeMessage(check.message), + // Never echo report-provided messages. They are free-form and + // can contain credentials, network coordinates, or local paths. + message: 'Installed candidate did not satisfy this pre-canary check.', })); if (sanitizedChecks.length === 0) { console.error('Pre-canary failed without a non-passing sanitized check.'); diff --git a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts index 0e278f755fc..2b6bdf0dbb7 100644 --- a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts +++ b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts @@ -301,7 +301,9 @@ describe('prepareEvaosDesktopBridgeResource', () => { { code: 'unsafe/code', status: 'fail', - message: 'token fixture-secret https://private.invalid /tmp/private 10.0.0.1', + message: + 'Authorization: Bearer fixture-secret eyJhbGciOiJIUzI1NiJ9.fixture.signature 2001:db8::1 /tmp/private path\n' + + 'x'.repeat(300), evidence: 'raw-evidence', }, ], @@ -314,8 +316,10 @@ describe('prepareEvaosDesktopBridgeResource', () => { }); expect(sanitized.status).toBe(0); expect(sanitized.stderr).toContain('invalid_check_code'); - expect(sanitized.stderr).toContain('token=[redacted]'); - expect(sanitized.stderr).not.toMatch(/fixture-secret|private\.invalid|\/tmp\/private|10\.0\.0\.1|raw-evidence/); + expect(sanitized.stderr).toContain('Installed candidate did not satisfy this pre-canary check.'); + expect(sanitized.stderr).not.toMatch( + /fixture-secret|eyJhbGciOiJIUzI1NiJ9|2001:db8::1|\/tmp\/private|raw-evidence|x{20}/ + ); writeFileSync(reportPath, '{"checks":[{"message":"token malformed-secret"}'); const malformed = spawnSync(process.execPath, ['-', reportPath], { From d91d09448aef0b8090b9cc3abecea54ce64ad062 Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 23:47:00 +0700 Subject: [PATCH 4/7] fix(release): preserve failed pre-canary exit status --- .github/workflows/evaos-beta-rc-canary.yml | 9 ++++----- .../process/prepareEvaosDesktopBridgeResource.test.ts | 5 ++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/evaos-beta-rc-canary.yml b/.github/workflows/evaos-beta-rc-canary.yml index 6dba977abdc..48a4ad40a90 100644 --- a/.github/workflows/evaos-beta-rc-canary.yml +++ b/.github/workflows/evaos-beta-rc-canary.yml @@ -669,14 +669,12 @@ jobs: 2> "$PROOF_DIR/installed-candidate-pre-canary.stderr.txt" PRE_CANARY_EXIT=$? set -e - if [ -f "$PRE_CANARY_DIR/qa-report.json" ]; then - cp "$PRE_CANARY_DIR/qa-report.json" "$PROOF_DIR/installed-candidate-pre-canary.json" - fi + PRE_CANARY_REPORT="$PRE_CANARY_DIR/qa-report.json" if [ "$PRE_CANARY_EXIT" -ne 0 ]; then echo "Pre-canary exit code: $PRE_CANARY_EXIT" - if [ -f "$PROOF_DIR/installed-candidate-pre-canary.json" ]; then + if [ -f "$PRE_CANARY_REPORT" ]; then set +e - node - "$PROOF_DIR/installed-candidate-pre-canary.json" <<'NODE' + node - "$PRE_CANARY_REPORT" <<'NODE' const fs = require('fs'); const reportPath = process.argv[2]; try { @@ -717,6 +715,7 @@ jobs: echo "::error::Installed candidate pre-canary failed; see the sanitized check summary above." exit "$PRE_CANARY_EXIT" fi + cp "$PRE_CANARY_REPORT" "$PROOF_DIR/installed-candidate-pre-canary.json" TOKEN_FILE="$HOME/Library/Application Support/evaos-desktop-bridge/connector.token" for _attempt in $(seq 1 30); do diff --git a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts index 2b6bdf0dbb7..185eb2f9d1a 100644 --- a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts +++ b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts @@ -268,7 +268,7 @@ describe('prepareEvaosDesktopBridgeResource', () => { it('captures pre-canary failures as sanitized check summaries before preserving the exit code', () => { const workflow = readFileSync(join(process.cwd(), '.github', 'workflows', 'evaos-beta-rc-canary.yml'), 'utf8'); - const sanitizerCommand = 'node - "$PROOF_DIR/installed-candidate-pre-canary.json" <<\'NODE\''; + const sanitizerCommand = 'node - "$PRE_CANARY_REPORT" <<\'NODE\''; const sanitizerStart = workflow.indexOf(sanitizerCommand); const sanitizerBodyStart = workflow.indexOf('\n', sanitizerStart) + 1; const sanitizerBodyEnd = workflow.indexOf('\n NODE', sanitizerBodyStart); @@ -287,6 +287,9 @@ describe('prepareEvaosDesktopBridgeResource', () => { expect(failureBlock).toContain('Pre-canary exit code: $PRE_CANARY_EXIT'); expect(failureBlock).toContain('Pre-canary sanitized check: ${JSON.stringify(check)}'); expect(failureBlock).toContain('exit "$PRE_CANARY_EXIT"'); + expect(failureBlock.indexOf('exit "$PRE_CANARY_EXIT"')).toBeLessThan( + failureBlock.indexOf('cp "$PRE_CANARY_REPORT" "$PROOF_DIR/installed-candidate-pre-canary.json"') + ); expect(failureBlock).not.toMatch(/\.evidence|\.inventory/); expect(sanitizerStart).toBeGreaterThan(-1); expect(sanitizerBodyEnd).toBeGreaterThan(sanitizerBodyStart); From 3f83db2688d35debdae380e9b212e957bcaf54a1 Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 23:47:31 +0700 Subject: [PATCH 5/7] test(release): keep uninspectable duplicates fail closed --- .../prepareEvaosDesktopBridgeResource.test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts index 185eb2f9d1a..389e6c76aed 100644 --- a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts +++ b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts @@ -684,6 +684,7 @@ describe('prepareEvaosDesktopBridgeResource', () => { 'from evaos_desktop_bridge import pre_canary', 'from pathlib import Path', 'from tempfile import TemporaryDirectory', + 'from unittest.mock import patch', 'assert pre_canary.DEFAULT_CANONICAL_PATH == "/Applications/evaOS Workbench.app"', 'assert pre_canary.DEFAULT_BUNDLE_ID == "com.evaos.workbench"', 'current = pre_canary.AppBundle(path=pre_canary.DEFAULT_CANONICAL_PATH, bundle_id=pre_canary.DEFAULT_BUNDLE_ID, version="2.1.36", build="2.1.36", team_id=pre_canary.DEFAULT_TEAM_ID)', @@ -716,6 +717,19 @@ describe('prepareEvaosDesktopBridgeResource', () => { ' duplicate_report = pre_canary.evaluate_inventory(duplicate_inventory, canonical_path=str(canonical))', ' assert not duplicate_report.ok, duplicate_report.to_dict()', ' assert "duplicate_registered_workbench_app" in {check.code for check in duplicate_report.checks}', + ' unverifiable = Path(inventory_root) / "permission-blocked" / "evaOS Workbench.app"', + ' original_stat = Path.stat', + ' def guarded_stat(path, *args, **kwargs):', + ' if path == unverifiable:', + ' raise PermissionError("fixture permission boundary")', + ' return original_stat(path, *args, **kwargs)', + ' pre_canary._mdfind_bundle_paths = lambda _bundle_id: (str(canonical), str(unverifiable), str(unverifiable))', + ' with patch.object(Path, "stat", guarded_stat):', + ' unverifiable_inventory = pre_canary.gather_inventory(canonical_path=str(canonical), artifact_roots=())', + ' assert unverifiable_inventory.registered_paths == (str(canonical), str(unverifiable)), unverifiable_inventory.to_dict()', + ' unverifiable_report = pre_canary.evaluate_inventory(unverifiable_inventory, canonical_path=str(canonical))', + ' assert not unverifiable_report.ok, unverifiable_report.to_dict()', + ' assert "duplicate_registered_workbench_app" in {check.code for check in unverifiable_report.checks}', 'with TemporaryDirectory() as artifact_dir:', ' report_path = pre_canary._write_report(report.to_dict(), Path(artifact_dir))', ' assert report_path.name == "qa-report.json" and report_path.is_file()', From 1f82d4b3750dc2f41f454eb5edf6d88fa370e4f1 Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 15 Jul 2026 23:56:21 +0700 Subject: [PATCH 6/7] fix(release): keep pre-canary scans fail closed --- .../src/evaos_desktop_bridge/pre_canary.py | 34 +++++++++++++++---- .../prepareEvaosDesktopBridgeResource.test.ts | 3 ++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py b/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py index da88749af17..7f4ac446b4c 100644 --- a/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py +++ b/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py @@ -27,9 +27,9 @@ ) DEFAULT_TEAM_ID = "TC6MS3T6NN" COMPUTER_USE_CLIENT_SUFFIX = "SkyComputerUseClient mcp" -# Optional developer/canary artifact locations. Missing roots are ignored, and -# callers can override them with --canary-artifact-root or -# EVAOS_CANARY_ARTIFACT_ROOTS. +# Optional developer/canary artifact locations. Missing roots are ignored. +# EVAOS_CANARY_ARTIFACT_ROOTS replaces these defaults, while each +# --canary-artifact-root adds a run-specific root to that baseline. DEFAULT_ARTIFACT_ROOTS = ( "/Volumes/LEXAR/Codex/artifacts", "/Volumes/LEXAR/Codex/evaos-provider-auth-96-canary", @@ -274,7 +274,9 @@ def gather_inventory( ) artifact_paths = tuple(_artifact_workbench_bundle_paths(artifact_roots=artifact_roots)) bundle_paths = _unique_paths((*registered_paths, *artifact_paths, canonical_path)) - app_bundles = tuple(_read_app_bundle(path) for path in bundle_paths if Path(path).exists()) + app_bundles = tuple( + bundle for path in bundle_paths if (bundle := _read_app_bundle_if_inspectable(path)) is not None + ) processes = tuple(_process_inventory()) return WorkbenchInventory(registered_paths=registered_paths, app_bundles=app_bundles, processes=processes) @@ -303,11 +305,15 @@ def main(argv: Sequence[str] | None = None) -> int: "--canary-artifact-root", action="append", dest="artifact_roots", - help="Optional root to scan for stale EvaDesktop.app canary artifacts. Repeatable; overrides EVAOS_CANARY_ARTIFACT_ROOTS/default roots.", + help="Additional root to scan for stale Workbench canary artifacts. Repeatable; appends to EVAOS_CANARY_ARTIFACT_ROOTS/default roots.", ) args = parser.parse_args(argv) - inventory = gather_inventory(canonical_path=args.canonical_path, bundle_id=args.bundle_id, artifact_roots=args.artifact_roots) + inventory = gather_inventory( + canonical_path=args.canonical_path, + bundle_id=args.bundle_id, + artifact_roots=_artifact_roots_with_additions(args.artifact_roots), + ) report = evaluate_inventory( inventory, canonical_path=args.canonical_path, @@ -429,6 +435,18 @@ def _registered_path_exists_or_is_unverifiable(path: str) -> bool: return True +def _read_app_bundle_if_inspectable(path: str) -> AppBundle | None: + try: + Path(path).stat() + except OSError: + # The registered-path inventory remains authoritative for duplicate + # failures. Avoid a Python-version-dependent Path.exists() exception + # while keeping an uninspectable canonical bundle fail-closed as + # missing from the readable bundle inventory. + return None + return _read_app_bundle(path) + + def _run(command: Sequence[str]) -> str: try: completed = subprocess.run(command, check=False, capture_output=True, text=True, timeout=10) @@ -472,6 +490,10 @@ def _artifact_workbench_bundle_paths(*, artifact_roots: Sequence[str] | None = N return tuple(paths) +def _artifact_roots_with_additions(additional_roots: Sequence[str] | None) -> tuple[str, ...]: + return _unique_paths((*_artifact_roots_from_environment(), *(additional_roots or ()))) + + def _is_workbench_app_artifact_name(name: str) -> bool: candidate = name.casefold() for stem in WORKBENCH_APP_NAME_STEMS: diff --git a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts index 389e6c76aed..2e979f3566f 100644 --- a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts +++ b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts @@ -687,6 +687,8 @@ describe('prepareEvaosDesktopBridgeResource', () => { 'from unittest.mock import patch', 'assert pre_canary.DEFAULT_CANONICAL_PATH == "/Applications/evaOS Workbench.app"', 'assert pre_canary.DEFAULT_BUNDLE_ID == "com.evaos.workbench"', + 'pre_canary._artifact_roots_from_environment = lambda: ("/baseline-artifacts", "/shared-artifacts")', + 'assert pre_canary._artifact_roots_with_additions(("/runner-temp", "/shared-artifacts")) == ("/baseline-artifacts", "/shared-artifacts", "/runner-temp")', 'current = pre_canary.AppBundle(path=pre_canary.DEFAULT_CANONICAL_PATH, bundle_id=pre_canary.DEFAULT_BUNDLE_ID, version="2.1.36", build="2.1.36", team_id=pre_canary.DEFAULT_TEAM_ID)', 'current_process = pre_canary.ProcessInfo(pid=1, command="/Applications/evaOS Workbench.app/Contents/MacOS/evaOS Workbench", path=pre_canary.DEFAULT_CANONICAL_PATH, kind="workbench")', 'inventory = pre_canary.WorkbenchInventory(registered_paths=(pre_canary.DEFAULT_CANONICAL_PATH,), app_bundles=(current,), processes=(current_process,))', @@ -727,6 +729,7 @@ describe('prepareEvaosDesktopBridgeResource', () => { ' with patch.object(Path, "stat", guarded_stat):', ' unverifiable_inventory = pre_canary.gather_inventory(canonical_path=str(canonical), artifact_roots=())', ' assert unverifiable_inventory.registered_paths == (str(canonical), str(unverifiable)), unverifiable_inventory.to_dict()', + ' assert str(unverifiable) not in {bundle.path for bundle in unverifiable_inventory.app_bundles}, unverifiable_inventory.to_dict()', ' unverifiable_report = pre_canary.evaluate_inventory(unverifiable_inventory, canonical_path=str(canonical))', ' assert not unverifiable_report.ok, unverifiable_report.to_dict()', ' assert "duplicate_registered_workbench_app" in {check.code for check in unverifiable_report.checks}', From b2182ab75ac2e79e9ea8ecc8391a114d7b44475d Mon Sep 17 00:00:00 2001 From: Eva Date: Thu, 16 Jul 2026 00:02:03 +0700 Subject: [PATCH 7/7] fix(release): bound fail-closed pre-canary evidence --- .github/workflows/evaos-beta-rc-canary.yml | 6 ++- .../src/evaos_desktop_bridge/pre_canary.py | 12 +++--- .../prepareEvaosDesktopBridgeResource.test.ts | 40 ++++++++++++++----- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/.github/workflows/evaos-beta-rc-canary.yml b/.github/workflows/evaos-beta-rc-canary.yml index 48a4ad40a90..46cdbe60eb3 100644 --- a/.github/workflows/evaos-beta-rc-canary.yml +++ b/.github/workflows/evaos-beta-rc-canary.yml @@ -678,6 +678,7 @@ jobs: const fs = require('fs'); const reportPath = process.argv[2]; try { + const MAX_SANITIZED_CHECKS = 20; const report = JSON.parse(fs.readFileSync(reportPath, 'utf8')); const checks = Array.isArray(report.checks) ? report.checks : []; const failedChecks = checks.filter((check) => check && check.status === 'fail'); @@ -694,9 +695,12 @@ jobs: if (sanitizedChecks.length === 0) { console.error('Pre-canary failed without a non-passing sanitized check.'); } else { - for (const check of sanitizedChecks) { + for (const check of sanitizedChecks.slice(0, MAX_SANITIZED_CHECKS)) { console.error(`Pre-canary sanitized check: ${JSON.stringify(check)}`); } + if (sanitizedChecks.length > MAX_SANITIZED_CHECKS) { + console.error('Additional pre-canary failed checks were omitted from the sanitized log.'); + } } } catch { // The shell emits one fixed fallback message. Never let a parser diff --git a/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py b/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py index 7f4ac446b4c..d139c635539 100644 --- a/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py +++ b/resources/evaos-beta/bridge/src/evaos_desktop_bridge/pre_canary.py @@ -438,12 +438,14 @@ def _registered_path_exists_or_is_unverifiable(path: str) -> bool: def _read_app_bundle_if_inspectable(path: str) -> AppBundle | None: try: Path(path).stat() - except OSError: - # The registered-path inventory remains authoritative for duplicate - # failures. Avoid a Python-version-dependent Path.exists() exception - # while keeping an uninspectable canonical bundle fail-closed as - # missing from the readable bundle inventory. + except (FileNotFoundError, NotADirectoryError): return None + except OSError: + # Preserve uninspectable bundles as path-only evidence. Registered + # duplicates still fail from registered_paths, while artifact-only or + # canonical paths fail their bundle-presence/identity checks instead + # of disappearing from the inventory. + return AppBundle(path=path) return _read_app_bundle(path) diff --git a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts index 2e979f3566f..71277acde1f 100644 --- a/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts +++ b/tests/unit/process/prepareEvaosDesktopBridgeResource.test.ts @@ -300,16 +300,14 @@ describe('prepareEvaosDesktopBridgeResource', () => { writeFileSync( reportPath, JSON.stringify({ - checks: [ - { - code: 'unsafe/code', - status: 'fail', - message: - 'Authorization: Bearer fixture-secret eyJhbGciOiJIUzI1NiJ9.fixture.signature 2001:db8::1 /tmp/private path\n' + - 'x'.repeat(300), - evidence: 'raw-evidence', - }, - ], + checks: Array.from({ length: 25 }, (_, index) => ({ + code: index === 0 ? 'unsafe/code' : `fixture_${index}`, + status: 'fail', + message: + 'Authorization: Bearer fixture-secret eyJhbGciOiJIUzI1NiJ9.fixture.signature 2001:db8::1 /tmp/private path\n' + + 'x'.repeat(300), + evidence: 'raw-evidence', + })), inventory: { registered_paths: ['/tmp/private'] }, }) ); @@ -320,6 +318,14 @@ describe('prepareEvaosDesktopBridgeResource', () => { expect(sanitized.status).toBe(0); expect(sanitized.stderr).toContain('invalid_check_code'); expect(sanitized.stderr).toContain('Installed candidate did not satisfy this pre-canary check.'); + expect( + sanitized.stderr.split('\n').filter((line) => line.startsWith('Pre-canary sanitized check: ')) + ).toHaveLength(20); + expect( + sanitized.stderr + .split('\n') + .filter((line) => line === 'Additional pre-canary failed checks were omitted from the sanitized log.') + ).toHaveLength(1); expect(sanitized.stderr).not.toMatch( /fixture-secret|eyJhbGciOiJIUzI1NiJ9|2001:db8::1|\/tmp\/private|raw-evidence|x{20}/ ); @@ -729,10 +735,22 @@ describe('prepareEvaosDesktopBridgeResource', () => { ' with patch.object(Path, "stat", guarded_stat):', ' unverifiable_inventory = pre_canary.gather_inventory(canonical_path=str(canonical), artifact_roots=())', ' assert unverifiable_inventory.registered_paths == (str(canonical), str(unverifiable)), unverifiable_inventory.to_dict()', - ' assert str(unverifiable) not in {bundle.path for bundle in unverifiable_inventory.app_bundles}, unverifiable_inventory.to_dict()', + ' assert str(unverifiable) in {bundle.path for bundle in unverifiable_inventory.app_bundles}, unverifiable_inventory.to_dict()', ' unverifiable_report = pre_canary.evaluate_inventory(unverifiable_inventory, canonical_path=str(canonical))', ' assert not unverifiable_report.ok, unverifiable_report.to_dict()', ' assert "duplicate_registered_workbench_app" in {check.code for check in unverifiable_report.checks}', + ' uninspectable_artifact = Path(inventory_root) / "artifact-only" / "EvaDesktop.app"', + ' def artifact_guarded_stat(path, *args, **kwargs):', + ' if path == uninspectable_artifact:', + ' raise PermissionError("artifact fixture permission boundary")', + ' return original_stat(path, *args, **kwargs)', + ' pre_canary._mdfind_bundle_paths = lambda _bundle_id: (str(canonical),)', + ' with patch.object(pre_canary, "_artifact_workbench_bundle_paths", return_value=(str(uninspectable_artifact),)), patch.object(Path, "stat", artifact_guarded_stat):', + ' artifact_inventory = pre_canary.gather_inventory(canonical_path=str(canonical), artifact_roots=())', + ' assert str(uninspectable_artifact) in {bundle.path for bundle in artifact_inventory.app_bundles}, artifact_inventory.to_dict()', + ' artifact_report = pre_canary.evaluate_inventory(artifact_inventory, canonical_path=str(canonical))', + ' assert not artifact_report.ok, artifact_report.to_dict()', + ' assert "stale_workbench_app_bundle_present" in {check.code for check in artifact_report.checks}', 'with TemporaryDirectory() as artifact_dir:', ' report_path = pre_canary._write_report(report.to_dict(), Path(artifact_dir))', ' assert report_path.name == "qa-report.json" and report_path.is_file()',