Skip to content

Commit

Permalink
SearchTermParseEvent::matches(), because preg_match is a footgun
Browse files Browse the repository at this point in the history
  • Loading branch information
shish committed Oct 10, 2024
1 parent f796471 commit f8df890
Show file tree
Hide file tree
Showing 18 changed files with 99 additions and 77 deletions.
6 changes: 2 additions & 4 deletions ext/approval/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,20 @@ public function onUserBlockBuilding(UserBlockBuildingEvent $event): void
}
}

public const SEARCH_REGEXP = "/^approved:(yes|no)/";
public const SEARCH_REGEXP = "/^approved:(yes|no)/i";
public function onSearchTermParse(SearchTermParseEvent $event): void
{
global $user, $config;

if ($config->get_bool(ApprovalConfig::IMAGES)) {
$matches = [];

if (is_null($event->term) && $this->no_approval_query($event->context)) {
$event->add_querylet(new Querylet("approved = :true", ["true" => true]));
}

if (is_null($event->term)) {
return;
}
if (preg_match(self::SEARCH_REGEXP, strtolower($event->term), $matches)) {
if ($matches = $event->matches(self::SEARCH_REGEXP)) {
if ($user->can(Permissions::APPROVE_IMAGE) && $matches[1] == "no") {
$event->add_querylet(new Querylet("approved != :true", ["true" => true]));
} else {
Expand Down
3 changes: 1 addition & 2 deletions ext/artists/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
if (preg_match("/^(author|artist)[=|:](.*)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^(author|artist)[=|:](.*)$/i")) {
$char = $matches[2];
$event->add_querylet(new Querylet("author = :author_char", ["author_char" => $char]));
}
Expand Down
7 changes: 3 additions & 4 deletions ext/comment/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,15 +381,14 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
if (preg_match("/^comments([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^comments([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$comments = $matches[2];
$event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM comments GROUP BY image_id HAVING count(image_id) $cmp $comments)"));
} elseif (preg_match("/^commented_by[=|:](.*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^commented_by[=|:](.*)$/i")) {
$user_id = User::name_to_id($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)"));
} elseif (preg_match("/^commented_by_userno[=|:]([0-9]+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^commented_by_userno[=|:]([0-9]+)$/i")) {
$user_id = int_escape($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)"));
}
Expand Down
8 changes: 4 additions & 4 deletions ext/favorites/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,17 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
}

$matches = [];
if (preg_match("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$favorites = $matches[2];
$event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE favorites $cmp $favorites)"));
} elseif (preg_match("/^favorited_by[=|:](.*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^favorited_by[=|:](.*)$/i")) {
$user_id = User::name_to_id($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)"));
} elseif (preg_match("/^favorited_by_userno[=|:](\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^favorited_by_userno[=|:](\d+)$/i")) {
$user_id = int_escape($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)"));
} elseif (preg_match("/^order[=|:](favorites)(?:_(desc|asc))?$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^order[=|:](favorites)(?:_(desc|asc))?$/i")) {
$default_order_for_column = "DESC";
$sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column;
$event->order = "images.favorites $sort";
Expand Down
15 changes: 15 additions & 0 deletions ext/index/events.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ public function add_tag_condition(TagCondition $c): void
{
$this->tag_conditions[] = $c;
}

/**
* @return array<string>|null
*/
public function matches(string $regex): ?array
{
$matches = [];
if (is_null($this->term)) {
return null;
}
if (\Safe\preg_match($regex, $this->term, $matches) === 1) {
return $matches;
}
return null;
}
}

class SearchTermParseException extends InvalidInput
Expand Down
21 changes: 10 additions & 11 deletions ext/index/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,44 +198,43 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
if (preg_match("/^filesize([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+[kmg]?b?)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^filesize([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+[kmg]?b?)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$val = parse_shorthand_int($matches[2]);
$event->add_querylet(new Querylet("images.filesize $cmp :val{$event->id}", ["val{$event->id}" => $val]));
} elseif (preg_match("/^id=([\d,]+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^id=([\d,]+)$/i")) {
$val = array_map(fn ($x) => int_escape($x), explode(",", $matches[1]));
$set = implode(",", $val);
$event->add_querylet(new Querylet("images.id IN ($set)"));
} elseif (preg_match("/^id([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^id([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$val = int_escape($matches[2]);
$event->add_querylet(new Querylet("images.id $cmp :val{$event->id}", ["val{$event->id}" => $val]));
} elseif (preg_match("/^(hash|md5)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^(hash|md5)[=|:]([0-9a-fA-F]*)$/i")) {
$hash = strtolower($matches[2]);
$event->add_querylet(new Querylet('images.hash = :hash', ["hash" => $hash]));
} elseif (preg_match("/^(phash)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^(phash)[=|:]([0-9a-fA-F]*)$/i")) {
$phash = strtolower($matches[2]);
$event->add_querylet(new Querylet('images.phash = :phash', ["phash" => $phash]));
} elseif (preg_match("/^(filename|name)[=|:](.+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^(filename|name)[=|:](.+)$/i")) {
$filename = strtolower($matches[2]);
$event->add_querylet(new Querylet("lower(images.filename) LIKE :filename{$event->id}", ["filename{$event->id}" => "%$filename%"]));
} elseif (preg_match("/^posted([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9-]*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^posted([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9-]*)$/i")) {
// TODO Make this able to search = without needing a time component.
$cmp = ltrim($matches[1], ":") ?: "=";
$val = $matches[2];
$event->add_querylet(new Querylet("images.posted $cmp :posted{$event->id}", ["posted{$event->id}" => $val]));
} elseif (preg_match("/^order[=|:](id|width|height|length|filesize|filename)[_]?(desc|asc)?$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^order[=|:](id|width|height|length|filesize|filename)[_]?(desc|asc)?$/i")) {
$ord = strtolower($matches[1]);
$default_order_for_column = preg_match("/^(id|filename)$/", $matches[1]) ? "ASC" : "DESC";
$sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column;
$event->order = "images.$ord $sort";
} elseif (preg_match("/^order[=|:]random[_]([0-9]{1,4})$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^order[=|:]random[_]([0-9]{1,4})$/i")) {
// requires a seed to avoid duplicates
// since the tag can't be changed during the parseevent, we instead generate the seed during submit using js
$seed = (int)$matches[1];
$event->order = $database->seeded_random($seed, "images.id");
} elseif (preg_match("/^order[=|:]dailyshuffle$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^order[=|:]dailyshuffle$/i")) {
// will use today's date as seed, thus allowing for a dynamic randomized list without outside intervention.
// This way the list will change every day, giving a more dynamic feel to the imageboard.
// recommended to change homepage to "post/list/order:dailyshuffle/1"
Expand Down
13 changes: 6 additions & 7 deletions ext/media/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,29 +243,28 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
if (preg_match("/^content[=|:]((video)|(audio)|(image)|(unknown))$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^content[=|:]((video)|(audio)|(image)|(unknown))$/i")) {
$field = $matches[1];
if ($field === "unknown") {
$event->add_querylet(new Querylet("video IS NULL OR audio IS NULL OR image IS NULL"));
} else {
$event->add_querylet(new Querylet("$field = :true", ["true" => true]));
}
} elseif (preg_match("/^ratio([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+):(\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^ratio([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+):(\d+)$/i")) {
$cmp = preg_replace_ex('/^:/', '=', $matches[1]);
$args = ["width{$event->id}" => int_escape($matches[2]), "height{$event->id}" => int_escape($matches[3])];
$event->add_querylet(new Querylet("width / :width{$event->id} $cmp height / :height{$event->id}", $args));
} elseif (preg_match("/^size([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)x(\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^size([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)x(\d+)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$args = ["width{$event->id}" => int_escape($matches[2]), "height{$event->id}" => int_escape($matches[3])];
$event->add_querylet(new Querylet("width $cmp :width{$event->id} AND height $cmp :height{$event->id}", $args));
} elseif (preg_match("/^width([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^width([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$event->add_querylet(new Querylet("width $cmp :width{$event->id}", ["width{$event->id}" => int_escape($matches[2])]));
} elseif (preg_match("/^height([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^height([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$event->add_querylet(new Querylet("height $cmp :height{$event->id}", ["height{$event->id}" => int_escape($matches[2])]));
} elseif (preg_match("/^length([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(.+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^length([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(.+)$/i")) {
$value = parse_to_milliseconds($matches[2]);
$cmp = ltrim($matches[1], ":") ?: "=";
$event->add_querylet(new Querylet("length $cmp :length{$event->id}", ["length{$event->id}" => $value]));
Expand Down
5 changes: 2 additions & 3 deletions ext/mime/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,11 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
// check for tags first as tag based searches are more common.
if (preg_match("/^ext[=|:]([a-zA-Z0-9]+)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^ext[=|:]([a-zA-Z0-9]+)$/i")) {
$ext = strtolower($matches[1]);
$event->add_querylet(new Querylet('images.ext = :ext', ["ext" => $ext]));
} elseif (preg_match("/^mime[=|:](.+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^mime[=|:](.+)$/i")) {
$mime = strtolower($matches[1]);
$event->add_querylet(new Querylet("images.mime = :mime", ["mime" => $mime]));
}
Expand Down
9 changes: 4 additions & 5 deletions ext/notes/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,18 +205,17 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
if (preg_match("/^note[=|:](.*)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^note[=|:](.*)$/i")) {
$notes = int_escape($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE note = $notes)"));
} elseif (preg_match("/^notes([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)%/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^notes([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)%/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$notes = $matches[2];
$event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE notes $cmp $notes)"));
} elseif (preg_match("/^notes_by[=|:](.*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^notes_by[=|:](.*)$/i")) {
$user_id = User::name_to_id($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE user_id = $user_id)"));
} elseif (preg_match("/^(notes_by_userno|notes_by_user_id)[=|:](\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^(notes_by_userno|notes_by_user_id)[=|:](\d+)$/i")) {
$user_id = int_escape($matches[2]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE user_id = $user_id)"));
}
Expand Down
15 changes: 7 additions & 8 deletions ext/numeric_score/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,36 +319,35 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
if (preg_match("/^score([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(-?\d+)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^score([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(-?\d+)$/i")) {
$cmp = ltrim($matches[1], ":") ?: "=";
$score = $matches[2];
$event->add_querylet(new Querylet("numeric_score $cmp $score"));
} elseif (preg_match("/^upvoted_by[=|:](.*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^upvoted_by[=|:](.*)$/i")) {
$duser = User::by_name($matches[1]);
$event->add_querylet(new Querylet(
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=1)",
["ns_user_id" => $duser->id]
));
} elseif (preg_match("/^downvoted_by[=|:](.*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^downvoted_by[=|:](.*)$/i")) {
$duser = User::by_name($matches[1]);
$event->add_querylet(new Querylet(
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)",
["ns_user_id" => $duser->id]
));
} elseif (preg_match("/^upvoted_by_id[=|:](\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^upvoted_by_id[=|:](\d+)$/i")) {
$iid = int_escape($matches[1]);
$event->add_querylet(new Querylet(
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=1)",
["ns_user_id" => $iid]
));
} elseif (preg_match("/^downvoted_by_id[=|:](\d+)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^downvoted_by_id[=|:](\d+)$/i")) {
$iid = int_escape($matches[1]);
$event->add_querylet(new Querylet(
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)",
["ns_user_id" => $iid]
));
} elseif (preg_match("/^order[=|:](?:numeric_)?(score)(?:_(desc|asc))?$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^order[=|:](?:numeric_)?(score)(?:_(desc|asc))?$/i")) {
$default_order_for_column = "DESC";
$sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column;
$event->order = "images.numeric_score $sort";
Expand All @@ -366,7 +365,7 @@ public function onTagTermParse(TagTermParseEvent $event): void
{
$matches = [];

if (preg_match("/^vote[=|:](up|down|remove)$/", $event->term, $matches)) {
if ($matches = $event->matches("/^vote[=|:](up|down|remove)$/")) {
global $user;
$score = ($matches[1] == "up" ? 1 : ($matches[1] == "down" ? -1 : 0));
if ($user->can(Permissions::CREATE_VOTE)) {
Expand Down
9 changes: 4 additions & 5 deletions ext/pools/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,7 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
return;
}

$matches = [];
if (preg_match("/^pool[=|:]([0-9]+|any|none)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^pool[=|:]([0-9]+|any|none)$/i")) {
$poolID = $matches[1];

if (preg_match("/^(any|none)$/", $poolID)) {
Expand All @@ -505,7 +504,7 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
} else {
$event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM pool_images WHERE pool_id = $poolID)"));
}
} elseif (preg_match("/^pool_by_name[=|:](.*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^pool_by_name[=|:](.*)$/i")) {
$poolTitle = str_replace("_", " ", $matches[1]);

$pool = $this->get_single_pool_from_title($poolTitle);
Expand All @@ -514,7 +513,7 @@ public function onSearchTermParse(SearchTermParseEvent $event): void
$poolID = $pool->id;
}
$event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM pool_images WHERE pool_id = $poolID)"));
} elseif (preg_match("/^pool_id[=|:](.*)$/i", $event->term, $matches)) {
} elseif ($matches = $event->matches("/^pool_id[=|:](.*)$/i")) {
$poolID = str_replace("_", " ", $matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM pool_images WHERE pool_id = $poolID)"));
}
Expand All @@ -531,7 +530,7 @@ public function onTagTermCheck(TagTermCheckEvent $event): void
public function onTagTermParse(TagTermParseEvent $event): void
{
$matches = [];
if (preg_match("/^pool[=|:]([^:]*|lastcreated):?([0-9]*)$/i", $event->term, $matches)) {
if ($matches = $event->matches("/^pool[=|:]([^:]*|lastcreated):?([0-9]*)$/i")) {
global $user;
$poolTag = (string) str_replace("_", " ", $matches[1]);

Expand Down
Loading

0 comments on commit f8df890

Please sign in to comment.