VYPR
Medium severity6.5NVD Advisory· Published Oct 10, 2025· Updated Apr 15, 2026

CVE-2025-60868

CVE-2025-60868

Description

The Alt Redirect 1.6.3 addon for Statamic fails to consistently strip query string parameters when the "Query String Strip" feature is enabled. Case variations, encoded keys, and duplicates are not removed, allowing attackers to bypass sanitization. This may lead to cache poisoning, parameter pollution, or denial of service.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
alt-design/alt-redirectPackagist
< 1.6.41.6.4

Affected products

1

Patches

1
5dc6753c7151

Fix - CVE-2025-60868

2 files changed · +31 46
  • src/Helpers/URISupport.php+4 39 modified
    @@ -4,61 +4,26 @@
     
     namespace AltDesign\AltRedirect\Helpers;
     
    -use Illuminate\Support\Arr;
     use Illuminate\Support\Str;
     
     class URISupport
     {
         /**
    -     * Returns the current URI with named Query Strings filtered out .
    +     * Returns the current URI without any query strings for redirect matching.
          *
          * @return string $uri
          */
         public static function uriWithFilteredQueryStrings() : string
         {
             $request = request();
    -        $data = new Data('query-strings');
    -        $withoutQueryStrings = Arr::pluck($data->all(), 'query_string');
    -        // Filter out unwanted params, then strip the base url to get a filtered uri
    -        $encoding = str_contains($request->getRequestURI(), '+')
    -            ? PHP_QUERY_RFC1738
    -            : PHP_QUERY_RFC3986;
     
    +        // Return just the path without any query parameters
    +        // Query parameters will be handled separately in redirectWithPreservedParams
             return Str::replace(
                 $request->root(),
                 '',
    -            self::fullUrlWithoutQuery($withoutQueryStrings, $encoding)
    +            $request->url()
             );
         }
     
    -    /**
    -     * Replacement for fullUrlWithoutQuery() in Request to use custom Arr::query() implementation
    -     *
    -     * @param array $keys
    -     * @param int $encoding_type (optional) Encoding Type
    -     * @return string
    -     */
    -    private static function fullUrlWithoutQuery(array $keys, $encoding_type = PHP_QUERY_RFC1738) : string
    -    {
    -        $request = request();
    -        $query = Arr::except($request->query(), $keys);
    -
    -        $question = $request->getBaseUrl().$request->getPathInfo() === '/' ? '/?' : '?';
    -
    -        return count($query) > 0
    -            ? $request->url().$question.self::myArrQuery($query, $encoding_type)
    -            : $request->url();
    -    }
    -
    -    /**
    -     * Replacement for Arr::query() to use default encoding method
    -     *
    -     * @param array $array
    -     * @param int $encoding_type (optional) Encoding Type
    -     * @return string
    -     */
    -    private static function myArrQuery(array $array, $encoding_type = PHP_QUERY_RFC1738) : string
    -    {
    -        return http_build_query($array, '', '&', $encoding_type);
    -    }
     }
    
  • src/Http/Middleware/CheckForRedirects.php+27 7 modified
    @@ -65,19 +65,39 @@ public function handle(Request $request, Closure $next, string ...$guards): Resp
     
         private function redirectWithPreservedParams($to, $status)
         {
    -        $preserveKeys = [];
    +        $stripKeys = [];
             foreach ((new Data('query-strings'))->all() as $item) {
    -            if (!($item['strip'] ?? false)) {
    -                $preserveKeys[] = $item['query_string'];
    +            if ($item['strip'] ?? false) {
    +                $stripKeys[] = strtolower($item['query_string']);
                 }
             }
     
    +        // Parse raw query string to handle double-encoding and duplicates
    +        $rawQueryString = request()->getQueryString();
             $filteredStrings = [];
    -        foreach(request()->all() as $key => $value) {
    -            if (!in_array($key, $preserveKeys)) {
    -                continue;
    +        $seenKeys = [];
    +
    +        if ($rawQueryString) {
    +            // Decode the query string recursively to handle multiple levels of encoding
    +            $decodedQueryString = $rawQueryString;
    +            $previousQueryString = '';
    +
    +            // Keep decoding until no more changes occur (handles double/triple encoding)
    +            while ($decodedQueryString !== $previousQueryString) {
    +                $previousQueryString = $decodedQueryString;
    +                $decodedQueryString = urldecode($decodedQueryString);
    +            }
    +
    +            parse_str($decodedQueryString, $parsedParams);
    +
    +            foreach($parsedParams as $key => $value) {
    +                $normalizedKey = strtolower($key);
    +                // Strip only parameters marked with strip:true, preserve all others
    +                if (!in_array($normalizedKey, $stripKeys) && !isset($seenKeys[$normalizedKey])) {
    +                    $seenKeys[$normalizedKey] = true;
    +                    $filteredStrings[] = sprintf("%s=%s", urlencode($key), urlencode($value));
    +                }
                 }
    -            $filteredStrings[] = sprintf( "%s=%s", $key, $value);
             }
     
             if ($filteredStrings) {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.