2 Commits

Author SHA1 Message Date
xarmian 157ca4e88f chore: bump Go toolchain to 1.26 (TASK-763) (#247)
* chore: bump Go toolchain to 1.26 (TASK-763)

Bump Go from 1.25 to 1.26 across all toolchain pins:

- go.mod — go 1.25.0 → go 1.26.0
- Dockerfile — golang:1.25-alpine → golang:1.26-alpine
- .github/workflows/ci.yml — three setup-go steps (Go, Go-Postgres, E2E jobs)
- .github/workflows/release.yml — release pipeline

No `toolchain` directive: the repo is pre-launch with no external
contributors yet, so we set the floor where we want it (hard requirement).

Verified locally before commit:
- golangci-lint v2.11.4 builds and runs under Go 1.26.2 (pinned in CI)
- golang:1.26-alpine and 1.26.2-alpine images present on Docker Hub
- go build ./... clean
- go vet ./... clean
- go test ./... all pass

Parent: PLAN-644 (OSS Repo Hygiene and Launch Polish).

* chore: gofmt -w under Go 1.26 (TASK-763)

Apply Go 1.26's gofmt to the codebase. ~41 files reformatted, all
struct-tag whitespace realignment — no semantic changes. Verified:

- gofmt -l ./cmd ./internal returns empty after
- go build ./... still clean
- go test ./... still passes (run before commit)

Bundling the gofmt diff with the toolchain bump in the same PR because
the formatting drift is a direct consequence of moving from 1.25 to
1.26; splitting them creates a mandatory two-PR ordering for no value.

Parent: PLAN-644.

* docs: bump documented Go floor to 1.26 (TASK-763)

Match go.mod's hard 1.26.0 requirement in the source-build instructions.
Caught by Codex review round 1 on PR #247.

- README.md:158 — "Go 1.25+" → "Go 1.26+"
- CONTRIBUTING.md:9 — "Go 1.25+" → "Go 1.26+"
2026-04-25 11:35:19 -04:00
xarmian baa1f75847 fix(server): cap JSON body + header size (TASK-663) (#184)
* fix(server): cap JSON body + header size (TASK-663)

decodeJSON called json.NewDecoder(r.Body).Decode(v) with no size limit.
Any client could POST a multi-GB JSON blob and watch Pad stream the
whole thing into one allocation — a single request could OOM the
process.

- internal/server/server.go: wrap r.Body in http.MaxBytesReader(..., 2 MB)
  inside decodeJSON. Every legitimate payload (item, collection, auth,
  etc.) is well under 100 KB so 2 MB is several orders of magnitude
  above real traffic. Factor out decodeJSONWithLimit(maxBytes) so
  future bulk-import endpoints can opt in to a larger cap without
  removing the wrapper.
- internal/server/server.go: set MaxHeaderBytes = 64 KiB on the
  http.Server (default is 1 MB). Plenty for cookies/auth/CORS while
  cheaply rejecting header-flood DoS.

Test: decode_json_test.go covers the 3 MiB body rejection, a happy
path, and a custom-limit override that rejects a 1 MiB body under a
256 KiB cap.

Parent: PLAN-643 (OSS Security Hardening).

* fix(server): bump workspace import JSON cap to 64 MiB per Codex P1

Codex flagged that handleImportWorkspace inherits the new 2 MiB default
cap, but WorkspaceExport contains full collections, items, comments,
and item_versions for the workspace — a realistic project backup
routinely exceeds 2 MiB, so existing exports stop re-importing.

Switch to decodeJSONWithLimit(64 << 20). 64 MiB is multiple orders of
magnitude above any realistic single-workspace backup while still far
from heap-exhaustion territory.
2026-04-21 22:47:09 -04:00