Skip to content

Commit d4087e6

Browse files
authored
fix(windows): avoid bypass in tray shortcuts (#133)
Replace persistent Bypass launch arguments with RemoteSigned, unblock only verified managed PowerShell copies, and add Windows regression and upgrade guidance.\n\nRelated to #130.
1 parent f7bf17e commit d4087e6

10 files changed

Lines changed: 63 additions & 5 deletions

File tree

windows/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
### 修复
1616

17+
- 安装器在完成受管运行时副本的 SHA-256 校验后,仅清除其中 PowerShell 脚本的下载区标记;启动、恢复、托盘快捷方式和托盘子进程改用 `RemoteSigned`,不再组合隐藏 PowerShell 与 `ExecutionPolicy Bypass` 触发常见 LNK 启发式告警,同时继续服从系统和企业组策略。
1718
- 保留 Codex 原生固定顶栏的定位与层级,避免打开任务侧边面板后开关被推出主区、导致面板无法关闭。
1819
- 暗色外观下,原生顶部菜单栏现在使用深色半透明可读性层,并提高菜单按钮与图标的文字对比度,避免浅色壁纸让导航项难以辨认。
1920
- 渲染层现在只在检测到完整 Codex 主界面壳层时启用皮肤;宠物等透明辅助窗口会主动清理主题背景与装饰节点,避免出现遮挡宠物的矩形背景框。

windows/README.en.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,18 @@ The installer validates the official Codex Store package and Node.js, saves a re
2828
- `Codex Dream Skin - Tray`: open the system tray theme controls.
2929
- `Codex Dream Skin - Restore`: restore the stock appearance and close the saved CDP session.
3030

31+
`Bypass` in the install command applies only to that user-initiated installer process. The installer verifies the runtime copy with SHA-256, then clears download-zone markers only from managed PowerShell copies under `%LOCALAPPDATA%\CodexDreamSkin\engine`. Daily shortcuts use `RemoteSigned` and do not override system or enterprise Group Policy.
32+
3133
Pass `-Port` during installation to use a fixed custom port. Valid ports range from `1024` through `65535`.
3234

3335
```powershell
3436
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\install-dream-skin.ps1 -Port 9444
3537
```
3638

39+
## Update
40+
41+
Exit the Dream Skin tray and close Codex, update the checkout (`git pull`, or download the latest source again), then rerun the install command above. The installer atomically replaces the managed runtime and rebuilds its shortcuts without deleting the active theme, saved themes, or imported images.
42+
3743
## Launch and verify
3844

3945
The `Codex Dream Skin` shortcut is the recommended launcher. It asks for confirmation before restarting an open Codex window.
@@ -126,6 +132,10 @@ The scripts accept only a registered official Store package. They do not launch
126132

127133
Close every Codex window and run the installer again. Installation requires stable app and configuration state.
128134

135+
### Antivirus reports the old tray shortcut
136+
137+
Older tray shortcuts combined hidden PowerShell with `ExecutionPolicy Bypass`, which can trigger behavior-based LNK detections. Do not whitelist the detection blindly. Update the source and rerun the installer so the shortcuts use `RemoteSigned`. If the updated shortcut is still detected, leave it quarantined and report the antivirus product, version, detection name, and shortcut properties without sharing secrets or private data.
138+
129139
### The port is occupied
130140

131141
When `-Port` is omitted, the launcher searches for a free port beginning at `9335`. If another process owns an explicitly requested port, choose a different port rather than stopping an unknown listener.

windows/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,18 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\install-dream-
2828
- `Codex Dream Skin - Tray`:打开系统托盘主题控制。
2929
- `Codex Dream Skin - Restore`:恢复官方外观并关闭已保存的 CDP 会话。
3030

31+
安装命令中的 `Bypass` 只作用于这一次由用户明确发起的安装进程。安装器会先校验运行时副本的 SHA-256,再仅对 `%LOCALAPPDATA%\CodexDreamSkin\engine` 中受管的 PowerShell 副本清除下载区标记。日常快捷方式使用 `RemoteSigned`,不会绕过系统或企业组策略。
32+
3133
如需使用自定义端口,可以在安装时传入 `-Port`。端口范围必须是 `1024``65535`
3234

3335
```powershell
3436
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\install-dream-skin.ps1 -Port 9444
3537
```
3638

39+
## 更新
40+
41+
先退出 Dream Skin 托盘并关闭 Codex,再更新仓库(`git pull`,或重新下载最新源码),然后重新运行上面的安装命令。安装器会原子替换受管运行时并重建快捷方式;当前主题、已保存主题和导入图片不会被删除。
42+
3743
## 启动与验证
3844

3945
推荐从 `Codex Dream Skin` 快捷方式启动。它发现 Codex 已经运行时会先询问是否重启。
@@ -126,6 +132,10 @@ Get-AppxPackage -Name OpenAI.Codex
126132

127133
关闭所有 Codex 窗口后再运行安装器。安装期间必须保持配置和应用状态稳定。
128134

135+
### 杀毒软件报告旧版托盘快捷方式
136+
137+
旧版托盘快捷方式同时使用隐藏 PowerShell 和 `ExecutionPolicy Bypass`,可能触发基于行为特征的 LNK 告警。不要直接加入白名单;更新源码并重新运行安装器,让快捷方式改用 `RemoteSigned`。如果新版仍然报警,请保留隔离状态,并在 Issue 中附上杀毒软件名称、版本、告警名称和快捷方式属性,不要上传密钥或私人数据。
138+
129139
### 端口被占用
130140

131141
没有显式指定 `-Port` 时,启动脚本会从默认端口 `9335` 开始寻找空闲端口。显式端口被其他进程占用时,改用另一个端口,不要关闭身份不明的监听进程。

windows/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Apply a reversible renderer skin through Chromium DevTools Protocol while launch
3434
- Preserve `config.toml` as strict UTF-8. Never use encoding-dependent whole-file PowerShell reads/writes, silently transcode UTF-16, or overwrite a file that changed after it was read. Ambiguous TOML shapes must fail before writing rather than receive a best-effort rewrite.
3535
- Keep install/start/restore/verify serialized with the per-user operation lock in `common-windows.ps1`.
3636
- Treat `%LOCALAPPDATA%\CodexDreamSkin\engine` as an installer-managed runtime. Exit the Dream Skin tray before reinstalling so the installer can replace that runtime atomically and update every shortcut to the same copy.
37+
- Keep installed shortcuts and tray child processes on `RemoteSigned`, never `Bypass`. Clear Internet-zone markers only from staged managed `.ps1` copies after their byte-content hashes match the selected source; never change the user's persistent execution policy or override Group Policy.
3738

3839
## Checks
3940

windows/references/qa-inventory.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- Restore/reapply cycle: remove live skin, verify marker absent, apply again, verify marker present.
2323
- Update resilience: resolve the current `OpenAI.Codex` Appx location dynamically for launch. A versioned path saved for cleanup must be revalidated against the registered package full/family identity before any process is stopped.
2424
- Restart consent: an existing normal Codex window is never force-closed without explicit CLI authorization or shortcut confirmation.
25+
- Shortcut policy: installed launch, restore, tray, and tray-child commands use `RemoteSigned` without `Bypass`; Internet-zone markers are removed only from hash-verified managed PowerShell copies.
2526
- Config safety: Chinese project names, LF/CRLF choice, quoted target keys, table-header comments, and unrelated TOML sections survive install/selective restore; ambiguous target shapes fail unchanged, exact recovery keeps a copy of the replaced current file, and install refuses both registered and state-recorded old Codex processes.
2627
- Theme safety: empty/over-16 MB images, over-16384px/50MP dimensions, path escapes, symlinks/junctions, malformed JSON, and unsupported formats are rejected before payload construction.
2728
- Tray lifecycle: pause/resume reflects the clicked state, bundled Arina Hashimoto theme is present on first install, and complete restore terminates any separately launched tray before it can reapply the skin.

windows/references/runtime-notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
- The managed theme root rejects junctions and symbolic links before initialization, import, save, switch, pause, or state writes. Windows uses the bundled Node image-metadata helper to enforce the same 16 MB, 16384px, and 50MP limits before an import is copied.
1414
- `config.toml` is read from raw bytes as strict UTF-8, written without BOM through same-directory atomic replacement, and backed up byte-for-byte. Install requires Codex to be closed; writes stage the temporary file first, then abort if the destination bytes changed immediately before replacement. Quoted keys and table-header comments are supported; escaped target keys, multiline strings/arrays, dotted target keys, or duplicate target keys fail before writing. Completed restore backups are retained as `config.restored-*.toml` so reinstall captures a fresh baseline.
1515
- A per-user named mutex prevents concurrent install, start, restore, and verify operations from racing state, ports, or config writes.
16+
- The installer compares every staged runtime file with its selected source by SHA-256, then clears Internet-zone markers only from staged managed `.ps1` copies. Installed shortcuts and tray child processes use process-scoped `RemoteSigned`; Machine/User Policy remains authoritative and no persistent execution policy is changed.
1617
- Store updates are supported because the launcher queries `Get-AppxPackage OpenAI.Codex` on every launch. State paths are eligible for automatic cleanup only after their Appx full name, family name, install root, and executable are matched against a currently registered package; an active unverified old path requires manual closure.

windows/scripts/common-windows.ps1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ function Install-DreamSkinRuntimeEngine {
195195
}
196196
}
197197

