Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions macos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
- 修复显式浅色、宽幅 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 函数。

### 内部

- 同步 v1.5.8 版本号以发布 Windows 安装器补丁;macOS 运行时代码相对 v1.5.7 无行为变化。

## 1.5.6 — 2026-07-26

### 安全
Expand Down
2 changes: 1 addition & 1 deletion macos/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.5.7
1.5.8
2 changes: 1 addition & 1 deletion macos/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codex-dream-skin-studio",
"version": "1.5.7",
"version": "1.5.8",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion macos/scripts/common-macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.7"
SKIN_VERSION="1.5.8"
DREAM_SKIN_VALIDATED_RUNTIME_PID=""
DREAM_SKIN_VALIDATED_RUNTIME_BUNDLE=""
DREAM_SKIN_VALIDATED_RUNTIME_EXE=""
Expand Down
2 changes: 1 addition & 1 deletion macos/scripts/injector.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const stableTestidLiteral = (testid) => {
}
return JSON.stringify(`[data-testid="${testid}"]`);
};
const SKIN_VERSION = "1.5.7";
const SKIN_VERSION = "1.5.8";
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;
Expand Down
4 changes: 2 additions & 2 deletions macos/tests/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ UPDATE_JSON="$({
})"
"$NODE" -e '
const value = JSON.parse(process.argv[1]);
if (value.currentVersion !== "v1.5.7" || value.latestVersion !== "v9.8.7") process.exit(1);
if (value.currentVersion !== "v1.5.8" || 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"
Expand Down Expand Up @@ -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.7" ]' _ "$ROOT"
/usr/bin/env -u HOME /bin/bash -c '. "$1/scripts/common-macos.sh"; [ -n "$HOME" ] && [ "$SKIN_VERSION" = "1.5.8" ]' _ "$ROOT"
if [ "${CODEX_DREAM_SKIN_SKIP_DOCTOR:-0}" = "1" ]; then
printf 'SKIP: Doctor requires an installed, signed Codex app.\n'
DOCTOR_RESULT="skipped"
Expand Down
1 change: 1 addition & 0 deletions windows/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### 修复

- 修复安装器遇到 Codex `config.toml` 中合法多行数组时直接拒绝写入的问题(#313)。配置编辑器现在按 TOML 结构扫描 table header,安全跨过普通多行数组并保持原字节风格;未闭合数组、括号不匹配,以及 Dream Skin 需要改写的目标 key 自身为多行值时仍会在写入前 fail-closed。
- 修复 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` 路径仍优先,旧版行为不变。
Expand Down
2 changes: 1 addition & 1 deletion windows/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.5.7
1.5.8
176 changes: 151 additions & 25 deletions windows/scripts/config-utf8.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -188,17 +188,119 @@ function Get-DreamSkinTomlArrayBracketBalance {
return $balance
}

function Get-DreamSkinTomlLineStructure {
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Line)

$builder = [System.Text.StringBuilder]::new()
$quote = $null
$escaped = $false
for ($index = 0; $index -lt $Line.Length; $index++) {
$character = $Line[$index]
if ($quote -eq '"') {
if ($escaped) { $escaped = $false; continue }
if ($character -eq '\') { $escaped = $true; continue }
if ($character -eq $quote) { $quote = $null }
continue
}
if ($quote -eq "'") {
if ($character -eq $quote) { $quote = $null }
continue
}
if ($character -eq '"' -or $character -eq "'") {
$quote = $character
continue
}
if ($character -eq '#') { break }
[void]$builder.Append($character)
}
return $builder.ToString()
}

function Test-DreamSkinTomlTableHeaderStructure {
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Structure)

$value = $Structure.Trim()
if ($value.StartsWith('[[')) {
return $value.EndsWith(']]') -and
-not $value.Substring(2, $value.Length - 4).Contains('[') -and
-not $value.Substring(2, $value.Length - 4).Contains(']')
}
return $value.StartsWith('[') -and
-not $value.StartsWith('[[') -and
$value.EndsWith(']') -and
-not $value.Substring(1, $value.Length - 2).Contains('[') -and
-not $value.Substring(1, $value.Length - 2).Contains(']')
}

function Update-DreamSkinTomlArrayDepth {
param(
[Parameter(Mandatory = $true)][AllowEmptyString()][string]$Structure,
[Parameter(Mandatory = $true)][int]$InitialDepth
)

$depth = $InitialDepth
for ($index = 0; $index -lt $Structure.Length; $index++) {
$character = $Structure[$index]
if ($character -eq '[') { $depth++ }
if ($character -eq ']') { $depth-- }
if ($depth -lt 0) {
throw 'Refusing to rewrite TOML containing an unmatched array bracket.'
}
}
return $depth
}

function Get-DreamSkinTomlTableHeaders {
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Content)

$headers = @()
$offset = 0
$arrayDepth = 0
$lines = [regex]::Matches($Content, '[^\n]*\n|[^\n]+$')
$desktopToken = Get-DreamSkinTomlKeyTokenPattern -Key 'desktop'
foreach ($lineMatch in $lines) {
$line = $lineMatch.Value
$structure = (Get-DreamSkinTomlLineStructure -Line $line).Trim()
if ($arrayDepth -eq 0 -and (Test-DreamSkinTomlTableHeaderStructure -Structure $structure)) {
$headers += [pscustomobject]@{
Index = $offset
BodyStart = $offset + $line.Length
Line = $line
IsDesktop = [regex]::IsMatch(
$line,
"^[\t ]*\[[\t ]*$desktopToken[\t ]*\][\t ]*(?:#[^\r\n]*)?(?:\r?\n)?$"
)
IsDesktopArray = [regex]::IsMatch(
$line,
"^[\t ]*\[\[[\t ]*$desktopToken[\t ]*(?:\]\]|\.)"
)
}
} else {
$assignment = $structure.IndexOf('=')
if ($arrayDepth -eq 0 -and $assignment -lt 0) {
if ($structure.Contains('[') -or $structure.Contains(']')) {
throw 'Refusing to rewrite malformed TOML array syntax.'
}
} else {
$expression = if ($arrayDepth -gt 0) { $structure } else { $structure.Substring($assignment + 1) }
$arrayDepth = Update-DreamSkinTomlArrayDepth -Structure $expression -InitialDepth $arrayDepth
}
}
$offset += $line.Length
}
if ($arrayDepth -ne 0) {
throw 'Refusing to rewrite TOML containing an unterminated array.'
}
return @($headers)
}

function Assert-DreamSkinTomlLineEditingSafe {
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Content)

if ($Content.Contains('"""') -or $Content.Contains("'''")) {
throw 'Refusing to rewrite TOML containing multiline strings; use single-line values before installing Dream Skin.'
}
foreach ($match in [regex]::Matches($Content, '(?m)^[^\r\n]*=[\t ]*\[[^\r\n]*\r?$')) {
if ((Get-DreamSkinTomlArrayBracketBalance -Line $match.Value) -ne 0) {
throw 'Refusing to rewrite TOML containing multiline arrays; use single-line arrays before installing Dream Skin.'
}
}
$null = Get-DreamSkinTomlTableHeaders -Content $Content

$probe = ConvertTo-DreamSkinTomlAsciiEscapeProbe -Value $Content
if ($probe -cne $Content) {
Expand All @@ -225,23 +327,27 @@ function Test-DreamSkinDesktopNestedTable {

$desktopToken = Get-DreamSkinTomlKeyTokenPattern -Key 'desktop'
$keyToken = Get-DreamSkinTomlKeyTokenPattern -Key $Key
return [regex]::IsMatch(
$Content,
"(?m)^[\t ]*\[[\t ]*$desktopToken[\t ]*\.[\t ]*$keyToken[\t ]*(?:\]|\.)"
)
foreach ($header in @(Get-DreamSkinTomlTableHeaders -Content $Content)) {
if ([regex]::IsMatch(
$header.Line,
"^[\t ]*\[[\t ]*$desktopToken[\t ]*\.[\t ]*$keyToken[\t ]*(?:\]|\.)"
)) {
return $true
}
}
return $false
}

function Assert-DreamSkinDesktopShapeSupported {
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Content)

Assert-DreamSkinTomlLineEditingSafe -Content $Content
$sectionPattern = Get-DreamSkinDesktopSectionPattern
if ([regex]::Matches($Content, $sectionPattern).Count -gt 1) {
$headers = @(Get-DreamSkinTomlTableHeaders -Content $Content)
if (@($headers | Where-Object { $_.IsDesktop }).Count -gt 1) {
throw 'Refusing to rewrite multiple equivalent [desktop] tables.'
}

$desktopToken = Get-DreamSkinTomlKeyTokenPattern -Key 'desktop'
if ([regex]::IsMatch($Content, "(?m)^[\t ]*\[\[[\t ]*$desktopToken[\t ]*(?:\]\]|\.)")) {
if (@($headers | Where-Object { $_.IsDesktopArray }).Count -gt 0) {
throw 'Refusing to rewrite a config that represents desktop as an array of tables.'
}
foreach ($key in @('appearanceTheme', 'appearanceLightCodeThemeId')) {
Expand All @@ -250,8 +356,9 @@ function Assert-DreamSkinDesktopShapeSupported {
}
}

$firstTable = [regex]::Match($Content, '(?m)^[\t ]*\[\[?')
$rootContent = if ($firstTable.Success) { $Content.Substring(0, $firstTable.Index) } else { $Content }
$desktopToken = Get-DreamSkinTomlKeyTokenPattern -Key 'desktop'
$firstTable = @($headers)[0]
$rootContent = if ($null -ne $firstTable) { $Content.Substring(0, $firstTable.Index) } else { $Content }
if ([regex]::IsMatch($rootContent, "(?m)^[\t ]*$desktopToken[\t ]*(?:\.|=)")) {
throw 'Refusing to rewrite root dotted or inline desktop keys; normalize them to a [desktop] table first.'
}
Expand Down Expand Up @@ -281,14 +388,24 @@ function Assert-DreamSkinDesktopShapeSupported {
function Get-DreamSkinDesktopSection {
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Content)

$match = [regex]::Match($Content, (Get-DreamSkinDesktopSectionPattern))
if (-not $match.Success) { return $null }
$headers = @(Get-DreamSkinTomlTableHeaders -Content $Content)
$desktopHeaders = @($headers | Where-Object { $_.IsDesktop })
if ($desktopHeaders.Count -eq 0) { return $null }
if ($desktopHeaders.Count -gt 1) { throw 'Refusing to rewrite multiple equivalent [desktop] tables.' }
$desktopHeader = $desktopHeaders[0]
$headerIndex = [array]::IndexOf($headers, $desktopHeader)
$bodyEnd = if ($headerIndex -ge 0 -and $headerIndex + 1 -lt $headers.Count) {
$headers[$headerIndex + 1].Index
} else {
$Content.Length
}
$bodyLength = $bodyEnd - $desktopHeader.BodyStart
return [pscustomobject]@{
Body = $match.Groups['body'].Value
BodyStart = $match.Groups['body'].Index
BodyLength = $match.Groups['body'].Length
SectionStart = $match.Index
SectionLength = $match.Length
Body = $Content.Substring($desktopHeader.BodyStart, $bodyLength)
BodyStart = $desktopHeader.BodyStart
BodyLength = $bodyLength
SectionStart = $desktopHeader.Index
SectionLength = $bodyEnd - $desktopHeader.Index
}
}

Expand All @@ -312,11 +429,17 @@ function Set-DreamSkinSectionSetting {
)

$keyToken = Get-DreamSkinTomlKeyTokenPattern -Key $Key
$pattern = "(?m)^[\t ]*$keyToken[\t ]*=[^\r\n]*(?:\r?\n|(?=\z))"
$matcher = [regex]::new($pattern)
if ($matcher.Matches($Body).Count -gt 1) {
$lineMatches = [regex]::Matches($Body, "(?m)^[\t ]*$keyToken[\t ]*=.*$")
if ($lineMatches.Count -gt 1) {
throw "Refusing to rewrite duplicate '$Key' entries in the [desktop] section."
}
foreach ($lineMatch in $lineMatches) {
if ((Get-DreamSkinTomlArrayBracketBalance -Line $lineMatch.Value) -ne 0) {
throw "Refusing to rewrite multiline '$Key' settings in the [desktop] section."
}
}
$pattern = "(?m)^[\t ]*$keyToken[\t ]*=[^\r\n]*(?:\r?\n|(?=\z))"
$matcher = [regex]::new($pattern)
if ($null -eq $Line) { return $matcher.Replace($Body, '', 1) }
$normalizedLine = $Line.TrimEnd("`r", "`n") + $NewLine
if ($matcher.IsMatch($Body)) {
Expand All @@ -336,6 +459,9 @@ function Get-DreamSkinSectionSettingLine {
$matches = [regex]::Matches($Body, "(?m)^[\t ]*$keyToken[\t ]*=.*$")
if ($matches.Count -gt 1) { throw "Refusing to inspect duplicate '$Key' entries in the [desktop] section." }
if ($matches.Count -eq 0) { return $null }
if ((Get-DreamSkinTomlArrayBracketBalance -Line $matches[0].Value) -ne 0) {
throw "Refusing to inspect multiline '$Key' settings in the [desktop] section."
}
return $matches[0].Value.Trim()
}

Expand Down
2 changes: 1 addition & 1 deletion windows/scripts/injector.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const stableTestidLiteral = (testid) => {
}
return JSON.stringify(`[data-testid="${testid}"]`);
};
const SKIN_VERSION = "1.5.7";
const SKIN_VERSION = "1.5.8";
const MAX_ART_BYTES = 10 * 1024 * 1024;
const MAX_SAFE_CSS_BYTES = 256 * 1024;
const STRONG_THEME_AUDIT_MS = 30000;
Expand Down
2 changes: 1 addition & 1 deletion windows/tests/injector-window-readiness.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ function makeDomFixture({
};
const window = {
__CODEX_DREAM_SKIN_STATE__: {
version: "1.5.7",
version: "1.5.8",
themeId: "fixture-theme",
revision: "fixture-revision",
styleMode: "style",
Expand Down
24 changes: 22 additions & 2 deletions windows/tests/run-tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,26 @@ try {
throw 'A safe single-line array containing bracket text was changed or rejected.'
}

$multilineArrayPath = Join-Path $temporaryRoot 'config-multiline-array.toml'
$multilineArrayBackup = Join-Path $temporaryRoot 'config-multiline-array.before.toml'
$multilineArrayOriginal = "model = `"gpt-5`"`r`nfeatures = [`r`n `"hash # stays inside the string`",`r`n `"brackets [stay] inside the string`", # ignored comment brackets []`r`n [`"nested`", `"array`"],`r`n]`r`n`r`n[desktop]`r`nlayout = [`r`n [`"one`", `"two`"], # unrelated desktop array`r`n [`"three`", `"[desktop]`"],`r`n]`r`nappearanceTheme = `"system`"`r`nappearanceLightCodeThemeId = `"github-light`"`r`nkeepMe = true`r`n`r`n[mcp_servers.example]`r`nargs = [`r`n `"--flag`",`r`n `"value#with-hash`",`r`n]`r`n"
[System.IO.File]::WriteAllText($multilineArrayPath, $multilineArrayOriginal, $utf8NoBom)
Install-DreamSkinBaseTheme -ConfigPath $multilineArrayPath -BackupPath $multilineArrayBackup -AppearanceTheme 'dark'
$multilineArrayInstalled = Read-DreamSkinUtf8File -Path $multilineArrayPath
if (-not $multilineArrayInstalled.Contains('layout = [')) {
throw 'Install removed an unrelated multiline array from the [desktop] table.'
}
if (-not $multilineArrayInstalled.Contains('[mcp_servers.example]')) {
throw 'Install lost a following TOML table after a multiline array.'
}
if (-not $multilineArrayInstalled.Contains('appearanceTheme = "dark"')) {
throw 'Install did not update appearanceTheme beside multiline arrays.'
}
Restore-DreamSkinBaseTheme -ConfigPath $multilineArrayPath -BackupPath $multilineArrayBackup
if ((Read-DreamSkinUtf8File -Path $multilineArrayPath) -cne $multilineArrayOriginal) {
throw 'Multiline arrays were not preserved exactly through install and restore.'
}

foreach ($unsupported in @(
'desktop.appearanceTheme = "system"',
'desktop = { appearanceTheme = "system" }',
Expand All @@ -549,8 +569,8 @@ try {
'["desk\u0074op"]',
"note = `"`"`"fake`r`n[desktop]`r`nappearanceTheme = `"dark`"`r`n`"`"`"",
"[desktop]`r`nappearanceTheme = [`r`n `"light`"`r`n]",
"[desktop]`r`nlayout = [`r`n [1, 2],`r`n [3, 4],`r`n]`r`nappearanceTheme = `"dark`"",
"[desktop]`r`nlayout = [`"]`",`r`n [`"[`", `"]`"],`r`n]`r`nappearanceTheme = `"dark`""
"features = ]`r`n`r`n[desktop]`r`nappearanceTheme = `"system`"",
"features = [`r`n `"one`"`r`n`r`n[desktop]`r`nappearanceTheme = `"system`""
)) {
$unsupportedPath = Join-Path $temporaryRoot ("unsupported-$([guid]::NewGuid().ToString('N')).toml")
$unsupportedBackup = "$unsupportedPath.before"
Expand Down
2 changes: 1 addition & 1 deletion windows/tests/start-verified-skin-preserved.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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.7","stylePresent":true,"homePresent":true,
{"installed":true,"version":"1.5.8","stylePresent":true,"homePresent":true,
"nativeWindow":{"pass":false,"bound":false,"reason":"target-window-unavailable"},
"documentVisibility":"visible","documentHidden":false,
"viewport":{"width":1289,"height":829},
Expand Down
Loading