Fix: ExecutionStore trusts persisted workspaceState data with zero validation (#19) - #20
Merged
thegoodengineer merged 1 commit intoAug 14, 2026
Conversation
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.
Fixes #19
Bug
ExecutionStore's constructor readcontext.workspaceState.get<Execution[]>(STORAGE_KEY, [])and trusted the result completely.Memento.get<T>()is a compile-time cast, not a runtime check — if the persisted value was ever anything other than a well-formed array ofExecutionobjects (corrupted state, a Settings Sync merge conflict, a future version of TruthLog changing the shape and the user downgrading, etc.), every store method that assumes an array (add()'sfindIndex/push/splice,treeProvider.getChildren()'sfilter/slice) would throw aTypeErrorthe moment the tree view rendered after activation — silently breaking the sidebar with no log line explaining why.Fix
isExecution(value): value is Execution, a runtime shape check for a single record (validatesid/command/outputare strings,truncatedis a boolean,startedAtis a number, and the optional fieldscwd/exitCode/endedAtare either the right type orundefined).loadPersisted()method that reads the raw value asunknown, and only accepts it ifArray.isArray(raw) && raw.every(isExecution). Otherwise it resets to[]and calls an injectedonWarningcallback with a message explaining that history was reset because the persisted data was invalid.ExecutionStore's constructor now takes an optional second parameter,onWarning?: (message: string) => void, mirroring the callback-injection patternrecorder.ts'screateRecorder(callbacks)already uses elsewhere in this codebase (rather than importingextension.ts's privatelog()directly, which would create a dependency in the wrong direction and make the store harder to unit test).extension.tsnow constructs it asnew ExecutionStore(context, log), so a corrupt-history warning shows up in the TruthLog output channel exactly like the shell-integration-disabled warning does.Design choice: if the top-level value isn't an array, or if any entry in it fails the shape check, the whole thing is treated as invalid and reset — I didn't try to filter out just the bad entries and keep the rest. This matches
getMaxEntries()'s existing all-or-nothing validate-or-fallback style in the same file, and avoids the ambiguity of partial corruption (is a record missing one field still "trustworthy" for the rest of its fields?).Testing
npm run compilepasses cleanly with no errors.src/test/suite/storeOrdering.test.tswith a newdescribe('loading persisted history', ...)block covering:null, and a non-array object → resets to[]and warns[]and warnsstore.add(...)→ doesn't throw, and the store is fully usable afterwardisExecution+ the array check) in a standalone plain-Node script against all five scenarios above, bypassing the blocked Electron host — all five behaved exactly as the new tests assert (reject/reject/reject/reject/accept).npm test(the Electron-hosted VS Code integration suite) in this environment — same Windows-sandbox limitation as those two PRs (Code.exedoesn't launch as a real GUI app here, matching why this repo's own CI only runs the integration suite onubuntu-latestunderxvfb). This PR's CI run will be the first real execution of the new test cases.