Repository navigation
Conversation
Signed-off-by: Jeremie Drouet <jeremie.drouet@gmail.com>
Contributor
|
📈 This is how your metrics evolved 📉 |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Breaking — for 7.0. Adds a variant to
prelude::parser::Errorand marks it#[non_exhaustive].Two unbounded recursions let an untrusted template abort the process. Both end in
fatal runtime error: stack overflow, aborting— SIGABRT, not a catchable panic, so a service rendering user-supplied MJML cannot defend itself:mj-includecycles.a.mjmlandb.mjmlincluding each other, or a template including itself. No depth counter and no visited-set existed anywhere.Both now return
Error::DepthLimitExceeded, on the sync and async paths alike.Two limits, not one
My first attempt used a single counter and had to tune it to the tightest measured cliff, which landed at 20 — and that rejected 15 nested
<div>s inside anmj-text, which is ordinary WYSIWYG output. Trading a DoS on adversarial input for a hard failure on real input is the worse bug.The two recursions have different legitimate ranges and different per-level stack costs, so they get their own limits:
element_depthtracks real call-stack depth and is inherited across includes rather than reset, so the two cannot be combined to escape the bound — 8 chained includes each nested 60 deep errors cleanly instead of aborting. Inside a fully structured template the effective budget for HTML nesting is about 57 levels; realistic templates use roughly 10.Limits are consts, not configuration. If someone turns up with a legitimate template that needs more, that is the moment to make it configurable.
Also
mrml-wasmmatched the error enum exhaustively, so#[non_exhaustive]broke it. It gains a matching variant plus anUnknown { message }fallback carrying the core error'sDisplay— deliberately notunreachable!(), since a future core variant panicking inside a browser is the worst available failure mode.Five regression tests in
tests/depth-limit.rs, covering both recursion shapes on both paths plus one pinning that ~60 levels of legitimate nesting still parse — that last one is what would have caught the bad limit. All pass in debug and release. Existing tests unchanged at 621.