198+
# Unblock only verified managed copies so shortcuts can honor RemoteSigned instead of bypassing policy.
199+
foreach ($runtimeScript in Get-ChildItem -LiteralPath (Join-Path $stagingRoot 'scripts') `
200+
-Filter '*.ps1' -Recurse -File -Force -ErrorAction Stop) {
201+
Unblock-File -LiteralPath $runtimeScript.FullName -ErrorAction Stop
202+
}
203+
198204
$hasBackup = $false
199205
if (Test-Path -LiteralPath $engine.Root) {
200206
Assert-DreamSkinRuntimeTree -Path $engine.Root

windows/scripts/install-dream-skin.ps1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,29 +57,29 @@ try {
5757
foreach ($folder in @($desktop, $startMenu)) {
5858
$shortcut = $shell.CreateShortcut((Join-Path $folder 'Codex Dream Skin.lnk'))
5959
$shortcut.TargetPath = $powershell
60-
$shortcut.Arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$startScript`"$portArgument -PromptRestart"
60+
$shortcut.Arguments = "-NoProfile -ExecutionPolicy RemoteSigned -File `"$startScript`"$portArgument -PromptRestart"
6161
$shortcut.WorkingDirectory = $engine.Root
6262
$shortcut.Description = 'Launch the official Codex app with Codex Dream Skin'
6363
$shortcut.Save()
6464
}
6565

6666
$restore = $shell.CreateShortcut((Join-Path $desktop 'Codex Dream Skin - Restore.lnk'))
6767
$restore.TargetPath = $powershell
68-
$restore.Arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$restoreScript`"$portArgument -RestoreBaseTheme -PromptRestart"
68+
$restore.Arguments = "-NoProfile -ExecutionPolicy RemoteSigned -File `"$restoreScript`"$portArgument -RestoreBaseTheme -PromptRestart"
6969
$restore.WorkingDirectory = $engine.Root
7070
$restore.Description = 'Restore the official Codex appearance and close the CDP session'
7171
$restore.Save()
7272

7373
foreach ($folder in @($desktop, $startMenu)) {
7474
$tray = $shell.CreateShortcut((Join-Path $folder 'Codex Dream Skin - Tray.lnk'))
7575
$tray.TargetPath = $powershell
76-
$tray.Arguments = "-NoProfile -STA -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$trayScript`"$portArgument"
76+
$tray.Arguments = "-NoProfile -STA -WindowStyle Hidden -ExecutionPolicy RemoteSigned -File `"$trayScript`"$portArgument"
7777
$tray.WorkingDirectory = $engine.Root
7878
$tray.Description = 'Open Codex Dream Skin status and theme controls in the system tray'
7979
$tray.Save()
8080
}
8181
Start-Process -FilePath $powershell -ArgumentList `
82-
"-NoProfile -STA -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$trayScript`"$portArgument" `
82+
"-NoProfile -STA -WindowStyle Hidden -ExecutionPolicy RemoteSigned -File `"$trayScript`"$portArgument" `
8383
-WindowStyle Hidden | Out-Null
8484
}
8585

