3.4 KiB
Hotfix Patches — v0.18.1 → v0.18.2
Priority order: apply top-to-bottom. All patches are independent except 1 and 2 which must be applied together.
Patch 1+2: Harden JSON scanning + SafeJSON (P0 — fixes broken chat list)
Root cause: Corrupt \x02 byte in a channel's settings column.
scanJSON accepts it, Gin's c.JSON() starts writing 200 + headers,
json.RawMessage.MarshalJSON() hits the bad byte, aborts, browser
gets truncated body → "Unexpected end of JSON input" → 0 chats loaded.
Files:
-
NEW:
server/handlers/safe_json.goCopypatches/server/handlers/safe_json.gointo your tree. Contains:SafeJSON(), hardenedscanJSON(), hardenedscanTags(). -
NEW:
server/handlers/safe_json_test.goCopypatches/server/handlers/safe_json_test.gointo your tree. -
EDIT:
server/handlers/channels.goApply these three changes (or diff againstpatches/server/handlers/channels.go):a. Remove the old
scanJSON+jsonScanner+scanTags+tagsScannerdefinitions (~lines 101–155). Replace with a comment:// scanJSON, scanTags, SafeJSON → safe_json.gob. Replace
c.JSON(http.StatusOK, paginatedResponse{in ListChannels withSafeJSON(c, http.StatusOK, paginatedResponse{c. Replace
c.JSON(http.StatusCreated, ch)at end of CreateChannel withSafeJSON(c, http.StatusCreated, ch)d. Replace
c.JSON(http.StatusOK, ch)at end of GetChannel withSafeJSON(c, http.StatusOK, ch)
Verify: go build ./... and go test ./server/handlers/ -run TestScanJSON -v
Data fix: Find and repair the corrupt row:
-- Find it
SELECT id, title, length(settings::text),
encode(substring(settings::bytea, 1, 20), 'hex') AS hex_preview
FROM channels
WHERE settings IS NOT NULL
AND settings::text ~ '[^\x20-\x7E\t\n\r]';
-- Fix it (reset to empty settings)
UPDATE channels SET settings = '{}'
WHERE settings IS NOT NULL
AND settings::text ~ '[^\x20-\x7E\t\n\r]';
Patch 3: Service Worker scheme guard (P1 — console noise)
Root cause: SW fetch handler tries to cache chrome-extension://
URLs. Cache.put() rejects non-http(s) schemes.
File: src/sw.js
Add scheme guard after const url = new URL(event.request.url);:
// Only handle http/https — ignore chrome-extension://, moz-extension://, etc.
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
return;
}
Or copy patches/src/sw.js.
Patch 4: Settings debounce (P2 — cosmetic waste)
Not included as a patch file — straightforward to implement:
- In
app.jsinit sequence, debounceAPI.updateSettings()calls - A 100ms
setTimeoutwrapper that coalesces multiple rapid saves
Future work (not in this hotfix)
-
Apply
SafeJSONto extension list endpoints (lines 47, 115 of extensions.go) — same vulnerability pattern, lower risk since extension data is seeded from known-good JSON. -
Harden
JSONMap.Scaninmodels/models.goto warn-and-default instead of returning error. Currently it fails the whole query which gives a clean 500, but it means one corrupt row blocks all results in a list query. -
Startup JSON integrity check (design doc §17.1 Layer 4) — scan all JSONB columns on boot, log warnings for corrupt data. Not blocking for hotfix but valuable for post-upgrade diagnostics.
-
JWT redaction in Gin log formatter for WebSocket URLs.