From 52fa978d33d22e778530ac09214e4314e86433ab Mon Sep 17 00:00:00 2001 From: bneradt Date: Wed, 6 May 2026 14:57:52 -0500 Subject: [PATCH] Limit regex remap substitutions Regex remap targets could repeat valid substitution markers enough times to exceed the fixed substitution arrays, even when every marker referred to an allowed capture group. This rejects targets with more substitution markers than the parser can store and covers the boundary with remap parser unit tests. --- src/proxy/http/remap/RemapConfig.cc | 5 +++ .../http/remap/unit-tests/test_RemapRules.cc | 44 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/proxy/http/remap/RemapConfig.cc b/src/proxy/http/remap/RemapConfig.cc index bb7bc915f5e..bac5253a675 100644 --- a/src/proxy/http/remap/RemapConfig.cc +++ b/src/proxy/http/remap/RemapConfig.cc @@ -1047,6 +1047,11 @@ process_regex_mapping_config(const char *from_host_lower, url_mapping *new_mappi Warning("Substitution id [%c] has no corresponding capture pattern in regex [%s]", to_host[i + 1], from_host_lower); goto lFail; } + if (reg_map->n_substitutions >= UrlRewrite::MAX_REGEX_SUBS) { + Warning("too many substitution markers in regex remap target [%.*s], saw %d markers, max %d", to_host_len, to_host.data(), + reg_map->n_substitutions + 1, UrlRewrite::MAX_REGEX_SUBS); + goto lFail; + } reg_map->substitution_markers[reg_map->n_substitutions] = i; reg_map->substitution_ids[reg_map->n_substitutions] = substitution_id; ++reg_map->n_substitutions; diff --git a/src/proxy/http/remap/unit-tests/test_RemapRules.cc b/src/proxy/http/remap/unit-tests/test_RemapRules.cc index 012e8b55bd7..f51e39d0dd6 100644 --- a/src/proxy/http/remap/unit-tests/test_RemapRules.cc +++ b/src/proxy/http/remap/unit-tests/test_RemapRules.cc @@ -37,6 +37,7 @@ #include "tscore/BaseLogFile.h" #include "tsutil/PostScript.h" +#include #include #include @@ -122,6 +123,49 @@ SCENARIO("Parsing ACL named filters", "[proxy][remap]") } } +std::string +make_regex_remap_with_substitutions(int n_substitutions) +{ + std::string substitutions; + + substitutions.reserve(n_substitutions * 2); + for (int i = 0; i < n_substitutions; ++i) { + substitutions += "$0"; + } + + return "regex_map http://([^.]+)\\.example\\.com/ http://" + substitutions + ".origin.example.com/\n"; +} + +SCENARIO("Parsing regex remap substitutions", "[proxy][remap]") +{ + GIVEN("A regex remap target with the maximum number of substitution markers") + { + std::unique_ptr urlrw = std::make_unique(); + + auto cpath = write_test_remap(make_regex_remap_with_substitutions(UrlRewrite::MAX_REGEX_SUBS), "max-regex-substitutions"); + ts::PostScript file_cleanup([&]() -> void { std::filesystem::remove(cpath.c_str()); }); + + THEN("the remap parse succeeds") + { + REQUIRE(urlrw->BuildTable(cpath.c_str()) == TS_SUCCESS); + } + } + + GIVEN("A regex remap target with too many substitution markers") + { + std::unique_ptr urlrw = std::make_unique(); + + auto cpath = + write_test_remap(make_regex_remap_with_substitutions(UrlRewrite::MAX_REGEX_SUBS + 1), "too-many-regex-substitutions"); + ts::PostScript file_cleanup([&]() -> void { std::filesystem::remove(cpath.c_str()); }); + + THEN("the remap parse fails") + { + REQUIRE(urlrw->BuildTable(cpath.c_str()) != TS_SUCCESS); + } + } +} + struct EasyURL { URL url; HdrHeap *heap;