mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Fix regexes display issue
This commit is contained in:
@@ -304,6 +304,19 @@ if (! function_exists('escapeString')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('regex_display_value')) {
|
||||
/**
|
||||
* Decode entity-encoded regex text at the presentation boundary.
|
||||
*
|
||||
* Blade should still render the returned value with escaped {{ }} output so regexes that
|
||||
* contain HTML-looking text remain safe while displaying named groups and quotes readably.
|
||||
*/
|
||||
function regex_display_value(mixed $value): string
|
||||
{
|
||||
return html_entity_decode((string) ($value ?? ''), ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('realDuration')) {
|
||||
|
||||
function realDuration(mixed $milliseconds): string
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
id="group_regex"
|
||||
name="group_regex"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
value="{{ htmlspecialchars($regex->group_regex ?? '') }}"
|
||||
value="{{ $regex->group_regex ?? '' }}"
|
||||
required>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
@@ -66,7 +66,7 @@
|
||||
name="regex"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
|
||||
rows="4"
|
||||
required>{{ htmlspecialchars($regex->regex ?? '') }}</textarea>
|
||||
required>{{ regex_display_value($regex->regex ?? '') }}</textarea>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Regex to use when categorizing releases.<br>
|
||||
@@ -87,7 +87,7 @@
|
||||
<textarea id="description"
|
||||
name="description"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
rows="3">{{ htmlspecialchars($regex->description ?? '') }}</textarea>
|
||||
rows="3">{{ $regex->description ?? '' }}</textarea>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Description for this regex. You can include an example usenet subject this regex would match on.
|
||||
|
||||
@@ -94,8 +94,8 @@
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<div class="max-w-[200px] wrap-break-word">
|
||||
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-xs break-all" title="{{ htmlspecialchars($row->regex) }}">
|
||||
{{ htmlspecialchars($row->regex) }}
|
||||
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-xs break-all" title="{{ regex_display_value($row->regex) }}">
|
||||
{{ regex_display_value($row->regex) }}
|
||||
</code>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
id="group_regex"
|
||||
name="group_regex"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
value="{{ htmlspecialchars($regex->group_regex ?? '') }}"
|
||||
value="{{ $regex->group_regex ?? '' }}"
|
||||
required>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -66,7 +66,7 @@
|
||||
name="regex"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
|
||||
rows="4"
|
||||
required>{{ htmlspecialchars($regex->regex ?? '') }}</textarea>
|
||||
required>{{ regex_display_value($regex->regex ?? '') }}</textarea>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
Regex to use when grouping binaries into collections.<br>
|
||||
@@ -87,7 +87,7 @@
|
||||
<textarea id="description"
|
||||
name="description"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
rows="3">{{ htmlspecialchars($regex->description ?? '') }}</textarea>
|
||||
rows="3">{{ $regex->description ?? '' }}</textarea>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
Description for this regex. You can include an example usenet subject this regex would match on.
|
||||
|
||||
@@ -93,8 +93,8 @@
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<div class="max-w-[200px] wrap-break-word">
|
||||
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-xs break-all" title="{{ htmlspecialchars($row->regex) }}">
|
||||
{{ htmlspecialchars($row->regex) }}
|
||||
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-xs break-all" title="{{ regex_display_value($row->regex) }}">
|
||||
{{ regex_display_value($row->regex) }}
|
||||
</code>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
|
||||
rows="4"
|
||||
required
|
||||
placeholder="/^(?P<name>.*?)([\. ]S\d{1,3}[\. ]?E\d{1,3})/i">{{ $regex }}</textarea>
|
||||
placeholder="/^(?P<name>.*?)([\. ]S\d{1,3}[\. ]?E\d{1,3})/i">{{ regex_display_value($regex) }}</textarea>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Enter the regex pattern to test. Include delimiters and flags.
|
||||
</p>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
id="group_regex"
|
||||
name="group_regex"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
value="{{ htmlspecialchars($regex->group_regex ?? '') }}"
|
||||
value="{{ $regex->group_regex ?? '' }}"
|
||||
required>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
@@ -66,7 +66,7 @@
|
||||
name="regex"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
|
||||
rows="4"
|
||||
required>{{ htmlspecialchars($regex->regex ?? '') }}</textarea>
|
||||
required>{{ regex_display_value($regex->regex ?? '') }}</textarea>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Regex to use when renaming releases.<br>
|
||||
@@ -87,7 +87,7 @@
|
||||
<textarea id="description"
|
||||
name="description"
|
||||
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
rows="3">{{ htmlspecialchars($regex->description ?? '') }}</textarea>
|
||||
rows="3">{{ $regex->description ?? '' }}</textarea>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Description for this regex. You can include an example release name this regex would match on.
|
||||
|
||||
@@ -93,8 +93,8 @@
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<div class="max-w-[200px] wrap-break-word">
|
||||
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-xs break-all" title="{{ htmlspecialchars($row->regex) }}">
|
||||
{{ htmlspecialchars($row->regex) }}
|
||||
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-xs break-all" title="{{ regex_display_value($row->regex) }}">
|
||||
{{ regex_display_value($row->regex) }}
|
||||
</code>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
|
||||
rows="4"
|
||||
required
|
||||
placeholder="/^(?P<name>.*?)([\. ]S\d{1,3}[\. ]?E\d{1,3})/i">{{ $regex }}</textarea>
|
||||
placeholder="/^(?P<name>.*?)([\. ]S\d{1,3}[\. ]?E\d{1,3})/i">{{ regex_display_value($regex) }}</textarea>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Enter the regex pattern to test. Include delimiters and flags.
|
||||
</p>
|
||||
|
||||
@@ -166,6 +166,63 @@ class AdminRegexesControllerTest extends TestCase
|
||||
->assertNotFound();
|
||||
}
|
||||
|
||||
public function test_admin_regex_list_pages_decode_entity_encoded_regexes_without_double_escaping(): void
|
||||
{
|
||||
$admin = $this->createUserWithRole('Admin');
|
||||
$rawRegex = '/^(?P<name>.+?) - "(?P<title>.+?)"$/i';
|
||||
$entityEncodedRegex = '/^(?P<name>.+?) - "(?P<title>.+?)"$/i';
|
||||
$scriptTag = '<'.'script>alert("x")</'.'script>';
|
||||
$htmlLookingRegex = '/^(?P<name>.+?)'.$scriptTag.'$/i';
|
||||
|
||||
foreach (['release_naming_regexes', 'collection_regexes', 'category_regexes'] as $table) {
|
||||
$this->insertRegexFixture($table, $rawRegex, 'Raw regex fixture');
|
||||
$this->insertRegexFixture($table, $entityEncodedRegex, 'Entity encoded regex fixture');
|
||||
$this->insertRegexFixture($table, $htmlLookingRegex, 'HTML-looking regex fixture');
|
||||
}
|
||||
|
||||
foreach ([
|
||||
route('admin.release_naming_regexes-list'),
|
||||
route('admin.collection_regexes-list'),
|
||||
route('admin.category_regexes-list'),
|
||||
] as $url) {
|
||||
$response = $this->actingAs($admin)->get($url);
|
||||
|
||||
$response->assertOk()
|
||||
->assertSee(e($rawRegex), false)
|
||||
->assertSee(e(html_entity_decode($entityEncodedRegex, ENT_QUOTES | ENT_HTML5, 'UTF-8')), false)
|
||||
->assertSee(e(html_entity_decode($htmlLookingRegex, ENT_QUOTES | ENT_HTML5, 'UTF-8')), false)
|
||||
->assertDontSee('&quot;', false)
|
||||
->assertDontSee('&lt;', false)
|
||||
->assertDontSee('&gt;', false)
|
||||
->assertDontSee($scriptTag, false);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_admin_regex_edit_pages_decode_entity_encoded_regexes_without_double_escaping(): void
|
||||
{
|
||||
$admin = $this->createUserWithRole('Admin');
|
||||
$entityEncodedRegex = '/^(?P<name>.+?) - "(?P<title>.+?)"$/i';
|
||||
$expectedRenderedRegex = e(html_entity_decode($entityEncodedRegex, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
|
||||
|
||||
$releaseNamingRegexId = $this->insertRegexFixture('release_naming_regexes', $entityEncodedRegex, 'Release naming encoded regex');
|
||||
$collectionRegexId = $this->insertRegexFixture('collection_regexes', $entityEncodedRegex, 'Collection encoded regex');
|
||||
$categoryRegexId = $this->insertRegexFixture('category_regexes', $entityEncodedRegex, 'Category encoded regex');
|
||||
|
||||
foreach ([
|
||||
route('admin.release_naming_regexes-edit', ['id' => (string) $releaseNamingRegexId]),
|
||||
route('admin.collection_regexes-edit', ['id' => (string) $collectionRegexId]),
|
||||
route('admin.category_regexes-edit', ['id' => (string) $categoryRegexId]),
|
||||
] as $url) {
|
||||
$this->actingAs($admin)
|
||||
->get($url)
|
||||
->assertOk()
|
||||
->assertSee($expectedRenderedRegex, false)
|
||||
->assertDontSee('&quot;', false)
|
||||
->assertDontSee('&lt;', false)
|
||||
->assertDontSee('&gt;', false);
|
||||
}
|
||||
}
|
||||
|
||||
private function setEnvironmentValue(string $key, ?string $value): void
|
||||
{
|
||||
if ($value === null) {
|
||||
@@ -373,6 +430,23 @@ class AdminRegexesControllerTest extends TestCase
|
||||
return $user->fresh();
|
||||
}
|
||||
|
||||
private function insertRegexFixture(string $table, string $regex, string $description): int
|
||||
{
|
||||
$data = [
|
||||
'group_regex' => 'alt\\.binaries\\.example',
|
||||
'regex' => $regex,
|
||||
'description' => $description,
|
||||
'ordinal' => 10,
|
||||
'status' => 1,
|
||||
];
|
||||
|
||||
if ($table === 'category_regexes') {
|
||||
$data['categories_id'] = 1;
|
||||
}
|
||||
|
||||
return (int) DB::table($table)->insertGetId($data);
|
||||
}
|
||||
|
||||
private function resetGlobalComposerState(): void
|
||||
{
|
||||
$reflection = new ReflectionClass(GlobalDataComposer::class);
|
||||
|
||||
Reference in New Issue
Block a user