Files
summercms/.planning/phases/05-data-layer-full-fidelity/05-REVIEW-FIX.md
2026-09-19 15:41:27 +02:00

4.2 KiB
Raw Blame History

phase, fixed_at, review_path, iteration, findings_in_scope, fixed, skipped, status
phase fixed_at review_path iteration findings_in_scope fixed skipped status
05-data-layer-full-fidelity 2026-09-19T13:37:26Z .planning/phases/05-data-layer-full-fidelity/05-REVIEW.md 1 6 6 0 all_fixed

Phase 5: Code Review Fix Report

Fixed at: 2026-09-19T13:37:26Z Source review: .planning/phases/05-data-layer-full-fidelity/05-REVIEW.md Iteration: 1

Summary:

  • Findings in scope: 6 (1 critical, 5 warning; Info findings out of scope)
  • Fixed: 6
  • Skipped: 0

Fixed Issues

CR-01: StaticHandler cannot serve the URLs File.Thumb returns

Files modified: lagoon/attach/static.go, lagoon/attach/static_test.go Commit: 44126cc Applied fix: parsePublicBlobPath now returns the validated 4-segment path as the blob key. The partition-matches-filename check is skipped for thumb_* names, which Winter stores beside the original. StaticHandler opens that key directly instead of rebuilding via BlobKey(last-segment). TestStaticHandlerServesThumbURL generates a thumb, then GETs the URL Thumb returns through the handler.

WR-01: Thumb has no bounds on dimensions or decoded image size

Files modified: lagoon/attach/thumb.go, lagoon/attach/thumb_test.go Commit: c211822 Applied fix: Reject w/h that are non-positive or larger than 4096 before any bucket I/O. Cap the decoder with io.LimitReader at 32MiB and reject decoded images above 4096×4096 pixels. TestFileThumbRejectsOutOfRangeSize asserts rejected sizes never touch the bucket.

WR-02: A failed thumb encode still commits the blob, which then caches forever

Files modified: lagoon/attach/thumb.go, lagoon/attach/thumb_test.go Commit: e54f9d7 Applied fix: After encodeImage / Writer.Close, a failure deletes the thumb key (NotFound ignored) so the next Thumb regenerates instead of serving a partial object. TestFileThumbEncodeFailureDoesNotCache injects an encode error, asserts the blob is gone, then retries successfully.

WR-03: Laravel between/min/max are translated as length tags; nullable plus omitempty lets numeric 0 skip the range

Files modified: lagoon/validate.go, lagoon/validate_test.go Commit: fb12b22 Status: fixed: requires human verification Applied fix: nullable no longer emits go-playground omitempty; empty is gated only by isEmptyValue (nil / empty string, not numeric 0). integer/numeric between/min/max compare with big.Rat via numericString (dereferences *int and accepts digit strings) instead of go-playground length min/max. String-length between/min/max are unchanged. Tests: digit string "1991" passes; 0 / float64(0) / "0" fail integer|between:1889,2100; numeric 0 still passes min:0.

WR-04: File.IsPublic zero value writes false, opposite Winter and the SQL default

Files modified: lagoon/attach/file.go, lagoon/attach/file_test.go, lagoon/attach/lifecycle_test.go Commit: c12e657 Status: fixed: requires human verification Applied fix: IsPublic is *bool with gorm:"column:is_public;not null;default:true" plus File.Public() (nil → true). A non-pointer bool with default:true cannot persist false because GORM replaces the zero value with the default. Create without the field stores true; explicit &false stores false (TestFileCreateDefaultsIsPublic).

WR-05: StaticHandler serves every matching blob and never consults is_public

Files modified: lagoon/attach/static.go, lagoon/attach/file.go, lagoon/attach/static_test.go, lagoon/attach/file_test.go, lagoon/attach/lifecycle_test.go Commit: 56156ae Status: fixed: requires human verification Applied fix: Documented StaticHandler as public-disk-only (must not hold protected files; do not mount ungated on the app origin). Added StaticHandlerPublic, which looks up system_files by disk_name (or file ID parsed from thumb_<id>_…) before NewReader and 404s on missing rows and is_public=false. Stub test proves the gate runs before the blob is opened; postgres test covers public/private originals and thumbs.


Fixed: 2026-09-19T13:37:26Z Fixer: Claude (gsd-code-fixer) Iteration: 1