|
@@ -0,0 +1,42 @@
|
|
|
|
|
+# Cap decompressed project size
|
|
|
|
|
+
|
|
|
|
|
+Branch: `limit-decompression` → `main`
|
|
|
|
|
+
|
|
|
|
|
+PR 2 of the review fixes plan (`docs/review-fixes-plan.md`). Part of 0.5.2; the version does not change in this PR.
|
|
|
|
|
+
|
|
|
|
|
+## What changed
|
|
|
|
|
+
|
|
|
|
|
+**Projects over 512 MB decompressed are refused (S2).** `xpj.Read` used to read the whole gzip stream into memory, however large it was. In the review, a 1 MB file padded with spaces used 3.1 GB and took 10 seconds. `Read` now reads through `io.LimitReader` and stops one byte past the new `xpj.MaxDecompressed` (512 MB, as decided). Past that, it returns `xpj: project is larger than 512 MB when decompressed`. A comment on the constant gives the reason. Device projects are about 3 MB decompressed, so real projects are far below the limit.
|
|
|
|
|
+
|
|
|
|
|
+**Test.** `TestDecompressionLimit` builds its gzip streams in memory with `gzip.NewWriter`; nothing new is committed:
|
|
|
|
|
+- A minimal project padded with spaces to one byte over the limit is refused with that exact error.
|
|
|
|
|
+- The same project padded to exactly the limit still decodes.
|
|
|
|
|
+- Against the old `Read`, the over-limit case loads without error, so the test fails.
|
|
|
|
|
+- It takes about 11 seconds, and `go test -short` skips it.
|
|
|
|
|
+
|
|
|
|
|
+## Notes
|
|
|
|
|
+
|
|
|
|
|
+**Memory is still high near the limit.** The limit stops the read early, but memory still grows well past 512 MB:
|
|
|
|
|
+- **Over the limit:** a file padded to 3.1 GB (6 MB compressed) is now refused after 1.3 seconds, at a peak of about 1.3 GB. The buffer `io.ReadAll` grows can reach roughly 2.5 times what it holds before the check runs.
|
|
|
|
|
+- **Exactly at the limit:** a file that passes the check still costs about 2.3 GB while it decodes, because the JSON decoder keeps its own copy of the padding.
|
|
|
|
|
+
|
|
|
|
|
+This is what the 512 MB decision implies, and it no longer grows without bound. If you want it lower, there are two options for a later PR:
|
|
|
|
|
+- Lower the limit. Device projects are about 3 MB, so 64 MB would still leave plenty of room.
|
|
|
|
|
+- Read into a buffer sized up front, which saves the growth but not the decoder's copy.
|
|
|
|
|
+
|
|
|
|
|
+**The test needs memory.** `TestDecompressionLimit` peaks at about 2.3 GB while it decodes the at-the-limit file. The server ran it with no trouble alongside the other tests. Use `go test -short ./...` to skip it.
|
|
|
|
|
+
|
|
|
|
|
+## Testing
|
|
|
|
|
+
|
|
|
|
|
+**On the server:**
|
|
|
|
|
+- `go test ./...` passes, including the round-trip tests, unchanged.
|
|
|
|
|
+- `go vet ./...` and `staticcheck ./...` are clean.
|
|
|
|
|
+- `govulncheck ./...` reports only GO-2026-6859 in `net/http`, which waits for Go 1.27.4 in PR 4.
|
|
|
|
|
+- `npm install`, `npm run check` (0 errors, 0 warnings) and `npm run build` pass. The lockfile `npm install` wrote was not committed.
|
|
|
|
|
+- `wails3 build` builds, and `timeout 15 xvfb-run -a bin/mpckit` starts and runs. The log holds only the usual headless GTK warnings.
|
|
|
|
|
+
|
|
|
|
|
+**On the laptop (PowerShell, in the repo):**
|
|
|
|
|
+1. `git fetch; git checkout limit-decompression`
|
|
|
|
|
+2. `go test -run TestDecompressionLimit -v ./internal/xpj`: `PASS`, in about 10–20 seconds. It uses about 2.3 GB of memory.
|
|
|
|
|
+3. `go test ./...`: all `ok`.
|
|
|
|
|
+4. `wails3 dev`: load `testdata\device\DemoFile.xpj` and `testdata\accepted\MpckitT1.xpj`. Both open as before.
|