Skip to content

fix(build): XML-escape config values interpolated into Info.plist - #15

Merged
tarwin merged 1 commit into
tarwin:mainfrom
slabbdev:fix/plist-xml-escaping
Sep 28, 2026
Merged

tarwin merged 1 commit into
tarwin:mainfrom
slabbdev:fix/plist-xml-escaping

Conversation

@slabbdev

Copy link
Copy Markdown
Contributor

What

The macOS build interpolated cfg strings into the bundle's Info.plist raw: title, id, version, size, the urlScheme/fileExtensions lists, the permission usage strings, and chrome vibrancy. Only url and userAgent were escaped. A & or < in any of the others produces invalid XML — plutil -lint rejects it and LaunchServices can't resolve the bundle, even though codesign signs it fine and the bare binary runs. url/userAgent's hand-rolled replace chains are folded into the same helper.

Repro (before this change)

Scaffold an app, set "title": "Tom & Jerry <Deck>", tinyjs build:

$ plutil -lint "dist/Tom & Jerry <Deck>.app/Contents/Info.plist"
…/Info.plist: (Encountered unknown ampersand-escape sequence at line 5)

$ open "dist/Tom & Jerry <Deck>.app"
The application cannot be opened because its executable is missing.   # exit 1

$ ./dist/plist-esc-test        # the bare binary runs fine — bundle-only breakage

After this change

Same app, same build:

$ plutil -lint "dist/Tom & Jerry <Deck>.app/Contents/Info.plist"
…/Info.plist: OK

$ codesign --verify --strict --deep "dist/Tom & Jerry <Deck>.app"   # OK

$ open "dist/Tom & Jerry <Deck>.app"    # launches; plist now carries
                                        # <string>Tom &amp; Jerry &lt;Deck&gt;</string>

escXml() escapes & < > ". Interpolations covered: window size, readAccess, userAgent, url, downloads, popups, chrome vibrancy, URL name + schemes, microphone/camera/speech usage strings, audio-capture reason, document type name + extensions, and the five root plist values (name, display name, identifier, version, executable).

The macOS build interpolates cfg strings (title, id, version, size,
urlScheme/fileExtensions lists, permission usage strings, chrome
vibrancy) into the bundle's Info.plist raw. A & or < in any of them —
'Tom & Jerry <Deck>' as a title, say — makes the plist invalid XML:
plutil -lint fails, and the bundle can't be opened (LaunchServices
reports 'executable is missing') even though codesign signs it fine and
the bare binary runs. url and userAgent were already escaped; everything
else wasn't.

Adds an escXml() helper and routes every cfg interpolation through it,
including the two hand-rolled replace chains. Verified on macOS 26:
before, a title containing ' & ' + '<Deck>' produces a bundle open()
refuses; after, plutil -lint passes, codesign --verify --strict --deep
passes, and the .app launches normally.
@tarwin
tarwin merged commit 00aa08c into tarwin:main Sep 28, 2026
@tarwin

tarwin commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Thanks @slabbdev this has been merged into main with some very small changes (just removing some chars that are not allowed). Thanks for finding this.

tarwin added a commit that referenced this pull request Sep 28, 2026
Changelog (md + site) for the debug.get gate fix, fetch header
validation and Info.plist escaping, crediting @slabbdev; skill version
strings bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@slabbdev
slabbdev deleted the fix/plist-xml-escaping branch October 3, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants