This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/README.md
2026-02-28 21:55:13 +00:00

3.4 KiB
Raw Blame History

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:

  1. NEW: server/handlers/safe_json.go Copy patches/server/handlers/safe_json.go into your tree. Contains: SafeJSON(), hardened scanJSON(), hardened scanTags().

  2. NEW: server/handlers/safe_json_test.go Copy patches/server/handlers/safe_json_test.go into your tree.

  3. EDIT: server/handlers/channels.go Apply these three changes (or diff against patches/server/handlers/channels.go):

    a. Remove the old scanJSON + jsonScanner + scanTags + tagsScanner definitions (~lines 101155). Replace with a comment:

    // scanJSON, scanTags, SafeJSON → safe_json.go
    

    b. Replace c.JSON(http.StatusOK, paginatedResponse{ in ListChannels with SafeJSON(c, http.StatusOK, paginatedResponse{

    c. Replace c.JSON(http.StatusCreated, ch) at end of CreateChannel with SafeJSON(c, http.StatusCreated, ch)

    d. Replace c.JSON(http.StatusOK, ch) at end of GetChannel with SafeJSON(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.js init sequence, debounce API.updateSettings() calls
  • A 100ms setTimeout wrapper that coalesces multiple rapid saves

Future work (not in this hotfix)

  • Apply SafeJSON to 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.Scan in models/models.go to 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.