windows/scripts/tray-dream-skin.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ try {
4343
function Start-DreamSkinPowerShell {
4444
param([Parameter(Mandatory = $true)][string]$Script, [string[]]$Arguments = @())
4545
$scriptToken = ConvertTo-DreamSkinProcessArgument -Value $Script
46-
$argumentLine = '-NoProfile -ExecutionPolicy Bypass -File ' + $scriptToken
46+
$argumentLine = '-NoProfile -ExecutionPolicy RemoteSigned -File ' + $scriptToken
4747
if ($Arguments.Count -gt 0) { $argumentLine += ' ' + ($Arguments -join ' ') }
4848
Start-Process -FilePath $powershell -ArgumentList $argumentLine | Out-Null
4949
}

windows/tests/run-tests.ps1

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ try {
1818
Copy-Item -LiteralPath (Join-Path $Root $directoryName) -Destination $runtimeSourceRoot `
1919
-Recurse -Force -ErrorAction Stop
2020
}
21+
$zoneMarkedSourceScript = Join-Path $runtimeSourceRoot 'scripts\start-dream-skin.ps1'
22+
Set-Content -LiteralPath $zoneMarkedSourceScript -Stream 'Zone.Identifier' `
23+
-Value "[ZoneTransfer]`r`nZoneId=3`r`n" -Encoding Ascii
24+
if (@(Get-Item -LiteralPath $zoneMarkedSourceScript -Stream 'Zone.Identifier').Count -ne 1) {
25+
throw 'Runtime test could not create an Internet-zone marker on its source fixture.'
26+
}
2127

2228
$engine = Install-DreamSkinRuntimeEngine -SkillRoot $runtimeSourceRoot -StateRoot $runtimeStateRoot
2329
$sourcePrefix = $runtimeSourceRoot.TrimEnd('\') + '\'
@@ -44,6 +50,10 @@ try {
4450
throw "Installed runtime hash does not match its source: $relative"
4551
}
4652
}
53+
if (@(Get-Item -LiteralPath $engine.Start -Stream 'Zone.Identifier' `
54+
-ErrorAction SilentlyContinue).Count -ne 0) {
55+
throw 'Installed runtime retained an Internet-zone marker and cannot use RemoteSigned safely.'
56+
}
4757

4858
[System.IO.File]::WriteAllText((Join-Path $engine.Root 'stale-runtime.txt'), 'stale')
4959
[System.IO.File]::WriteAllText((Join-Path $runtimeSourceRoot 'scripts\runtime-update.test'), 'updated')
@@ -118,6 +128,16 @@ try {
118128
}
119129

120130
$installSource = Read-DreamSkinUtf8File -Path (Join-Path $Root 'scripts\install-dream-skin.ps1')
131+
$commonSource = Read-DreamSkinUtf8File -Path (Join-Path $Root 'scripts\common-windows.ps1')
132+
$hashVerificationIndex = $commonSource.IndexOf(
133+
'Staged Dream Skin runtime failed hash verification', [System.StringComparison]::Ordinal
134+
)
135+
$unblockIndex = $commonSource.IndexOf(
136+
'Unblock-File -LiteralPath $runtimeScript.FullName', [System.StringComparison]::Ordinal
137+
)
138+
if ($hashVerificationIndex -lt 0 -or $unblockIndex -le $hashVerificationIndex) {
139+
throw 'Runtime scripts are not unblocked only after staged byte-content verification.'
140+
}
121141
$trayGuardIndex = $installSource.IndexOf('if (Test-DreamSkinTrayActive)', [System.StringComparison]::Ordinal)
122142
$engineInstallIndex = $installSource.IndexOf('$engine = Install-DreamSkinRuntimeEngine', [System.StringComparison]::Ordinal)
123143
if ($trayGuardIndex -lt 0 -or $engineInstallIndex -le $trayGuardIndex) {
@@ -135,6 +155,10 @@ try {
135155
throw "Installer shortcut still depends on its source checkout: $requiredShortcutBinding"
136156
}
137157
}
158+
if ([regex]::Matches($installSource, '-ExecutionPolicy RemoteSigned').Count -ne 4 -or
159+
$installSource.Contains('-ExecutionPolicy Bypass')) {
160+
throw 'Installer shortcuts or tray launch still bypass the PowerShell execution policy.'
161+
}
138162

139163
Remove-Item -LiteralPath $runtimeSourceRoot -Recurse -Force
140164
foreach ($installedScript in Get-ChildItem -LiteralPath $engine.Scripts -Filter '*.ps1' -File) {
@@ -729,6 +753,10 @@ try {
729753
if (-not $traySource.Contains('$nextPaused') -or -not $traySource.Contains('[System.Windows.Forms.Application]::Exit()')) {
730754
throw 'Tray pause/restore closures do not terminate cleanly.'
731755
}
756+
if ([regex]::Matches($traySource, '-ExecutionPolicy RemoteSigned').Count -ne 1 -or
757+
$traySource.Contains('-ExecutionPolicy Bypass')) {
758+
throw 'Tray actions still bypass the PowerShell execution policy.'
759+
}
732760
if (-not $traySource.Contains('Read-DreamSkinTheme -ThemeDirectory $paths.Active -SkipImageMetadata') -or
733761
-not $traySource.Contains('Get-DreamSkinSavedThemes -StateRoot $StateRoot -SkipImageMetadata')) {
734762
throw 'Tray menu metadata enumeration still performs full image parsing on every open.'

0 commit comments

Comments
 (0)