Skip to content

Universal/CommaSpacing: fix arbitrary text escaping in error message#401

Merged
jrfnl merged 1 commit intodevelopfrom
feature/400-universal-commaspacing-bug-fix
Oct 28, 2025
Merged

Universal/CommaSpacing: fix arbitrary text escaping in error message#401
jrfnl merged 1 commit intodevelopfrom
feature/400-universal-commaspacing-bug-fix

Conversation

@jrfnl
Copy link
Copy Markdown
Member

@jrfnl jrfnl commented Oct 28, 2025

This commit fixes a bug which could lead to a Fatal error: Uncaught ValueError: Unknown format specifier "D".

Analysis of the bug:

  1. The original regex did not "remember" the chars before/after the targeted % sign, but the replacement is being done on the complete match, including those chars, not only on the % chars.
  2. Once that is fixed and the char before and after the % sign are now a) remembered and b) included in the replacement, the next problem becomes clear: the regex engine involves characters only in one match. This means that for a string like this: C%D%E%F, the C%D would become the first match. As the D was included in the first match, the regex engine only continues searching after, so the next match will be E%F, but the D%E would not be matched.
  3. To fix that, we execute the regex search & replace twice. This is safe to do as the original regex already contained protection against double escaping.

Includes tests.

Note: while the test now added would have caught this issue due to the fatal error, the original tests for this issue on line 85 - 93 of the same test file suffered from the same problem. The reason this was not caught via the tests, is because the test framework doesn't check the actual error messages, only the fact that an error is thrown (and fixed correctly).

It is the intention to at some point change this via a better base test class in PHPCSUtils, but for now, this will have to do.

Fixes #400

This commit fixes a bug which could lead to a `Fatal error: Uncaught ValueError: Unknown format specifier "D"`.

Analysis of the bug:
1. The original regex did not "remember" the chars before/after the targeted `%` sign, but the replacement is being done on the complete match, including those chars, not only on the `%` chars.
2. Once that is fixed and the char before and after the `%` sign are now a) remembered and b) included in the replacement, the next problem becomes clear: the regex engine involves characters only in **_one_** match.
    This means that for a string like this: `C%D%E%F`, the `C%D` would become the first match. As the `D` was included in the first match, the regex engine only continues searching _after_, so the next match will be `E%F`, but the `D%E` would not be matched.
3. To fix that, we execute the regex search & replace twice. This is safe to do as the original regex already contained protection against double escaping.

Includes tests.

Note: while the test now added would have caught this issue due to the fatal error, the _original_ tests for this issue on line 85 - 93 of the same test file suffered from the same problem. The reason this was not caught via the tests, is because the test framework doesn't check the actual error messages, only the fact that an error is thrown (and fixed correctly).

It is the intention to at some point change this via a better base test class in PHPCSUtils, but for now, this will have to do.

Fixes 400
@jrfnl jrfnl force-pushed the feature/400-universal-commaspacing-bug-fix branch from bebe01c to 0177e9c Compare October 28, 2025 13:44
@westonruter
Copy link
Copy Markdown

This fixes the issue I reported!

@jrfnl
Copy link
Copy Markdown
Member Author

jrfnl commented Oct 28, 2025

Thanks for testing @westonruter !

@jrfnl jrfnl merged commit e63a219 into develop Oct 28, 2025
79 checks passed
@jrfnl jrfnl deleted the feature/400-universal-commaspacing-bug-fix branch October 28, 2025 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CommaSpacingSniff doesn't escape vsprintf() placeholders adequately

2 participants