TestMapDecodedSizeTooLarge verifies that a small on-wire frame (well under the cap) which decompresses into a huge JSON payload is rejected. This is the "zstd bomb" case: a tiny compressed frame that would explode into a huge decoded payload for json.Decoder to consume.
(t *testing.T)
| 336 | // This is the "zstd bomb" case: a tiny compressed frame that would |
| 337 | // explode into a huge decoded payload for json.Decoder to consume. |
| 338 | func TestMapDecodedSizeTooLarge(t *testing.T) { |
| 339 | const max = 4 << 20 |
| 340 | big := strings.Repeat("a", 5<<20) // 5 MiB of 'a' |
| 341 | raw, err := json.Marshal(&tailcfg.MapResponse{Domain: big}) |
| 342 | if err != nil { |
| 343 | t.Fatal(err) |
| 344 | } |
| 345 | if int64(len(raw)) <= max { |
| 346 | t.Fatalf("raw JSON unexpectedly small: %d", len(raw)) |
| 347 | } |
| 348 | compressed := zstdFrame(t, raw) |
| 349 | if int64(len(compressed)) >= max { |
| 350 | t.Fatalf("compressed too large (%d); test needs a more compressible payload", len(compressed)) |
| 351 | } |
| 352 | |
| 353 | var wire bytes.Buffer |
| 354 | wireFrame(&wire, compressed) |
| 355 | |
| 356 | jdec := newTestPipeline(t, wire.Bytes(), max) |
| 357 | var resp tailcfg.MapResponse |
| 358 | err = jdec.Decode(&resp) |
| 359 | if err == nil { |
| 360 | t.Fatal("Decode: got nil error, want decoded-size-exceeded") |
| 361 | } |
| 362 | if !strings.Contains(err.Error(), "exceeds max") { |
| 363 | t.Errorf("Decode error = %q, want one containing %q", err, "exceeds max") |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | // TestMapBudgetResetsBetweenFrames verifies that the per-message decoded |
| 368 | // budget is reset at each new frame boundary. Two consecutive 3-MiB frames |
nothing calls this directly
no test coverage detected
searching dependent graphs…