You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 59cc8bc
Browse filesBrowse the repository at this point in the historyBrowse files
fix(render): keep a seal's place until its preparation ends
run_seal held the seal's place in flight in the handler future, while
Seal::seal prepares the field on a blocking task. When a client
disconnected, the future and its place dropped, but the blocking work
ran on, so it went uncounted by the seal limit and the shutdown drain.
Seal::seal now takes the place and moves it into the blocking task,
which hands it back when it ends: a dropped seal keeps its place until
the preparation finishes, and the drain waits for it.
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Copy file name to clipboardExpand all lines: products/render/SECURITY-MATRIX.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@ conditions, follow the table.
16
16
| 7 | Request data values or the API key leak into the audit log or logs | Audit events carry a closed, value-free field set; lifecycle logs use fixed dimensions (method class, route template, status, latency, trace id); the seal route's `seal refused` warning carries only the seal id and the problem and `reason` names |`serve::audit_events_are_value_free` (canary + key scans over the audit file) |
17
17
| 8 | A render starts, or a document leaves, with no accepted audit record of it | The shared audit writer accepts a `file` entry only after `fsync` (owner-only file, single-writer lock) and a `stdout` entry only after it is written and flushed, then stops accepting after any failure. The `request` entry is accepted before the worker starts, else `503 audit-failed` and no render; the `response` entry is accepted before the document leaves, else `503 audit-failed` and no PDF or hash headers; `/ready` reports a stopped writer. Each call pairs its entries under a correlation the server draws, never the caller's `Idempotency-Key` (echoed only as `correlationId`), so calls sharing a key cannot be confused, and a call that ends before its outcome is written writes an `unfinished` response. Host-level tampering with the log is not defended here: the log carries no hash chain or signature (residual below) | `server::tests::the_request_entry_is_accepted_before_the_render_starts`; `server::tests::a_refused_request_entry_prevents_the_render`; `serve::a_refused_request_entry_prevents_the_render`; `serve::a_refused_response_entry_withholds_the_pdf`; `serve::a_render_writes_a_request_entry_then_a_response_entry_sharing_correlation` (file mode 0600); `server::tests::concurrent_calls_sharing_an_idempotency_key_pair_their_own_entries` |
18
18
| 9 | A render succeeds but is wrong on paper (missing glyphs) | Check-time label-script coverage over the actual label characters; package validates before it writes any output; render-time warnings; `--strict` fails on warnings |`scaffold::package_refuses_a_broken_bundle_before_writing_output` (CJK label, no font, so the package directory is never created) |
19
-
| 10 | Pathological template data exhausts CPU or memory | Serves render in a supervised worker process: killed at the configured timeout, which also bounds its exit after answering, `RLIMIT_AS` on Linux, recycled on panic; bounded concurrency, and as many seals in flight as render slots once a seal releases its slot; request body and output caps with hard ceilings |`serve::pathological_renders_are_bounded_and_the_service_recovers` (timeout or memory wall is audited; a fresh worker immediately serves a healthy render); `worker::tests::a_worker_that_lingers_after_answering_is_cut_off_at_the_deadline`; `serve_seal::seals_in_flight_beyond_the_render_concurrency_are_refused_as_rate_limited`|
19
+
| 10 | Pathological template data exhausts CPU or memory | Serves render in a supervised worker process: killed at the configured timeout, which also bounds its exit after answering, `RLIMIT_AS` on Linux, recycled on panic; bounded concurrency, and as many seals in flight as render slots once a seal releases its slot, a seal counted until its preparation ends even after its client has gone; request body and output caps with hard ceilings |`serve::pathological_renders_are_bounded_and_the_service_recovers` (timeout or memory wall is audited; a fresh worker immediately serves a healthy render); `worker::tests::a_worker_that_lingers_after_answering_is_cut_off_at_the_deadline`; `serve_seal::seals_in_flight_beyond_the_render_concurrency_are_refused_as_rate_limited`; `seal::tests::a_dropped_seal_keeps_its_place_until_its_blocking_work_ends`|
| 12 | Deeply nested JSON parse DoS |`serde_json` default 128-depth recursion limit on top of the tower body-limit layer | platform-owned; body limit exercised by config ceiling (`runtime.rs` 64 MiB hard cap) |
22
22
| 13 | Response header injection via echoed `Idempotency-Key`|`HeaderValue::from_str` rejects CR/LF/NUL; 128-char bound at read | code-reviewed; echo asserted in `serve::render_returns_pdf_with_hash_headers_matching_golden`|
0 commit comments