fix: a library comes from the system directory, and the catalogue from one place - #68
Merged
Conversation
…m one place Two findings of the outside security review. One of them turned out to be wrong about the fix and right about the risk, and the guard written for it found a second call the review never looked at. S7 asked for syscall.NewLazySystemDLL instead of NewLazyDLL in the free space lookup. THAT FUNCTION DOES NOT EXIST: measured, the compiler calls it undefined, and syscall has no LoadLibraryEx and no LOAD_LIBRARY_SEARCH_ constants either. It lives in golang.org/x/sys/windows, which untouchable rule 11 keeps out of the command line binary - the comment beside that call has said so since 2026-08-25. And that call is safe as written: kernel32.dll is a KnownDLL, measured in the registry, so it is already mapped and the loader never consults a search order for it. The review was right about the next call rather than that one, and the guard written to hold that found it: internal/gui loaded uxtheme.dll by name, and uxtheme.dll is NOT a KnownDLL - the same registry key, thirty seven entries, and it is not among them. The Windows search order puts the directory the program was started from before the system one, so a file of that name beside a downloaded tfg-gui.exe would have been loaded and run. It now comes from an absolute path under the system directory, asked for through kernel32, which is the one library that cannot be diverted. Checked on a real system rather than by reading: the guard that asks Windows for dark menus calls it twice and still gets them. That change moved the library's NAME out of the syscall call and into a helper of ours, where the no telemetry scan would have walked past it. One shared list of loading calls now covers both, and our helper is on it. A guard that gets safer code and stops looking is worse than the code it was guarding. S9 is accepted as a fact and refused as a remedy. The localiser is a package variable with no lock, written once before any screen exists, which is a constraint rather than a property of the code. An atomic pointer would be an eighth defence nothing in this build could redden - seven have been removed for that. The constraint is mechanical instead: a guard fails when a second caller appears, so the language switch starts from the sentence on the variable rather than discovering it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Findings
S7andS9of the outside security review. The first was wrong about the fix, right about the risk, and the guard written to hold that risk found a second call the review never looked at.S7: the proposed fix does not exist, and the real one was elsewhereThe review asked for
syscall.NewLazySystemDLLinstead ofNewLazyDLLin the free space lookup, saying it is in the standard library.It is not. Measured: the compiler answers
undefined: syscall.NewLazySystemDLL, andsyscallhas noLoadLibraryExand noLOAD_LIBRARY_SEARCH_constants either. The System form lives ingolang.org/x/sys/windows, which untouchable rule 11 keeps out of the command line binary - the comment beside that call has said so since 2026-08-25.And that call is safe as written.
kernel32.dllis a KnownDLL: measured in the registry, 37 entries,*kernel32 = kernel32.dll. A KnownDLL is already mapped, so the loader hands back what is there and never consults a search order.So the finding is about the next call, not that one - and the guard written to hold that found it:
🔴
internal/guiloadeduxtheme.dllby name, anduxtheme.dllis not a KnownDLL - same registry key, not among the 37. The Windows search order puts the directory the program was started from before the system one, so a file of that name left beside a downloadedtfg-gui.exewould have been loaded into the window and run.It now comes from an absolute path under the system directory, asked for through
kernel32- the one library that cannot be diverted. Checked on a real system rather than by reading: the guard that asks Windows for dark menus calls it twice and still gets them.syscallcall and into a helper of ours, where the no-telemetry scan would have walked straight past it. One shared list of loading calls now covers both shapes, and our helper is on it. A guard that gets safer code and stops looking is worse than the code it was guarding.S9: accepted as a fact, refused as a remedyThe localiser is a package variable with no lock:
Loadwrites it, andsay,sayfandsayNread it on every string the window draws. Safe because it happens once, before any screen exists - a constraint rather than a property of the code.The review offered an
atomic.Pointeror writing the constraint down. An atomic pointer here would be an eighth defence nothing in this build could redden, and seven such pieces have been removed from this project for exactly that reason.So the constraint is mechanical instead: a guard fails when a second caller appears. Whoever adds the language switch - which
LoadBuiltIn's own comment names as its own piece of work - starts from the sentence on the variable rather than discovering the race. The race detector would not necessarily find it either, becausefyne.Doruns on the calling goroutine under the test driver.Guards
Two new, both with mutations, plus one existing mutation repaired and the no-telemetry registry extended. All caught.
🤖 Generated with Claude Code