From d6291ebf0d16bd0be25dd289907ed21250c602fe Mon Sep 17 00:00:00 2001 From: Fei-Away <100042407+Fei-Away@users.noreply.github.com> Date: Thu, 30 Jul 2026 06:44:10 +0800 Subject: [PATCH] Fix v1.5.7 installer and theme verification issues --- macos/CHANGELOG.md | 8 ++++++ macos/VERSION | 2 +- macos/assets/dream-skin.css | 4 +-- macos/assets/safe-css-validator.mjs | 2 +- macos/package.json | 2 +- macos/scripts/common-macos.sh | 2 +- macos/scripts/injector.mjs | 7 ++++-- macos/tests/run-tests.sh | 4 +-- macos/tests/runtime-css-nested-has.test.mjs | 14 +++++++++++ macos/tests/safe-css-validator.test.mjs | 2 +- macos/tests/window-readiness.test.mjs | 20 +++++++++++++++ runtime/dream-skin.css | 4 +-- runtime/safe-css-validator.mjs | 2 +- windows/CHANGELOG.md | 8 ++++++ windows/VERSION | 2 +- windows/assets/dream-skin.css | 4 +-- windows/assets/safe-css-validator.mjs | 2 +- windows/scripts/common-windows.ps1 | 19 ++++++++++++++ windows/scripts/injector.mjs | 11 ++++++-- .../tests/injector-window-readiness.test.mjs | 24 +++++++++++++++++- windows/tests/installer-static.tests.ps1 | 25 +++++++++++++++++-- .../start-verified-skin-preserved.tests.ps1 | 2 +- 22 files changed, 146 insertions(+), 24 deletions(-) diff --git a/macos/CHANGELOG.md b/macos/CHANGELOG.md index 6f9d123e..9c8363e3 100644 --- a/macos/CHANGELOG.md +++ b/macos/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### 修复 + +- 修复 Codex Desktop 26.721.x 首页在 `home-icon` 延迟渲染时被误判为注入校验失败的问题(#306)。校验现在复用已由首页内容信号解析出的 `[role="main"]` 容器;严格的 `home-icon` 路径仍优先,旧版行为不变。 +- 修复显式浅色、宽幅 artwork 的 `taskMode: "full"` 任务页 Markdown 继承原生深色壳白色前景的问题(#309)。完整模式现与 ambient/banner 一样强制使用主题文本色,并保留浅色壳对比阴影。 +- 修复社区主题 ZIP 导入拒绝 `backdrop-filter: blur(var(--ds-theme-surface-blur))` 的问题(#312)。Safe CSS 仍只允许 `none`、0-20px blur 或注册的主题 blur 变量,不放宽到任意 filter 函数。 + ## 1.5.6 — 2026-07-26 ### 安全 diff --git a/macos/VERSION b/macos/VERSION index eac1e0ad..f01291b8 100644 --- a/macos/VERSION +++ b/macos/VERSION @@ -1 +1 @@ -1.5.6 +1.5.7 diff --git a/macos/assets/dream-skin.css b/macos/assets/dream-skin.css index bd112ec0..2656280b 100644 --- a/macos/assets/dream-skin.css +++ b/macos/assets/dream-skin.css @@ -444,7 +444,7 @@ html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream- backdrop-filter: none !important; } -html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"] +html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"] main.main-surface:not(:has([role="main"])) [class*="_markdown"] { color: var(--ds-text) !important; text-shadow: @@ -452,7 +452,7 @@ html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream- 0 0 10px rgb(var(--ds-bg-rgb) / .58); } -html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"] +html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"] main.main-surface:not(:has([role="main"])) [class*="_markdown"] { text-shadow: 0 1px 2px rgb(var(--ds-panel-rgb) / .92), diff --git a/macos/assets/safe-css-validator.mjs b/macos/assets/safe-css-validator.mjs index 8321e8ea..cf9572ad 100644 --- a/macos/assets/safe-css-validator.mjs +++ b/macos/assets/safe-css-validator.mjs @@ -278,7 +278,7 @@ function validatePropertyValue(property, value) { } if (property === "backdrop-filter") { if (value.toLowerCase() === "none") return true; - const match = value.match(/^blur\(\s*([^\s)]+)\s*\)$/i); + const match = value.match(/^blur\(\s*(.+?)\s*\)$/i); return Boolean(match && ( registeredVar(match[1], new Set(["--ds-theme-surface-blur"])) || zeroOrPx(match[1], 0, 20) diff --git a/macos/package.json b/macos/package.json index b992b814..5e1fc333 100644 --- a/macos/package.json +++ b/macos/package.json @@ -1,6 +1,6 @@ { "name": "codex-dream-skin-studio", - "version": "1.5.6", + "version": "1.5.7", "private": true, "type": "module", "scripts": { diff --git a/macos/scripts/common-macos.sh b/macos/scripts/common-macos.sh index 921ae633..e815649b 100755 --- a/macos/scripts/common-macos.sh +++ b/macos/scripts/common-macos.sh @@ -29,7 +29,7 @@ CODEX_APP_JOB_LABEL="com.openai.codex-dream-skin-studio.app" INJECTOR_JOB_LABEL="com.openai.codex-dream-skin-studio.injector" EXPECTED_CODEX_TEAM_ID="2DC432GLL2" EXPECTED_CODEX_REQUIREMENT="anchor apple generic and certificate leaf[subject.OU] = \"$EXPECTED_CODEX_TEAM_ID\"" -SKIN_VERSION="1.5.6" +SKIN_VERSION="1.5.7" DREAM_SKIN_VALIDATED_RUNTIME_PID="" DREAM_SKIN_VALIDATED_RUNTIME_BUNDLE="" DREAM_SKIN_VALIDATED_RUNTIME_EXE="" diff --git a/macos/scripts/injector.mjs b/macos/scripts/injector.mjs index aa3679d6..04d51fc7 100644 --- a/macos/scripts/injector.mjs +++ b/macos/scripts/injector.mjs @@ -43,7 +43,7 @@ const stableTestidLiteral = (testid) => { } return JSON.stringify(`[data-testid="${testid}"]`); }; -const SKIN_VERSION = "1.5.6"; +const SKIN_VERSION = "1.5.7"; const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "[::1]"]); const CDP_ID_PATTERN = /^[A-Za-z0-9._-]{1,200}$/; const MAX_ART_BYTES = 10 * 1024 * 1024; @@ -1086,7 +1086,10 @@ async function verifySession(session, expectedThemeId = null, expectedRevision = const homeSignal = homeIndicator ?? document.querySelector(${selectorLiteral("game-source")}) ?? document.querySelector(${selectorLiteral("home-suggestions")}); const homeRoute = homeSignal?.closest('[role="main"]') ?? null; - const home = document.querySelector(${selectorLiteral("home-route")}); + // Codex 26.721.x can render the home content before home-icon. Reuse the + // already-resolved semantic home container so a healthy home session is + // not rejected solely because the stricter home-icon selector is late. + const home = document.querySelector(${selectorLiteral("home-route")}) ?? homeRoute; const suggestions = home?.querySelector(${selectorLiteral("home-suggestions")}) ?? null; const cardButtons = suggestions ? [...suggestions.querySelectorAll('button')] : []; const cardBoxes = cardButtons.map(box); diff --git a/macos/tests/run-tests.sh b/macos/tests/run-tests.sh index e5c9ec3e..79cc8c12 100755 --- a/macos/tests/run-tests.sh +++ b/macos/tests/run-tests.sh @@ -86,7 +86,7 @@ UPDATE_JSON="$({ })" "$NODE" -e ' const value = JSON.parse(process.argv[1]); - if (value.currentVersion !== "v1.5.6" || value.latestVersion !== "v9.8.7") process.exit(1); + if (value.currentVersion !== "v1.5.7" || value.latestVersion !== "v9.8.7") process.exit(1); if (!value.updateAvailable) process.exit(1); if (value.releaseUrl !== "https://github.com/Fei-Away/Codex-Dream-Skin/releases/latest") process.exit(1); ' "$UPDATE_JSON" @@ -1071,7 +1071,7 @@ CRLF_BACKUP="$TMP/config-crlf-backup.json" "$NODE" "$ROOT/scripts/theme-config.mjs" restore "$CRLF_CONFIG" "$CRLF_BACKUP" >/dev/null /usr/bin/cmp -s "$CRLF_CONFIG" "$TMP/original-crlf.toml" -/usr/bin/env -u HOME /bin/bash -c '. "$1/scripts/common-macos.sh"; [ -n "$HOME" ] && [ "$SKIN_VERSION" = "1.5.6" ]' _ "$ROOT" +/usr/bin/env -u HOME /bin/bash -c '. "$1/scripts/common-macos.sh"; [ -n "$HOME" ] && [ "$SKIN_VERSION" = "1.5.7" ]' _ "$ROOT" if [ "${CODEX_DREAM_SKIN_SKIP_DOCTOR:-0}" = "1" ]; then printf 'SKIP: Doctor requires an installed, signed Codex app.\n' DOCTOR_RESULT="skipped" diff --git a/macos/tests/runtime-css-nested-has.test.mjs b/macos/tests/runtime-css-nested-has.test.mjs index 403bc9d7..964d13d0 100644 --- a/macos/tests/runtime-css-nested-has.test.mjs +++ b/macos/tests/runtime-css-nested-has.test.mjs @@ -45,4 +45,18 @@ for (const file of files) { const findings = findNestedHas(css); assert.deepEqual(findings, [], `nested :has() found in ${file}`); }); + + test(`wide full-mode Markdown retains themed light text in ${file}`, () => { + const css = readFileSync(join(root, file), "utf8"); + const selectorToken = file.startsWith("runtime/") + ? "__DREAM_SELECTOR_SHELL_MAIN__:not\\(:has\\(__DREAM_SELECTOR_HOME_ROUTE_CSS__\\)\\) __DREAM_SELECTOR_MARKDOWN__" + : "main\\.main-surface:not\\(:has\\(\\[role=\"main\"\\]\\)\\) \\[class\\*=\"_markdown\"\\]"; + const fullMode = ':is\\([^)]*\\[data-dream-task-mode="full"\\][^)]*\\[data-dream-art-task-mode="full"\\][^)]*\\)\\[data-dream-art-wide="true"\\]'; + const markdownRule = new RegExp(`${fullMode}\\s*\\n?\\s*${selectorToken}\\s*\\{\\s*\\n?\\s*color:\\s*var\\(--ds-text\\)\\s*!important;`); + const lightShadowRule = new RegExp(`\\[data-dream-shell="light"\\]${fullMode}\\s*\\n?\\s*${selectorToken}\\s*\\{\\s*\\n?\\s*text-shadow:`); + assert.match(css, markdownRule, + "Full task mode must override native dark-shell Markdown foreground (#309)."); + assert.match(css, lightShadowRule, + "Full task mode must retain the light-shell Markdown contrast shadow (#309)."); + }); } diff --git a/macos/tests/safe-css-validator.test.mjs b/macos/tests/safe-css-validator.test.mjs index a72ea627..baf4a3c9 100644 --- a/macos/tests/safe-css-validator.test.mjs +++ b/macos/tests/safe-css-validator.test.mjs @@ -50,7 +50,7 @@ test("accepts only the bounded public part/property/value contract", () => { border-style: solid; border-radius: 12px; box-shadow: 0 4px 18px rgba(0, 0, 0, 0.25); - backdrop-filter: blur(18px); + backdrop-filter: blur(var(--ds-theme-surface-blur)); transition-property: background-color, border-color, box-shadow; transition-duration: 180ms; } diff --git a/macos/tests/window-readiness.test.mjs b/macos/tests/window-readiness.test.mjs index bc11a6d1..ef684963 100644 --- a/macos/tests/window-readiness.test.mjs +++ b/macos/tests/window-readiness.test.mjs @@ -19,6 +19,7 @@ const [startSource, commonSource] = await Promise.all([ fs.readFile(startPath, "utf8"), fs.readFile(commonPath, "utf8"), ]); +const injectorSource = await fs.readFile(path.join(macosRoot, "scripts", "injector.mjs"), "utf8"); const exactPayload = { skinVersion: "test-version", @@ -56,6 +57,25 @@ const baseRenderer = { assert.equal(readyNativeWindow.status, "ready"); assert.equal(assessRendererVerification(baseRenderer, readyNativeWindow, exactPayload).pass, true); +// Codex 26.721.x sometimes renders game-source/home suggestions before +// home-icon. In that interval homeRoute is already a real [role=main], so +// verification must use it when the stricter home-route selector is late. +assert.match( + injectorSource, + /const home = document\.querySelector\(\$\{selectorLiteral\("home-route"\)\}\) \?\? homeRoute;/, + "Home verification must fall back to the already-resolved semantic home route (#306).", +); +assert.equal( + assessRendererVerification({ + ...baseRenderer, + homeRoute: true, + homePresent: true, + hero: { visible: true, width: 800, height: 520 }, + }, readyNativeWindow, exactPayload).pass, + true, + "A visible fallback home container must satisfy the ordinary home verification gate.", +); + const windowCalls = []; assert.equal((await inspectNativeWindow({ target: { id: "target-main" }, diff --git a/runtime/dream-skin.css b/runtime/dream-skin.css index b2559ffc..ecafd4fd 100644 --- a/runtime/dream-skin.css +++ b/runtime/dream-skin.css @@ -444,7 +444,7 @@ html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream- backdrop-filter: none !important; } -html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"] +html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"] __DREAM_SELECTOR_SHELL_MAIN__:not(:has(__DREAM_SELECTOR_HOME_ROUTE_CSS__)) __DREAM_SELECTOR_MARKDOWN__ { color: var(--ds-text) !important; text-shadow: @@ -452,7 +452,7 @@ html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream- 0 0 10px rgb(var(--ds-bg-rgb) / .58); } -html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"] +html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"] __DREAM_SELECTOR_SHELL_MAIN__:not(:has(__DREAM_SELECTOR_HOME_ROUTE_CSS__)) __DREAM_SELECTOR_MARKDOWN__ { text-shadow: 0 1px 2px rgb(var(--ds-panel-rgb) / .92), diff --git a/runtime/safe-css-validator.mjs b/runtime/safe-css-validator.mjs index 8321e8ea..cf9572ad 100644 --- a/runtime/safe-css-validator.mjs +++ b/runtime/safe-css-validator.mjs @@ -278,7 +278,7 @@ function validatePropertyValue(property, value) { } if (property === "backdrop-filter") { if (value.toLowerCase() === "none") return true; - const match = value.match(/^blur\(\s*([^\s)]+)\s*\)$/i); + const match = value.match(/^blur\(\s*(.+?)\s*\)$/i); return Boolean(match && ( registeredVar(match[1], new Set(["--ds-theme-surface-blur"])) || zeroOrPx(match[1], 0, 20) diff --git a/windows/CHANGELOG.md b/windows/CHANGELOG.md index 972f3362..d82703bc 100644 --- a/windows/CHANGELOG.md +++ b/windows/CHANGELOG.md @@ -1,5 +1,13 @@ # Windows Changelog +## Unreleased + +### 修复 + +- 修复 v1.5.6 安装器在部分 Windows 10/11 环境中校验自带 Node.js 签名时,PowerShell 自动加载 `Microsoft.PowerShell.Security` / `Get-AuthenticodeSignature` 失败而中止安装的问题(#313、#314)。签名校验现在会在执行 `node.exe` 前显式加载安全模块,并在模块名加载失败时回退到 `$PSHOME` 下的系统模块清单路径;签名状态和发行者校验仍保持 fail-closed。 +- 修复社区主题一键换肤和 ZIP 导入拒绝 `backdrop-filter: blur(var(--ds-theme-surface-blur))` 的问题(#307、#312)。Safe CSS 仍只允许 `none`、0-20px blur 或注册的主题 blur 变量,不放宽到任意 filter 函数。 +- 修复 Codex Desktop 26.721.x 首页在 `home-icon` 延迟渲染时被误判为注入校验失败的问题(#307)。Windows 校验现在与 macOS 一样复用已由首页内容信号解析出的 `[role="main"]` 容器;严格的 `home-icon` 路径仍优先,旧版行为不变。 + ## 1.5.6 — 2026-07-26 ### 安全 diff --git a/windows/VERSION b/windows/VERSION index eac1e0ad..f01291b8 100644 --- a/windows/VERSION +++ b/windows/VERSION @@ -1 +1 @@ -1.5.6 +1.5.7 diff --git a/windows/assets/dream-skin.css b/windows/assets/dream-skin.css index bd112ec0..2656280b 100644 --- a/windows/assets/dream-skin.css +++ b/windows/assets/dream-skin.css @@ -444,7 +444,7 @@ html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream- backdrop-filter: none !important; } -html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"] +html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"] main.main-surface:not(:has([role="main"])) [class*="_markdown"] { color: var(--ds-text) !important; text-shadow: @@ -452,7 +452,7 @@ html[data-dream-skin="active"]:is([data-dream-task-mode="ambient"], [data-dream- 0 0 10px rgb(var(--ds-bg-rgb) / .58); } -html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"])[data-dream-art-wide="true"] +html[data-dream-skin="active"][data-dream-shell="light"]:is([data-dream-task-mode="ambient"], [data-dream-art-task-mode="ambient"], [data-dream-task-mode="banner"], [data-dream-art-task-mode="banner"], [data-dream-task-mode="full"], [data-dream-art-task-mode="full"])[data-dream-art-wide="true"] main.main-surface:not(:has([role="main"])) [class*="_markdown"] { text-shadow: 0 1px 2px rgb(var(--ds-panel-rgb) / .92), diff --git a/windows/assets/safe-css-validator.mjs b/windows/assets/safe-css-validator.mjs index 8321e8ea..cf9572ad 100644 --- a/windows/assets/safe-css-validator.mjs +++ b/windows/assets/safe-css-validator.mjs @@ -278,7 +278,7 @@ function validatePropertyValue(property, value) { } if (property === "backdrop-filter") { if (value.toLowerCase() === "none") return true; - const match = value.match(/^blur\(\s*([^\s)]+)\s*\)$/i); + const match = value.match(/^blur\(\s*(.+?)\s*\)$/i); return Boolean(match && ( registeredVar(match[1], new Set(["--ds-theme-surface-blur"])) || zeroOrPx(match[1], 0, 20) diff --git a/windows/scripts/common-windows.ps1 b/windows/scripts/common-windows.ps1 index 54c5497c..ca61c8cf 100644 --- a/windows/scripts/common-windows.ps1 +++ b/windows/scripts/common-windows.ps1 @@ -419,12 +419,31 @@ function Invoke-DreamSkinNative { } } +function Import-DreamSkinPowerShellSecurityModule { + $command = Get-Command Get-AuthenticodeSignature -CommandType Cmdlet -ErrorAction SilentlyContinue + if ($command) { return } + try { + Import-Module Microsoft.PowerShell.Security -ErrorAction Stop + } catch { + $modulePath = Join-Path $PSHOME 'Modules\Microsoft.PowerShell.Security\Microsoft.PowerShell.Security.psd1' + if (-not (Test-Path -LiteralPath $modulePath -PathType Leaf)) { + throw "PowerShell security module is unavailable: $($_.Exception.Message)" + } + Import-Module $modulePath -ErrorAction Stop + } + $command = Get-Command Get-AuthenticodeSignature -CommandType Cmdlet -ErrorAction SilentlyContinue + if (-not $command) { + throw 'PowerShell security module loaded, but Get-AuthenticodeSignature is unavailable.' + } +} + function Assert-DreamSkinTrustedNodeImage { param([Parameter(Mandatory = $true)][string]$Path) # Runs BEFORE the binary is ever executed. Get-DreamSkinValidatedNodeRuntime # learns the version by running `node -p`, so any authenticity check placed # after that point would already have executed attacker-controlled code. + Import-DreamSkinPowerShellSecurityModule $signature = Get-AuthenticodeSignature -LiteralPath $Path -ErrorAction Stop if ("$($signature.Status)" -ine 'Valid') { throw "The Node.js runtime is not validly signed: $Path ($($signature.Status))." diff --git a/windows/scripts/injector.mjs b/windows/scripts/injector.mjs index 636bf5bf..5bdabf5f 100644 --- a/windows/scripts/injector.mjs +++ b/windows/scripts/injector.mjs @@ -39,7 +39,7 @@ const stableTestidLiteral = (testid) => { } return JSON.stringify(`[data-testid="${testid}"]`); }; -const SKIN_VERSION = "1.5.6"; +const SKIN_VERSION = "1.5.7"; const MAX_ART_BYTES = 10 * 1024 * 1024; const MAX_SAFE_CSS_BYTES = 256 * 1024; const STRONG_THEME_AUDIT_MS = 30000; @@ -1145,7 +1145,14 @@ export async function verifySession( visible: Boolean(node.isConnected !== false && cssVisible && intersectsViewport), }; }; - const home = document.querySelector(${selectorLiteral("home-route")}); + const homeIndicator = document.querySelector(${selectorLiteral("home-icon")}); + const homeSignal = homeIndicator ?? document.querySelector(${selectorLiteral("game-source")}) ?? + document.querySelector(${selectorLiteral("home-suggestions")}); + const homeRoute = homeSignal?.closest('[role="main"]') ?? null; + // Codex 26.721.x can render the home content before home-icon. Reuse the + // already-resolved semantic home container so a healthy home session is + // not rejected solely because the stricter home-icon selector is late. + const home = document.querySelector(${selectorLiteral("home-route")}) ?? homeRoute; const settingsAnchor = document.querySelector(${selectorLiteral("appearance-radio")}) || document.querySelector(${stableTestidLiteral("theme-preview")}); const suggestions = home?.querySelector(${selectorLiteral("home-suggestions")}) ?? null; diff --git a/windows/tests/injector-window-readiness.test.mjs b/windows/tests/injector-window-readiness.test.mjs index 6940e229..0d0ad3b9 100644 --- a/windows/tests/injector-window-readiness.test.mjs +++ b/windows/tests/injector-window-readiness.test.mjs @@ -13,7 +13,10 @@ const selectors = { shell: "main.main-surface", sidebar: "aside.app-shell-left-panel", composer: ".composer-surface-chrome", + homeIcon: '[data-testid="home-icon"]', home: '[role="main"]:has([data-testid="home-icon"])', + gameSource: '[data-feature="game-source"]', + suggestions: ".group\\/home-suggestions", settings: 'input[name="appearance-theme"]', themePreview: '[data-testid="theme-preview"]', }; @@ -51,6 +54,7 @@ function makeDomFixture({ sidebar = makeElement(), composer = makeElement(), home = null, + homeSignal = null, settings = null, visibilityState = "visible", hidden = false, @@ -74,7 +78,9 @@ function makeDomFixture({ if (selector === selectors.shell) return shell; if (selector === selectors.sidebar) return sidebar; if (selector === selectors.composer) return composer; + if (selector === selectors.homeIcon) return null; if (selector === selectors.home) return home; + if (selector === selectors.gameSource || selector === selectors.suggestions) return homeSignal; if (selector === selectors.settings || selector === selectors.themePreview) return settings; return null; }, @@ -83,7 +89,7 @@ function makeDomFixture({ }; const window = { __CODEX_DREAM_SKIN_STATE__: { - version: "1.5.6", + version: "1.5.7", themeId: "fixture-theme", revision: "fixture-revision", styleMode: "style", @@ -178,6 +184,22 @@ test("visible settings and home anchors are the only L0 structure exceptions", a }); assert.equal(home.result.pass, true); + const fallbackHome = makeHome({ rect: makeRect(900, 650, 20, 20) }); + const lateHomeIconSignal = { + closest: (selector) => selector === '[role="main"]' ? fallbackHome : null, + }; + const lateHomeIcon = await verify({ + dom: makeDomFixture({ + scope: { level: "L0", baseState: "home" }, + shell: null, + sidebar: null, + home: null, + homeSignal: lateHomeIconSignal, + }), + }); + assert.equal(lateHomeIcon.result.homePresent, true); + assert.equal(lateHomeIcon.result.pass, true); + const noAnchor = await verify({ dom: makeDomFixture({ scope: { level: "L0", baseState: "settings" }, diff --git a/windows/tests/installer-static.tests.ps1 b/windows/tests/installer-static.tests.ps1 index 5f8401c5..bb0060e9 100644 --- a/windows/tests/installer-static.tests.ps1 +++ b/windows/tests/installer-static.tests.ps1 @@ -8,10 +8,11 @@ $definitionPath = Join-Path $installerRoot 'codex-dream-skin.iss' $builderPath = Join-Path $installerRoot 'build-release.ps1' $bootstrapPath = Join-Path $installerRoot 'setup-bootstrap.ps1' $communityApplyPath = Join-Path $windowsRoot 'scripts\apply-community-theme.ps1' +$commonPath = Join-Path $windowsRoot 'scripts\common-windows.ps1' $manifestPath = Join-Path $installerRoot 'node-runtime.json' $builderAst = $null -foreach ($scriptPath in @($builderPath, $bootstrapPath, $communityApplyPath)) { +foreach ($scriptPath in @($builderPath, $bootstrapPath, $communityApplyPath, $commonPath)) { if (-not (Test-Path -LiteralPath $scriptPath -PathType Leaf)) { throw "Required installer PowerShell does not exist: $scriptPath" } @@ -42,9 +43,11 @@ if ("$($manifest.version)" -cne '22.23.1' -or $definition = [System.IO.File]::ReadAllText($definitionPath) $builder = [System.IO.File]::ReadAllText($builderPath) $bootstrap = [System.IO.File]::ReadAllText($bootstrapPath) +$common = [System.IO.File]::ReadAllText($commonPath) if ($definition.Contains('-ExecutionPolicy Bypass') -or $builder.Contains('-ExecutionPolicy Bypass') -or - $bootstrap.Contains('-ExecutionPolicy Bypass')) { + $bootstrap.Contains('-ExecutionPolicy Bypass') -or + $common.Contains('-ExecutionPolicy Bypass')) { throw 'The installer layer must never bypass the PowerShell execution policy.' } if ($definition.Contains('ssPostInstall')) { @@ -196,6 +199,24 @@ if ($bootstrap.Contains('@restoreArguments')) { throw 'Installer restore switches must not use positional array splatting.' } +foreach ($requiredSecurityBootstrap in @( + 'function Import-DreamSkinPowerShellSecurityModule', + 'Import-Module Microsoft.PowerShell.Security -ErrorAction Stop', + "Join-Path `$PSHOME 'Modules\Microsoft.PowerShell.Security\Microsoft.PowerShell.Security.psd1'", + 'Get-Command Get-AuthenticodeSignature -CommandType Cmdlet', + 'Import-DreamSkinPowerShellSecurityModule', + 'Get-AuthenticodeSignature -LiteralPath $Path -ErrorAction Stop' +)) { + if (-not $common.Contains($requiredSecurityBootstrap)) { + throw "Node signature validation no longer explicitly loads the PowerShell security module: $requiredSecurityBootstrap" + } +} +$securityImportIndex = $common.IndexOf('Import-DreamSkinPowerShellSecurityModule', [System.StringComparison]::Ordinal) +$authenticodeIndex = $common.IndexOf('Get-AuthenticodeSignature -LiteralPath $Path -ErrorAction Stop', [System.StringComparison]::Ordinal) +if ($securityImportIndex -lt 0 -or $authenticodeIndex -le $securityImportIndex) { + throw 'Node signature validation can call Get-AuthenticodeSignature before the security module is loaded.' +} + $iconGenerator = $builderAst.Find({ param($node) $node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and diff --git a/windows/tests/start-verified-skin-preserved.tests.ps1 b/windows/tests/start-verified-skin-preserved.tests.ps1 index ef992549..faffa9af 100644 --- a/windows/tests/start-verified-skin-preserved.tests.ps1 +++ b/windows/tests/start-verified-skin-preserved.tests.ps1 @@ -169,7 +169,7 @@ function Invoke-DreamSkinStartupFixture { # The exact renderer output from #267: theme installed and painted, every # readiness signal true except the native-window probe. $renderedPayload = @' -{"installed":true,"version":"1.5.6","stylePresent":true,"homePresent":true, +{"installed":true,"version":"1.5.7","stylePresent":true,"homePresent":true, "nativeWindow":{"pass":false,"bound":false,"reason":"target-window-unavailable"}, "documentVisibility":"visible","documentHidden":false, "viewport":{"width":1289,"height":829},