Fix CVE-2022-4993 stop routing foreign text into the Locale::Maketext format - #159
Open
robrwo wants to merge 1 commit into
Open
Fix CVE-2022-4993 stop routing foreign text into the Locale::Maketext format#159robrwo wants to merge 1 commit into
robrwo wants to merge 1 commit into
Conversation
…t format
The first argument to add_error is the Locale::Maketext FORMAT: bracket groups
in it are compiled into method-dispatch code. Three kinds of text that
FormHandler did not author reach that position, all of them carrying submitted
request data:
* _apply_actions traps warnings into $error_message (Validate.pm, the
$SIG{__WARN__} handler). A warning survives a SUCCESSFUL action, so an
ordinary numeric transform on a text field turns
`Argument "[sprintf,%2000000000d,0]" isn't numeric` into the format --
Perl quotes the value verbatim, so the group is well formed and reaches
CORE::sprintf with an attacker-chosen width (~GB allocation).
* a type constraint's failure message. Moose renders the rejected value with
Devel::PartialDump when it can load it, Type::Tiny always uses its own
dumper, and both render a reference in bracket-and-comma form -- so on any
field with `apply => [ Str ]`, two same-named request parameters put
`[ "a", "b" ]` in the format and maketext croaks, which add_error re-dies:
an unhandled 500 with no payload at all.
* exceptions from a coercion or transform.
* a date parser's error message. Field/Date.pm passes
`$strp->errmsg || $@` -- DateTime::Format::Strptime's own text -- straight
into the format position. DTFS 1.80 answers a rejected value with the
fixed string "Your datetime does not match your pattern.", so there is no
reachable payload through it today; that is a property of the current
version of a separate distribution rather than of this code, and the
`|| $@` fallback is a second channel that was not exercised. Escaped for
the same reason as the others.
Escape the bracket-notation metacharacters in all four before they are used
as a format. Tilde is Locale::Maketext's escape, and text with no brackets is
returned unchanged, so lexicon lookups and translated type-constraint messages
are byte-identical to before.
Escape the bracket-notation metacharacters in all four before they are used
as a format. Tilde is Locale::Maketext's escape, and text with no brackets is
returned unchanged, so lexicon lookups and translated type-constraint messages
are byte-identical to before.
Also: add_error derefs an arrayref first argument into (template, @Args). That
spelling is the same list-or-arrayref convenience idiom as add_element_class
and friends; it is not documented for add_error, and an instrumented run of the
distribution's own suite (150 files, 1491 tests) never reaches the branch. What
does reach it is request data -- `$field->add_error($field->value)` where the
request parser folded a duplicate parameter into an arrayref puts submitted
text in element 0. Since no message the library raises arrives in that shape,
treat an arrayref argument as a value: keep the deref, render element 0
literally. A caller who wants a compiled template passes it as a plain list,
`$field->add_error($template, @Args)`, which is the documented spelling and is
unchanged.
One case cannot be fixed here: an application that concatenates the value into
its own message, `add_error("The value '" . $field->value . "' is not
allowed")`, is indistinguishable from a legitimate template, so the add_error
POD now documents the hazard and the inert-argument idiom.
Behaviour trade-offs -- the only output changes outside the attack cases:
* a custom type constraint whose own message block uses bracket notation
(message { 'Try [quant,1,thing]' }) now renders that literally. Such a
block receives the rejected value and can interpolate it, so escaping it
is the safe default; a maintainer who would rather keep those compiled can
exempt the has_message branch specifically.
* an application calling the undocumented arrayref spelling with a template
that uses bracket notation, add_error([ 'Try [quant,_1,thing]', 3 ]), now
renders it literally; the list spelling of the same call still compiles.
FormHandler's own message templates, and application templates passed to
add_error together with their arguments, are unaffected.
Verified against the 0.40068 test suite: 150 files, 1491 tests, PASS both
before and after. A before/after table of rendered error messages
(maxlength, minlength, required, invalid select value, integer range,
duplicate-parameter arrays, application template with arguments, plain and
bracketed type messages, plain and bracketed warnings and exceptions) is
byte-identical except the lines above.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Robert Rothenberg <rrwo@cpansec.org>
Author
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.
The first argument to add_error is the Locale::Maketext FORMAT: bracket groups in it are compiled into method-dispatch code. Three kinds of text that FormHandler did not author reach that position, all of them carrying submitted request data:
Argument "[sprintf,%2000000000d,0]" isn't numericinto the format -- Perl quotes the value verbatim, so the group is well formed and reaches CORE::sprintf with an attacker-chosen width (~GB allocation).apply => [ Str ], two same-named request parameters put[ "a", "b" ]in the format and maketext croaks, which add_error re-dies: an unhandled 500 with no payload at all.$strp->errmsg || $@-- DateTime::Format::Strptime's own text -- straight into the format position. DTFS 1.80 answers a rejected value with the fixed string "Your datetime does not match your pattern.", so there is no reachable payload through it today; that is a property of the current version of a separate distribution rather than of this code, and the|| $@fallback is a second channel that was not exercised. Escaped for the same reason as the others.Escape the bracket-notation metacharacters in all four before they are used as a format. Tilde is Locale::Maketext's escape, and text with no brackets is returned unchanged, so lexicon lookups and translated type-constraint messages are byte-identical to before.
Escape the bracket-notation metacharacters in all four before they are used as a format. Tilde is Locale::Maketext's escape, and text with no brackets is returned unchanged, so lexicon lookups and translated type-constraint messages are byte-identical to before.
Also: add_error derefs an arrayref first argument into (template, @Args). That spelling is the same list-or-arrayref convenience idiom as add_element_class and friends; it is not documented for add_error, and an instrumented run of the distribution's own suite (150 files, 1491 tests) never reaches the branch. What does reach it is request data --
$field->add_error($field->value)where the request parser folded a duplicate parameter into an arrayref puts submitted text in element 0. Since no message the library raises arrives in that shape, treat an arrayref argument as a value: keep the deref, render element 0 literally. A caller who wants a compiled template passes it as a plain list,$field->add_error($template, @args), which is the documented spelling and is unchanged.One case cannot be fixed here: an application that concatenates the value into its own message,
add_error("The value '" . $field->value . "' is not allowed"), is indistinguishable from a legitimate template, so the add_error POD now documents the hazard and the inert-argument idiom.Behaviour trade-offs -- the only output changes outside the attack cases:
FormHandler's own message templates, and application templates passed to add_error together with their arguments, are unaffected.
Verified against the 0.40068 test suite: 150 files, 1491 tests, PASS both before and after. A before/after table of rendered error messages (maxlength, minlength, required, invalid select value, integer range, duplicate-parameter arrays, application template with arguments, plain and bracketed type messages, plain and bracketed warnings and exceptions) is byte-identical except the lines above.