Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 08dc2bd

Browse filesBrowse files
committed
Allow OutputFormatter::escape() to be used for escaping URLs used in <href>
- escape() now escapes `>` as well as `<` - URLs containing escaped `<` and `>` are rendered correctly as is - user-provided URLs should now be safe to use (as in they cannot break the formatting) as long as they're piped through `escape()`
1 parent 93ed7c3 commit 08dc2bd
Copy full SHA for 08dc2bd

File tree

Expand file treeCollapse file tree

8 files changed

+27
-25
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+27
-25
lines changed

‎src/Symfony/Component/Console/Formatter/OutputFormatter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Formatter/OutputFormatter.php
+10-12Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,14 @@ public function __clone()
3434
}
3535

3636
/**
37-
* Escapes "<" special char in given text.
37+
* Escapes "<" and ">" special chars in given text.
3838
*
3939
* @param string $text Text to escape
40-
*
41-
* @return string Escaped text
40+
* @return string
4241
*/
4342
public static function escape($text)
4443
{
45-
$text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
44+
$text = preg_replace('/([^\\\\]|^)([<>])/', '$1\\\\$2', $text);
4645

4746
return self::escapeTrailingBackslash($text);
4847
}
@@ -144,9 +143,11 @@ public function formatAndWrap(string $message, int $width)
144143
{
145144
$offset = 0;
146145
$output = '';
147-
$tagRegex = '[a-z][^<>]*+';
146+
$openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
147+
$closeTagRegex = '[a-z][^<>]*+';
148148
$currentLineLength = 0;
149-
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
149+
preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
150+
150151
foreach ($matches[0] as $i => $match) {
151152
$pos = $match[1];
152153
$text = $match[0];
@@ -180,11 +181,7 @@ public function formatAndWrap(string $message, int $width)
180181

181182
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
182183

183-
if (str_contains($output, "\0")) {
184-
return strtr($output, ["\0" => '\\', '\\<' => '<']);
185-
}
186-
187-
return str_replace('\\<', '<', $output);
184+
return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
188185
}
189186

190187
/**
@@ -218,7 +215,8 @@ private function createStyleFromString(string $string): ?OutputFormatterStyleInt
218215
} elseif ('bg' == $match[0]) {
219216
$style->setBackground(strtolower($match[1]));
220217
} elseif ('href' === $match[0]) {
221-
$style->setHref($match[1]);
218+
$url = preg_replace('{\\\\([<>])}', '$1', $match[1]);
219+
$style->setHref($url);
222220
} elseif ('options' === $match[0]) {
223221
preg_match_all('([^,;]+)', strtolower($match[1]), $options);
224222
$options = array_shift($options);

‎src/Symfony/Component/Console/Tests/Fixtures/command_2.txt

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/Fixtures/command_2.txt
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
command 2 description
33

44
<comment>Usage:</comment>
5-
descriptor:command2 [options] [--] \<argument_name>
6-
descriptor:command2 -o|--option_name \<argument_name>
7-
descriptor:command2 \<argument_name>
5+
descriptor:command2 [options] [--] \<argument_name\>
6+
descriptor:command2 -o|--option_name \<argument_name\>
7+
descriptor:command2 \<argument_name\>
88

99
<comment>Arguments:</comment>
1010
<info>argument_name</info>

‎src/Symfony/Component/Console/Tests/Fixtures/command_mbstring.txt

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/Fixtures/command_mbstring.txt
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
command åèä description
33

44
<comment>Usage:</comment>
5-
descriptor:åèä [options] [--] \<argument_åèä>
6-
descriptor:åèä -o|--option_name \<argument_name>
7-
descriptor:åèä \<argument_name>
5+
descriptor:åèä [options] [--] \<argument_åèä\>
6+
descriptor:åèä -o|--option_name \<argument_name\>
7+
descriptor:åèä \<argument_name\>
88

99
<comment>Arguments:</comment>
1010
<info>argument_åèä</info>
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<info>argument_name</info> argument description<comment> [default: "\<comment>style\</>"]</comment>
1+
<info>argument_name</info> argument description<comment> [default: "\<comment\>style\</\>"]</comment>
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: "\<comment>style\</>"]</comment>
1+
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: "\<comment\>style\</\>"]</comment>
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: ["\<comment>Hello\</comment>","\<info>world\</info>"]]</comment><comment> (multiple values allowed)</comment>
1+
<info>-o, --option_name=OPTION_NAME</info> option description<comment> [default: ["\<comment\>Hello\</comment\>","\<info\>world\</info\>"]]</comment><comment> (multiple values allowed)</comment>

‎src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ public function testLGCharEscaping()
3232
$this->assertEquals('foo << bar \\', $formatter->format('foo << bar \\'));
3333
$this->assertEquals("foo << \033[32mbar \\ baz\033[39m \\", $formatter->format('foo << <info>bar \\ baz</info> \\'));
3434
$this->assertEquals('<info>some info</info>', $formatter->format('\\<info>some info\\</info>'));
35-
$this->assertEquals('\\<info>some info\\</info>', OutputFormatter::escape('<info>some info</info>'));
35+
$this->assertEquals('\\<info\\>some info\\</info\\>', OutputFormatter::escape('<info>some info</info>'));
36+
// every < and > gets escaped if not already escaped, but already escaped ones do not get escaped again
37+
// and escaped backslashes remain as such, same with backslashes escaping non-special characters
38+
$this->assertEquals('foo \\< bar \\< baz \\\\< foo \\> bar \\> baz \\\\> \\x', OutputFormatter::escape('foo < bar \\< baz \\\\< foo > bar \\> baz \\\\> \\x'));
3639

3740
$this->assertEquals(
3841
"\033[33mSymfony\\Component\\Console does work very well!\033[39m",
@@ -259,6 +262,7 @@ public function provideDecoratedAndNonDecoratedOutput()
259262
['<question>some question</question>', 'some question', "\033[30;46msome question\033[39;49m"],
260263
['<fg=red>some text with inline style</>', 'some text with inline style', "\033[31msome text with inline style\033[39m"],
261264
['<href=idea://open/?file=/path/SomeFile.php&line=12>some URL</>', 'some URL', "\033]8;;idea://open/?file=/path/SomeFile.php&line=12\033\\some URL\033]8;;\033\\"],
265+
['<href=https://example.com/\<woohoo\>>some URL with \<woohoo\></>', 'some URL with <woohoo>', "\033]8;;https://example.com/<woohoo>\033\\some URL with <woohoo>\033]8;;\033\\"],
262266
['<href=idea://open/?file=/path/SomeFile.php&line=12>some URL</>', 'some URL', 'some URL', 'JetBrains-JediTerm'],
263267
];
264268
}

‎src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ public function testFormatBlockLGEscaping()
8383
$formatter = new FormatterHelper();
8484

8585
$this->assertEquals(
86-
'<error> </error>'."\n".
87-
'<error> \<info>some info\</info> </error>'."\n".
88-
'<error> </error>',
86+
'<error> </error>'."\n".
87+
'<error> \<info\>some info\</info\> </error>'."\n".
88+
'<error> </error>',
8989
$formatter->formatBlock('<info>some info</info>', 'error', true),
9090
'::formatBlock() escapes \'<\' chars'
9191
);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.