import { createHash } from "node:crypto"; const SAFE_DISMISS_BUTTONS = [ "稍后", "跳过", "以后再说", "不,谢谢", "Not now", "Skip", "Later", "Cancel", "Maybe later", "No thanks", ]; const BLOCKED_TEXT_PATTERNS = [ /screen recording/i, /accessibility/i, /input monitoring/i, /full disk access/i, /keychain/i, /administrator/i, /apple id/i, /user account control/i, /make changes to your device/i, /屏幕录制/, /辅助功能/, /输入监控/, /完整磁盘访问/, /钥匙串/, /管理员密码/, /用户帐户控制/, /用户账户控制/, ]; export function normalizeDialogText(value) { return String(value || "").replace(/\s+/g, " ").trim(); } export function normalizeDialogSnapshot(input = {}) { const buttons = Array.isArray(input.buttons) ? input.buttons.map(normalizeDialogText).filter(Boolean) : []; const appName = normalizeDialogText(input.appName || input.app || "Unknown App"); return { platform: normalizeDialogText(input.platform || process.platform || "unknown"), deviceId: normalizeDialogText(input.deviceId || "unknown-device"), appName, appBundleId: normalizeDialogText(input.appBundleId || input.appId || appName || "unknown-app"), title: normalizeDialogText(input.title), text: normalizeDialogText(input.text), buttons, raw: input.raw, }; } function parseSnapshotJson(raw, sourceName) { const value = String(raw || "").trim(); if (!value) { return undefined; } const parsed = JSON.parse(value); if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { throw new Error(`INVALID_DIALOG_GUARD_SNAPSHOT:${sourceName}`); } return parsed; } export function readDialogSnapshotFromEnv(env = process.env, platform = process.platform) { const normalizedPlatform = normalizeDialogText(platform || process.platform || "unknown"); const platformSnapshotKey = normalizedPlatform === "darwin" ? "BOSS_MAC_DIALOG_GUARD_SNAPSHOT_JSON" : normalizedPlatform === "win32" ? "BOSS_WINDOWS_DIALOG_GUARD_SNAPSHOT_JSON" : ""; const parsed = parseSnapshotJson(env.BOSS_DIALOG_GUARD_SNAPSHOT_JSON, "BOSS_DIALOG_GUARD_SNAPSHOT_JSON") || (platformSnapshotKey ? parseSnapshotJson(env[platformSnapshotKey], platformSnapshotKey) : undefined); if (!parsed) { return undefined; } return normalizeDialogSnapshot({ ...parsed, platform: parsed.platform || normalizedPlatform, }); } function hash(value) { return createHash("sha256").update(String(value || "")).digest("hex").slice(0, 16); } export function createDialogSignature(snapshotInput = {}) { const snapshot = normalizeDialogSnapshot(snapshotInput); const titleHash = hash(snapshot.title.toLowerCase()); const textHash = hash(snapshot.text.toLowerCase()); const buttonHash = hash(snapshot.buttons.join("|").toLowerCase()); return { id: hash([ snapshot.platform, snapshot.deviceId, snapshot.appBundleId, titleHash, textHash, buttonHash, ].join("|")), scopeKey: [snapshot.platform, snapshot.deviceId, snapshot.appBundleId].join(":"), platform: snapshot.platform, deviceId: snapshot.deviceId, appBundleId: snapshot.appBundleId, titleHash, textHash, buttonHash, }; } function textMatchesAny(text, patterns) { return patterns.some((pattern) => pattern.test(text)); } function findSafeDismissButton(buttons) { return buttons.find((button) => SAFE_DISMISS_BUTTONS.some((candidate) => candidate.toLowerCase() === button.toLowerCase()), ); } function isBlockedPrompt(snapshot) { const combined = `${snapshot.title} ${snapshot.text}`; return textMatchesAny(combined, BLOCKED_TEXT_PATTERNS); } export function evaluateDialogSnapshot(snapshotInput = {}) { const snapshot = normalizeDialogSnapshot(snapshotInput); const signature = createDialogSignature(snapshot); if (isBlockedPrompt(snapshot)) { return { disposition: "needs_user_action", kind: "permission_required", risk: "high", action: "pause_for_user", signature, }; } const safeButton = findSafeDismissButton(snapshot.buttons); if (safeButton) { return { disposition: "auto_action", kind: "safe_dismiss", risk: "low", action: "click_button", button: safeButton, signature, }; } return { disposition: "needs_user_action", kind: "unknown_dialog", risk: "medium", action: "pause_for_user", signature, }; } export function buildDialogAuditEntry({ requestId, snapshot: snapshotInput, decision, handledAt = new Date().toISOString() }) { const snapshot = normalizeDialogSnapshot(snapshotInput); const signature = decision?.signature || createDialogSignature(snapshot); return { kind: "desktop_dialog_guard", requestId: requestId || undefined, handledAt, platform: snapshot.platform, appName: snapshot.appName, dialogId: signature.id, risk: decision?.risk || "medium", disposition: decision?.disposition || "unknown", action: decision?.action || "pause_for_user", button: decision?.button || undefined, policyKind: decision?.kind || "unknown_dialog", }; } export function buildDialogInterventionResult({ requestId, snapshot: snapshotInput, decision }) { const snapshot = normalizeDialogSnapshot(snapshotInput); const signature = decision?.signature || createDialogSignature(snapshot); const blocked = decision?.risk === "high"; return { status: "needs_user_action", requestId: requestId || undefined, kind: "dialog_intervention_required", dialogId: signature.id, risk: decision?.risk || "medium", summary: `${snapshot.appName} 弹窗需要确认:${snapshot.title || snapshot.text || "未知弹窗"}`, recommendedAction: blocked ? "handled_on_device" : "allow_once", availableActions: blocked ? ["handled_on_device", "cancel_task"] : ["allow_once", "allow_for_device_dialog", "deny"], platform: snapshot.platform, appName: snapshot.appName, }; }