From 44609f3da004348f5b0aa0fd08ca945a27f96e2f Mon Sep 17 00:00:00 2001 From: sudacode Date: Fri, 15 May 2026 18:58:42 -0700 Subject: [PATCH] fix(macos): validate PID and socket before reporting window as minimized - Move PID extraction and app PID match before minimized check in windowStateFromAccessibilityAPI - Add test asserting correct validation order in get-mpv-window-macos.swift - Include new test in test:fast suite --- package.json | 2 +- scripts/get-mpv-window-macos.swift | 18 +++++++++------ scripts/get-mpv-window-macos.test.ts | 33 ++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 scripts/get-mpv-window-macos.test.ts diff --git a/package.json b/package.json index 9e3ca7b8..eec5d91c 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "test:launcher": "bun run test:launcher:src", "test:core": "bun run test:core:src", "test:subtitle": "bun run test:subtitle:src", - "test:fast": "bun run test:config:src && bun run test:core:src && bun run test:docs:kb && bun test src/main-entry-runtime.test.ts src/anki-integration.test.ts src/anki-integration/card-creation-manual-update.test.ts src/anki-integration/anki-connect-proxy.test.ts src/anki-integration/field-grouping-workflow.test.ts src/anki-integration/field-grouping.test.ts src/anki-integration/field-grouping-merge.test.ts src/release-workflow.test.ts src/prerelease-workflow.test.ts src/ci-workflow.test.ts scripts/build-changelog.test.ts scripts/electron-builder-after-pack.test.ts scripts/mkv-to-readme-video.test.ts scripts/run-coverage-lane.test.ts scripts/update-aur-package.test.ts && bun test src/core/services/immersion-tracker/__tests__/query.test.ts src/core/services/immersion-tracker/__tests__/query-split-modules.test.ts && bun run tsc && bun test dist/main/runtime/registry.test.js", + "test:fast": "bun run test:config:src && bun run test:core:src && bun run test:docs:kb && bun test src/main-entry-runtime.test.ts src/anki-integration.test.ts src/anki-integration/card-creation-manual-update.test.ts src/anki-integration/anki-connect-proxy.test.ts src/anki-integration/field-grouping-workflow.test.ts src/anki-integration/field-grouping.test.ts src/anki-integration/field-grouping-merge.test.ts src/release-workflow.test.ts src/prerelease-workflow.test.ts src/ci-workflow.test.ts scripts/build-changelog.test.ts scripts/electron-builder-after-pack.test.ts scripts/get-mpv-window-macos.test.ts scripts/mkv-to-readme-video.test.ts scripts/run-coverage-lane.test.ts scripts/update-aur-package.test.ts && bun test src/core/services/immersion-tracker/__tests__/query.test.ts src/core/services/immersion-tracker/__tests__/query-split-modules.test.ts && bun run tsc && bun test dist/main/runtime/registry.test.js", "generate:config-example": "bun run src/generate-config-example.ts", "verify:config-example": "bun run src/verify-config-example.ts", "start": "bun run build && electron . --start", diff --git a/scripts/get-mpv-window-macos.swift b/scripts/get-mpv-window-macos.swift index 7439ce3e..059c951b 100644 --- a/scripts/get-mpv-window-macos.swift +++ b/scripts/get-mpv-window-macos.swift @@ -174,22 +174,26 @@ private func windowStateFromAccessibilityAPI() -> WindowLookupResult? { } for window in windows { - var minimizedRef: CFTypeRef? - let minimizedStatus = AXUIElementCopyAttributeValue(window, kAXMinimizedAttribute as CFString, &minimizedRef) - if minimizedStatus == .success, let minimized = minimizedRef as? Bool, minimized { - foundMinimizedTargetWindow = true - continue - } - var windowPid: pid_t = 0 if AXUIElementGetPid(window, &windowPid) != .success { continue } + if windowPid != app.processIdentifier { + continue + } + if !windowHasTargetSocket(windowPid) { continue } + var minimizedRef: CFTypeRef? + let minimizedStatus = AXUIElementCopyAttributeValue(window, kAXMinimizedAttribute as CFString, &minimizedRef) + if minimizedStatus == .success, let minimized = minimizedRef as? Bool, minimized { + foundMinimizedTargetWindow = true + continue + } + if let geometry = geometryFromAXWindow(window) { return .visible( WindowState( diff --git a/scripts/get-mpv-window-macos.test.ts b/scripts/get-mpv-window-macos.test.ts new file mode 100644 index 00000000..0f955cf6 --- /dev/null +++ b/scripts/get-mpv-window-macos.test.ts @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import test from 'node:test'; + +const source = readFileSync('scripts/get-mpv-window-macos.swift', 'utf8'); + +test('minimized Accessibility windows are validated by PID and socket before reporting minimized', () => { + const minimizedAssignmentIndex = source.indexOf('foundMinimizedTargetWindow = true'); + assert.notEqual(minimizedAssignmentIndex, -1); + + const loopStartIndex = source.lastIndexOf('for window in windows', minimizedAssignmentIndex); + assert.notEqual(loopStartIndex, -1); + + const pidExtractionIndex = source.indexOf( + 'AXUIElementGetPid(window, &windowPid)', + loopStartIndex, + ); + const appPidMatchIndex = source.indexOf('windowPid != app.processIdentifier', loopStartIndex); + const socketCheckIndex = source.indexOf('if !windowHasTargetSocket(windowPid)', loopStartIndex); + + assert.ok( + pidExtractionIndex > loopStartIndex && pidExtractionIndex < minimizedAssignmentIndex, + 'window PID must be extracted before accepting a minimized window', + ); + assert.ok( + appPidMatchIndex > pidExtractionIndex && appPidMatchIndex < minimizedAssignmentIndex, + 'window PID must match the owning app before accepting a minimized window', + ); + assert.ok( + socketCheckIndex > appPidMatchIndex && socketCheckIndex < minimizedAssignmentIndex, + 'target socket must be validated before accepting a minimized window', + ); +});