diff --git a/src/DDTrace/Integrations/CakePHP/CakePHPIntegration.php b/src/DDTrace/Integrations/CakePHP/CakePHPIntegration.php index 9adf7691128..2d416f19ea5 100644 --- a/src/DDTrace/Integrations/CakePHP/CakePHPIntegration.php +++ b/src/DDTrace/Integrations/CakePHP/CakePHPIntegration.php @@ -71,7 +71,12 @@ public static function init(): int $rootSpan = \DDTrace\root_span(); if ($rootSpan !== null) { - $rootSpan->meta[Tag::HTTP_ROUTE] = $app->template; + $template = $app->template; + $rootSpan->meta[Tag::HTTP_ROUTE] = $template; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromCakePHP($template); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } } }; diff --git a/src/DDTrace/Integrations/CodeIgniter/V2/CodeIgniterIntegration.php b/src/DDTrace/Integrations/CodeIgniter/V2/CodeIgniterIntegration.php index 15c8f684f67..8b1b4ad6c02 100644 --- a/src/DDTrace/Integrations/CodeIgniter/V2/CodeIgniterIntegration.php +++ b/src/DDTrace/Integrations/CodeIgniter/V2/CodeIgniterIntegration.php @@ -230,6 +230,10 @@ private static function setHttpRoute($router, $rootSpan) { if (isset($router->routes[$uri])) { $rootSpan->meta[Tag::HTTP_ROUTE] = $uri; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromCodeIgniter($uri); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } return; } @@ -244,6 +248,10 @@ private static function setHttpRoute($router, $rootSpan) { if (preg_match('#^'.$key.'$#', $uri)) { $rootSpan->meta[Tag::HTTP_ROUTE] = $origKey; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromCodeIgniter($origKey); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } return; } } @@ -251,5 +259,9 @@ private static function setHttpRoute($router, $rootSpan) { // If we got this far it means we didn't encounter a // matching route so we'll set the site default route $rootSpan->meta[Tag::HTTP_ROUTE] = $uri; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromCodeIgniter($uri); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } } } diff --git a/src/DDTrace/Integrations/Laminas/LaminasIntegration.php b/src/DDTrace/Integrations/Laminas/LaminasIntegration.php index 0d55f3d22a9..273903cfb47 100644 --- a/src/DDTrace/Integrations/Laminas/LaminasIntegration.php +++ b/src/DDTrace/Integrations/Laminas/LaminasIntegration.php @@ -284,6 +284,11 @@ static function (SpanData $span) use ($controller, $action) { $httpRoute = LaminasIntegration::httpRouteTemplateFromNamedRouteStack($this, (string) $routeName); if ($httpRoute !== null && $httpRoute !== '') { $rootSpan->meta[Tag::HTTP_ROUTE] = $httpRoute; + $allParams = method_exists($routeMatch, 'getParams') ? ($routeMatch->getParams() ?? []) : []; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromLaminas($httpRoute, $allParams); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } } } diff --git a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php index 0f00a02b825..5862792bd73 100644 --- a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php +++ b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php @@ -140,7 +140,13 @@ static function ($This, $scope, $args, $route) { $rootSpan->meta[Tag::HTTP_URL] = \DDTrace\Util\Normalizer::urlSanitize($request->fullUrl()); } if (\method_exists($route, 'uri')) { - $rootSpan->meta[Tag::HTTP_ROUTE] = $route->uri(); + $httpRoute = $route->uri(); + $rootSpan->meta[Tag::HTTP_ROUTE] = $httpRoute; + $matchedParams = \method_exists($route, 'parameters') ? ($route->parameters() ?? []) : []; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromLaravel($httpRoute, $matchedParams); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } } if (\method_exists($route, 'parameters') && function_exists('\datadog\appsec\push_addresses')) { $parameters = $route->parameters(); diff --git a/src/DDTrace/Integrations/Slim/SlimIntegration.php b/src/DDTrace/Integrations/Slim/SlimIntegration.php index d135e66d810..8c1725dec06 100644 --- a/src/DDTrace/Integrations/Slim/SlimIntegration.php +++ b/src/DDTrace/Integrations/Slim/SlimIntegration.php @@ -75,11 +75,16 @@ static function ($errorMiddleware, $self, $args) use ($rootSpan, $integration) { null, static function ($router, $scope, $args, $return) use ($rootSpan) { /** @var \Slim\Interfaces\RouteInterface $return */ - $rootSpan->meta[Tag::HTTP_ROUTE] = $return->getPattern(); + $pattern = $return->getPattern(); + $rootSpan->meta[Tag::HTTP_ROUTE] = $pattern; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromSlim($pattern); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } if (dd_trace_env_config("DD_HTTP_SERVER_ROUTE_BASED_NAMING")) { $rootSpan->resource = - $_SERVER['REQUEST_METHOD'] . ' ' . ($return->getName() ?: $return->getPattern()); + $_SERVER['REQUEST_METHOD'] . ' ' . ($return->getName() ?: $pattern); } } ); @@ -92,7 +97,13 @@ static function ($router, $scope, $args, $return) use ($rootSpan) { static function ($router, $scope, $args, $return) use ($rootSpan) { /** @var \Slim\Interfaces\RouteInterface $route */ $route = $return; - $rootSpan->meta[Tag::HTTP_ROUTE] = $route->getPattern(); + $pattern = $route->getPattern(); + $rootSpan->meta[Tag::HTTP_ROUTE] = $pattern; + // Normalized route will be refined in traceControllers once matched params are available + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromSlim($pattern); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } } ); } @@ -131,6 +142,15 @@ static function ($router, $scope, $args, $return) use ($rootSpan) { $span->meta['slim.route.name'] = $routeName; $rootSpan->meta['slim.route.name'] = $routeName; } + // Refine normalized route now that matched params are available + $matchedParams = method_exists($route, 'getArguments') ? ($route->getArguments() ?? []) : []; + $pattern = isset($rootSpan->meta[Tag::HTTP_ROUTE]) ? $rootSpan->meta[Tag::HTTP_ROUTE] : ''; + if ($pattern !== '') { + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromSlim($pattern, $matchedParams); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } + } } } else { $rootSpan->meta['slim.route.controller'] = $callableName; diff --git a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php index d0540860fa5..a77f59d2998 100644 --- a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php +++ b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php @@ -456,6 +456,10 @@ static function() { if ($path !== null) { $rootSpan->meta[Tag::HTTP_ROUTE] = $path; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromSymfony($path); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } } }; } else { diff --git a/src/DDTrace/Integrations/WordPress/WordPressIntegrationLoader.php b/src/DDTrace/Integrations/WordPress/WordPressIntegrationLoader.php index 4a9cd2a5178..cf10e8ce85b 100644 --- a/src/DDTrace/Integrations/WordPress/WordPressIntegrationLoader.php +++ b/src/DDTrace/Integrations/WordPress/WordPressIntegrationLoader.php @@ -732,7 +732,12 @@ static function (HookData $hook) use ( function_exists('is_404') && is_404() === false) { $rootSpan = \DDTrace\root_span(); if (\property_exists($This, 'matched_rule')) { - $rootSpan->meta[Tag::HTTP_ROUTE] = $This->matched_rule; + $matchedRule = $This->matched_rule; + $rootSpan->meta[Tag::HTTP_ROUTE] = $matchedRule; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromWordPress($matchedRule); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } } } }); diff --git a/src/DDTrace/Integrations/Yii/YiiIntegration.php b/src/DDTrace/Integrations/Yii/YiiIntegration.php index 91fa862d5f9..8acc12e9047 100644 --- a/src/DDTrace/Integrations/Yii/YiiIntegration.php +++ b/src/DDTrace/Integrations/Yii/YiiIntegration.php @@ -156,6 +156,10 @@ function (SpanData $span, $args) use (&$firstController) { $rootSpan->meta['app.route.path'] = $routePath; $rootSpan->meta[Tag::HTTP_ROUTE] = $routePath; + $normalizedRoute = \DDTrace\Util\RouteNormalizer::normalizeFromYii($routePath); + if ($normalizedRoute !== null) { + $rootSpan->meta[Tag::APPSEC_NORMALIZED_ROUTE] = $normalizedRoute; + } if (dd_trace_env_config("DD_HTTP_SERVER_ROUTE_BASED_NAMING")) { $resourceName = \str_replace( diff --git a/src/DDTrace/Util/RouteNormalizer.php b/src/DDTrace/Util/RouteNormalizer.php new file mode 100644 index 00000000000..a613ac0ecae --- /dev/null +++ b/src/DDTrace/Util/RouteNormalizer.php @@ -0,0 +1,518 @@ +uri(), e.g. "/users/{id}/{format?}" + * @param array $matchedParams Parameters from $route->parameters(); used to resolve optionals + * @return string|null Normalized route, or null on parse failure + */ + public static function normalizeFromLaravel(string $routeUri, array $matchedParams = []): ?string + { + return self::normalizeBraceRoute($routeUri, $matchedParams); + } + + /** + * Normalize a Slim route pattern. + * + * Slim uses {param} or {param:regex} for required parameters. Slim 4 supports optional + * segments in square brackets: /users/{id}[/{format}]. + * + * @param string $pattern Pattern from $route->getPattern(), e.g. "/users/{id:[0-9]+}" + * @param array $matchedParams Matched params from $route->getArguments(); resolves optionals + * @return string|null + */ + public static function normalizeFromSlim(string $pattern, array $matchedParams = []): ?string + { + return self::normalizeBraceRoute($pattern, $matchedParams, true); + } + + /** + * Normalize a Symfony route path. + * + * The path produced by EndpointCatalog::pathForRoute() already uses {param} notation. + * + * @param string $path Path template, e.g. "/users/{id}" + * @return string|null + */ + public static function normalizeFromSymfony(string $path): ?string + { + return self::normalizeBraceRoute($path, []); + } + + /** + * Normalize a Laminas route template. + * + * Laminas uses :param for dynamic parameters and [...] for optional sections. + * The Wildcard route type produces "/*" which is treated as a catch-all. + * Example: "/users/:id[.:format]" + * + * @param string $template Template from httpRouteTemplateFromMatchedRoute() + * @param array $matchedParams Matched params from $routeMatch->getParams() + * @return string|null + */ + public static function normalizeFromLaminas(string $template, array $matchedParams = []): ?string + { + $expanded = self::expandBracketOptionals($template, $matchedParams, ':'); + // Convert wildcard segments ('*') to a {param} placeholder before brace conversion + $expanded = preg_replace('#/\*$#', '/{param1}', $expanded); + $braceFormat = self::colonParamsToBraces($expanded); + return self::normalizeBraceRoute($braceFormat, $matchedParams); + } + + /** + * Normalize a CakePHP route template. + * + * CakePHP uses :param syntax and * / ** for catch-all segments. + * + * @param string $template Template from $app->template, e.g. "/articles/:id.:ext" + * @return string|null + */ + public static function normalizeFromCakePHP(string $template): ?string + { + $braceFormat = self::cakephpToBraces($template); + return self::normalizeBraceRoute($braceFormat, []); + } + + /** + * Normalize a Yii route path containing :param placeholders. + * + * The Yii integration builds a URL via Url::toRoute() with ":paramName" as + * placeholder values, producing a path like "/articles/:id". + * + * @param string $routePath Path from Url::toRoute() with colon placeholders + * @return string|null + */ + public static function normalizeFromYii(string $routePath): ?string + { + $braceFormat = self::colonParamsToBraces($routePath); + return self::normalizeBraceRoute($braceFormat, []); + } + + /** + * Normalize a CodeIgniter V2 route pattern. + * + * CodeIgniter uses :any / :num wildcards and positional regex groups. + * Named parameters are not available, so placeholders param1, param2, … are used. + * + * @param string $route Route key from $router->routes, e.g. "blog/(:num)" + * @return string|null + */ + public static function normalizeFromCodeIgniter(string $route): ?string + { + $route = trim($route, '/'); + if ($route === '') { + return '/'; + } + + $segments = explode('/', $route); + $normalizedSegments = []; + $paramIndex = 1; + + foreach ($segments as $segment) { + if ($segment === '') { + continue; + } + + $lower = strtolower($segment); + if ( + $lower === ':any' || $lower === ':num' || + $lower === '(:any)' || $lower === '(:num)' + ) { + $normalizedSegments[] = '{param' . $paramIndex++ . '}'; + } elseif (preg_match('/[()[\].*+?|^$\\\\]/', $segment) || strpos($segment, ':') !== false) { + $normalizedSegments[] = '{param' . $paramIndex++ . '}'; + } else { + $normalizedSegments[] = self::encodeStaticSegment($segment); + } + } + + return '/' . implode('/', $normalizedSegments); + } + + /** + * Normalize a WordPress matched_rule (regex). + * + * WordPress route matching uses regex rules like "^blog/([^/]+)/?$". + * Named parameters are not available; placeholders param1, param2, … are used. + * + * @param string $matchedRule Value of $wp->matched_rule + * @return string|null + */ + public static function normalizeFromWordPress(string $matchedRule): ?string + { + $rule = $matchedRule; + + // Strip regex anchors + $rule = ltrim($rule, '^'); + $rule = rtrim($rule, '$'); + + // Strip optional trailing slash pattern "\/?" or "/?" + if (preg_match('#\\\\?/\?$#', $rule, $m)) { + $rule = substr($rule, 0, -strlen($m[0])); + } + + $rule = trim($rule, '/'); + if ($rule === '') { + return '/'; + } + + // Use bracket-aware splitting so that [^/] character classes are not split + $segments = self::splitRegexBySlash($rule); + $normalizedSegments = []; + $paramIndex = 1; + + foreach ($segments as $segment) { + if ($segment === '') { + continue; + } + + if (preg_match('/^\([^)]+\)$/', $segment)) { + // Whole segment is a single capture group + $normalizedSegments[] = '{param' . $paramIndex++ . '}'; + } elseif (preg_match('/[()[\].*+?|^${}\\\\]/', $segment)) { + // Contains regex metacharacters → treat as dynamic + $normalizedSegments[] = '{param' . $paramIndex++ . '}'; + } else { + $normalizedSegments[] = self::encodeStaticSegment($segment); + } + } + + if (empty($normalizedSegments)) { + return '/{param1}'; + } + + return '/' . implode('/', $normalizedSegments); + } + + /** + * Split a regex string by '/' but do not split inside character classes [...]. + * This prevents [^/] from being broken into two segments. + */ + private static function splitRegexBySlash(string $str): array + { + $segments = []; + $current = ''; + $len = strlen($str); + $bracketDepth = 0; + + for ($i = 0; $i < $len; $i++) { + $c = $str[$i]; + + if ($c === '\\' && $i + 1 < $len) { + $current .= $c . $str[$i + 1]; + $i++; + continue; + } + + if ($c === '[') { + $bracketDepth++; + $current .= $c; + } elseif ($c === ']' && $bracketDepth > 0) { + $bracketDepth--; + $current .= $c; + } elseif ($c === '/' && $bracketDepth === 0) { + $segments[] = $current; + $current = ''; + } else { + $current .= $c; + } + } + + $segments[] = $current; + return $segments; + } + + // ------------------------------------------------------------------------- + // Core brace-format normalizer + // ------------------------------------------------------------------------- + + /** + * Normalize a route that uses {param} notation. + * + * Handles: + * {param} - required parameter + * {param?} - optional parameter (resolved via $matchedParams) + * {param:regex} - regex-constrained parameter (constraint stripped) + * [{param}] - optional segment (Slim-style; only when $expandSquare=true) + * Static text mixed with params in a segment → single combined element + * + * @param bool $expandSquare When true, expand Slim-style [...] optional sections + */ + private static function normalizeBraceRoute( + string $route, + array $matchedParams, + bool $expandSquare = false + ): ?string { + $route = trim($route); + if ($route === '' || $route === '/') { + return '/'; + } + + $trailingSlash = (strlen($route) > 1 && $route[-1] === '/') ? '/' : ''; + $route = rtrim($route, '/'); + + if ($route[0] !== '/') { + $route = '/' . $route; + } + + if ($expandSquare) { + $route = self::expandSquareBracketOptionals($route, $matchedParams); + } + + $raw = ltrim($route, '/'); + $parts = explode('/', $raw); + $normalizedSegments = []; + + foreach ($parts as $segment) { + if ($segment === '') { + continue; + } + + $result = self::normalizeBraceSegment($segment, $matchedParams); + if ($result === null) { + // Optional segment whose parameter is absent — skip + continue; + } + + $normalizedSegments[] = $result; + } + + return '/' . implode('/', $normalizedSegments) . $trailingSlash; + } + + /** + * Normalize a single URL segment that may contain {param} placeholders. + * + * @return string|null The normalized element, or null if the segment is optional and absent + */ + private static function normalizeBraceSegment(string $segment, array $matchedParams): ?string + { + preg_match_all('/\{([^}]+)\}/', $segment, $matches, PREG_SET_ORDER); + + if (empty($matches)) { + return self::encodeStaticSegment($segment); + } + + $paramNames = []; + foreach ($matches as $match) { + $raw = $match[1]; + + $isOptional = ($raw[-1] === '?'); + if ($isOptional) { + $raw = substr($raw, 0, -1); + } + + // Strip regex constraint: {param:regex} → param + $colon = strpos($raw, ':'); + if ($colon !== false) { + $raw = substr($raw, 0, $colon); + } + + $name = trim($raw); + + if ($isOptional && !array_key_exists($name, $matchedParams)) { + return null; + } + + $paramNames[] = self::encodeParamName($name); + } + + if (count($paramNames) === 1) { + return '{' . $paramNames[0] . '}'; + } + + // Multiple params in the same segment → combine with the '+' marker + return '{' . implode('+', $paramNames) . '}'; + } + + // ------------------------------------------------------------------------- + // Optional-section expanders + // ------------------------------------------------------------------------- + + /** + * Expand Slim-style optional sections [...] in a route based on matched params. + * Example: "/users/{id}[/{format}]" with format present → "/users/{id}/{format}" + */ + private static function expandSquareBracketOptionals(string $route, array $matchedParams): string + { + // Expand from innermost to outermost by looping until stable + $prev = null; + while ($prev !== $route) { + $prev = $route; + $route = preg_replace_callback( + '/\[([^\[\]]*)\]/', + function ($m) use ($matchedParams) { + $inner = $m[1]; + preg_match_all('/\{([^}?:]+)[?:]?[^}]*\}/', $inner, $pm); + $innerParams = $pm[1]; + + if (empty($innerParams)) { + // Purely static optional section — include it (best effort) + return $inner; + } + + foreach ($innerParams as $param) { + if (array_key_exists($param, $matchedParams)) { + return $inner; + } + } + + return ''; + }, + $route + ); + } + return $route; + } + + /** + * Expand Laminas [...] optional sections based on matched params. + * Example: "/:id[.:format]" with format present → "/:id.:format" + */ + private static function expandBracketOptionals( + string $template, + array $matchedParams, + string $paramPrefix = ':' + ): string { + $prev = null; + while ($prev !== $template) { + $prev = $template; + $template = preg_replace_callback( + '/\[([^\[\]]*)\]/', + function ($m) use ($matchedParams, $paramPrefix) { + $inner = $m[1]; + $pattern = '/' . preg_quote($paramPrefix, '/') . '([a-zA-Z_][a-zA-Z0-9_]*)/'; + preg_match_all($pattern, $inner, $pm); + $innerParams = $pm[1]; + + foreach ($innerParams as $param) { + if (array_key_exists($param, $matchedParams)) { + return $inner; + } + } + + return ''; + }, + $template + ); + } + return $template; + } + + // ------------------------------------------------------------------------- + // Framework-specific syntax converters + // ------------------------------------------------------------------------- + + /** + * Convert ":paramName" colon-prefix notation to "{paramName}" brace notation. + * Laminas segment constraints like ":param{constraint}" are also handled. + */ + private static function colonParamsToBraces(string $template): string + { + return preg_replace_callback( + '/:([a-zA-Z_][a-zA-Z0-9_]*)(?:\{[^}]*\})?/', + static function ($m) { + return '{' . $m[1] . '}'; + }, + $template + ); + } + + /** + * Convert CakePHP route template syntax to brace notation. + * Handles :param and * / ** catch-all segments. + */ + private static function cakephpToBraces(string $template): string + { + // Replace /** and /* with a {catchall} placeholder + $result = preg_replace('#/\*\*#', '/{catchall}', $template); + $result = preg_replace('#/\*(?!\*)#', '/{catchall}', $result); + + // Replace remaining bare * (e.g. at start or standalone segment) + $result = preg_replace('#(?= 'A' && $c <= 'Z') || ($c >= 'a' && $c <= 'z') || + ($c >= '0' && $c <= '9') || + $c === '.' || $c === '-' || $c === '~' || $c === '_' + ) { + $result .= $c; + } elseif ( + $c === '%' && + $i + 2 < $len && + ctype_xdigit($segment[$i + 1]) && + ctype_xdigit($segment[$i + 2]) + ) { + $result .= '%' . strtoupper($segment[$i + 1]) . strtoupper($segment[$i + 2]); + $i += 2; + } else { + $result .= rawurlencode($c); + } + } + return $result; + } + + /** + * URL-encode reserved characters in a parameter name. + * Reserved: /?#+{} — these must not appear literally. + * The '+' sign is the combining marker and must be encoded if it appears in a + * framework-supplied name. + */ + public static function encodeParamName(string $name): string + { + $reserved = '/?#+{}'; + $result = ''; + $len = strlen($name); + for ($i = 0; $i < $len; $i++) { + $c = $name[$i]; + if (strpos($reserved, $c) !== false) { + $result .= rawurlencode($c); + } else { + $result .= $c; + } + } + return $result; + } +} diff --git a/src/api/Tag.php b/src/api/Tag.php index f2cb6b7c1e4..ca264c04db6 100644 --- a/src/api/Tag.php +++ b/src/api/Tag.php @@ -26,6 +26,7 @@ class Tag const ERROR_STACK = 'error.stack'; // human readable version of the stack const HTTP_METHOD = 'http.method'; const HTTP_ROUTE = 'http.route'; + const APPSEC_NORMALIZED_ROUTE = '_dd.appsec.normalized_route'; const HTTP_STATUS_CODE = 'http.status_code'; const HTTP_URL = 'http.url'; const HTTP_VERSION = 'http.version'; diff --git a/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php b/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php index f1e85015734..953d3705cb9 100644 --- a/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php +++ b/tests/Integrations/CakePHP/V4_5/CommonScenariosTest.php @@ -61,6 +61,7 @@ public function provideSpecs() 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'cakephp', + Tag::APPSEC_NORMALIZED_ROUTE => '/{controller}', ])->withChildren([ SpanAssertion::build( 'Controller.invokeAction', @@ -87,6 +88,7 @@ public function provideSpecs() 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'cakephp', + Tag::APPSEC_NORMALIZED_ROUTE => '/{controller}', ])->withChildren([ SpanAssertion::build( 'Controller.invokeAction', @@ -123,6 +125,7 @@ public function provideSpecs() 'http.route' => '/{controller}', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'cakephp', + Tag::APPSEC_NORMALIZED_ROUTE => '/{controller}', ])->withExistingTagsNames([ 'error.stack' ])->setError( @@ -165,6 +168,7 @@ public function provideSpecs() 'http.route' => '/parameterized/{param}', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'cakephp', + Tag::APPSEC_NORMALIZED_ROUTE => '/parameterized/{param}', ])->withChildren([ SpanAssertion::build( 'Controller.invokeAction', diff --git a/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php b/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php index 69e022c6fc8..900e78630b5 100644 --- a/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php +++ b/tests/Integrations/CodeIgniter/V2_2/CommonScenariosTest.php @@ -56,6 +56,7 @@ public function provideSpecs() Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'codeigniter', Tag::HTTP_ROUTE => 'simple', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple', ])->withChildren([ SpanAssertion::build( 'Simple.index', @@ -81,6 +82,7 @@ public function provideSpecs() Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'codeigniter', Tag::HTTP_ROUTE => 'simple_view', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple_view', ])->withChildren([ SpanAssertion::build( 'Simple_View.index', @@ -115,7 +117,8 @@ public function provideSpecs() 'app.endpoint' => 'Error_::index', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'codeigniter', - Tag::HTTP_ROUTE => 'error' + Tag::HTTP_ROUTE => 'error', + Tag::APPSEC_NORMALIZED_ROUTE => '/error', ]) ->setError("Exception", "Uncaught Exception: datadog in %s:%d") ->withExistingTagsNames(['error.stack']) @@ -144,6 +147,7 @@ public function provideSpecs() Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'codeigniter', Tag::HTTP_ROUTE => 'parameterized/(:any)', + Tag::APPSEC_NORMALIZED_ROUTE => '/parameterized/{param1}', ])->withChildren([ SpanAssertion::build( 'Parameterized.customAction', diff --git a/tests/Integrations/Slim/Latest/CommonScenariosTest.php b/tests/Integrations/Slim/Latest/CommonScenariosTest.php index e801ed8b350..2ab60ff6f8d 100644 --- a/tests/Integrations/Slim/Latest/CommonScenariosTest.php +++ b/tests/Integrations/Slim/Latest/CommonScenariosTest.php @@ -122,6 +122,7 @@ public function provideSpecs() Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', Tag::HTTP_ROUTE => '/simple', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple', ])->withChildren([ $this->wrapMiddleware([ SpanAssertion::build( @@ -150,6 +151,7 @@ public function provideSpecs() Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', Tag::HTTP_ROUTE => '/simple_view', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple_view', ])->withChildren([ $this->wrapMiddleware([ SpanAssertion::build( @@ -187,6 +189,7 @@ public function provideSpecs() Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', Tag::HTTP_ROUTE => '/error', + Tag::APPSEC_NORMALIZED_ROUTE => '/error', ]) ->setError(null, null) ->withChildren([ @@ -221,6 +224,7 @@ public function provideSpecs() Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'slim', Tag::HTTP_ROUTE => '/parameterized/{value}', + Tag::APPSEC_NORMALIZED_ROUTE => '/parameterized/{value}', ])->withChildren([ $this->wrapMiddleware([ SpanAssertion::build( diff --git a/tests/Integrations/Symfony/Latest/CommonScenariosTest.php b/tests/Integrations/Symfony/Latest/CommonScenariosTest.php index f7a1571c7af..0a301317752 100644 --- a/tests/Integrations/Symfony/Latest/CommonScenariosTest.php +++ b/tests/Integrations/Symfony/Latest/CommonScenariosTest.php @@ -74,6 +74,7 @@ public function provideSpecs() 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple', ])->withChildren([ SpanAssertion::exists('symfony.httpkernel.kernel.handle') ->withChildren([ @@ -113,6 +114,7 @@ public function provideSpecs() 'http.status_code' => '200', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple_view', ])->withChildren([ SpanAssertion::exists('symfony.kernel.terminate'), SpanAssertion::exists('symfony.httpkernel.kernel.handle')->withChildren([ @@ -159,6 +161,7 @@ public function provideSpecs() 'http.status_code' => '500', Tag::SPAN_KIND => 'server', Tag::COMPONENT => 'symfony', + Tag::APPSEC_NORMALIZED_ROUTE => '/error', ]) ->setError('Exception', 'An exception occurred') ->withExistingTagsNames(['error.stack']) diff --git a/tests/Integrations/Yii/Latest/CommonScenariosTest.php b/tests/Integrations/Yii/Latest/CommonScenariosTest.php index 62410148e30..8a8509739aa 100644 --- a/tests/Integrations/Yii/Latest/CommonScenariosTest.php +++ b/tests/Integrations/Yii/Latest/CommonScenariosTest.php @@ -59,6 +59,7 @@ public function provideSpecs() 'app.endpoint' => 'app\controllers\SimpleController::actionIndex', 'app.route.path' => '/simple', Tag::HTTP_ROUTE => '/simple', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple', Tag::SPAN_KIND => "server", Tag::COMPONENT => "yii", ])->withChildren([ @@ -103,6 +104,7 @@ public function provideSpecs() 'app.endpoint' => 'app\controllers\SimpleController::actionView', 'app.route.path' => '/simple_view', Tag::HTTP_ROUTE => '/simple_view', + Tag::APPSEC_NORMALIZED_ROUTE => '/simple_view', Tag::SPAN_KIND => "server", Tag::COMPONENT => "yii", ])->withChildren([ @@ -150,6 +152,7 @@ public function provideSpecs() 'app.endpoint' => 'app\controllers\SimpleController::actionError', 'app.route.path' => '/error', Tag::HTTP_ROUTE => '/error', + Tag::APPSEC_NORMALIZED_ROUTE => '/error', Tag::SPAN_KIND => "server", Tag::COMPONENT => "yii", ]) @@ -222,6 +225,7 @@ public function provideSpecs() 'app.endpoint' => 'app\controllers\SimpleController::actionParameterized', 'app.route.path' => '/parameterized/:value', Tag::HTTP_ROUTE => '/parameterized/:value', + Tag::APPSEC_NORMALIZED_ROUTE => '/parameterized/{value}', Tag::SPAN_KIND => "server", Tag::COMPONENT => "yii", ])->withChildren([ diff --git a/tests/Unit/Util/Normalizer/RouteNormalizerTest.php b/tests/Unit/Util/Normalizer/RouteNormalizerTest.php new file mode 100644 index 00000000000..5b9c7785d35 --- /dev/null +++ b/tests/Unit/Util/Normalizer/RouteNormalizerTest.php @@ -0,0 +1,349 @@ +assertSame('hello', RouteNormalizer::encodeStaticSegment('hello')); + $this->assertSame('Hello-World_v1.0~test', RouteNormalizer::encodeStaticSegment('Hello-World_v1.0~test')); + } + + public function testEncodeStaticSegmentEncodesReserved() + { + $this->assertSame('dump-request', RouteNormalizer::encodeStaticSegment('dump-request')); + $this->assertSame('foo%40bar', RouteNormalizer::encodeStaticSegment('foo@bar')); + $this->assertSame('foo%20bar', RouteNormalizer::encodeStaticSegment('foo bar')); + } + + public function testEncodeStaticSegmentPreservesExistingPercentEncoding() + { + $this->assertSame('%2F', RouteNormalizer::encodeStaticSegment('%2F')); + $this->assertSame('%2F', RouteNormalizer::encodeStaticSegment('%2f')); + } + + // ------------------------------------------------------------------------- + // encodeParamName + // ------------------------------------------------------------------------- + + public function testEncodeParamNamePreservesNormal() + { + $this->assertSame('id', RouteNormalizer::encodeParamName('id')); + $this->assertSame('user_id', RouteNormalizer::encodeParamName('user_id')); + } + + public function testEncodeParamNameEncodesPlusSign() + { + $this->assertSame('foo%2Bbar', RouteNormalizer::encodeParamName('foo+bar')); + } + + public function testEncodeParamNameEncodesReserved() + { + $this->assertSame('foo%23bar', RouteNormalizer::encodeParamName('foo#bar')); + } + + // ------------------------------------------------------------------------- + // normalizeFromLaravel + // ------------------------------------------------------------------------- + + public function testLaravelSimpleRoute() + { + $this->assertSame('/users', RouteNormalizer::normalizeFromLaravel('/users')); + $this->assertSame('/users/{id}', RouteNormalizer::normalizeFromLaravel('/users/{id}')); + } + + public function testLaravelOptionalParamPresent() + { + $result = RouteNormalizer::normalizeFromLaravel('/users/{id}/{format?}', ['id' => '1', 'format' => 'json']); + $this->assertSame('/users/{id}/{format}', $result); + } + + public function testLaravelOptionalParamAbsent() + { + $result = RouteNormalizer::normalizeFromLaravel('/users/{id}/{format?}', ['id' => '1']); + $this->assertSame('/users/{id}', $result); + } + + public function testLaravelMixedSegmentTwoParams() + { + // /photos/{id}.{format} → both in same URL segment → combined + $result = RouteNormalizer::normalizeFromLaravel('/photos/{id}.{format}', ['id' => '1', 'format' => 'jpg']); + $this->assertSame('/photos/{id+format}', $result); + } + + public function testLaravelMixedSegmentOptionalFormat() + { + // /posts/:id(.:format) style — optional format present + $result = RouteNormalizer::normalizeFromLaravel('/posts/{id}/{format?}', ['id' => '1', 'format' => 'json']); + $this->assertSame('/posts/{id}/{format}', $result); + + // optional format absent + $result = RouteNormalizer::normalizeFromLaravel('/posts/{id}/{format?}', ['id' => '1']); + $this->assertSame('/posts/{id}', $result); + } + + public function testLaravelDeeperRoute() + { + $result = RouteNormalizer::normalizeFromLaravel('/dashboard/shared_widget_update/{id}/{widget_id}'); + $this->assertSame('/dashboard/shared_widget_update/{id}/{widget_id}', $result); + } + + public function testLaravelTrailingSlash() + { + $result = RouteNormalizer::normalizeFromLaravel('/users/{id}/'); + $this->assertSame('/users/{id}/', $result); + } + + public function testLaravelRoot() + { + $this->assertSame('/', RouteNormalizer::normalizeFromLaravel('/')); + } + + // ------------------------------------------------------------------------- + // normalizeFromSlim + // ------------------------------------------------------------------------- + + public function testSlimSimpleRoute() + { + $this->assertSame('/users/{id}', RouteNormalizer::normalizeFromSlim('/users/{id}')); + } + + public function testSlimRegexConstraintStripped() + { + $this->assertSame('/users/{id}', RouteNormalizer::normalizeFromSlim('/users/{id:[0-9]+}')); + $this->assertSame('/v2/{name}/blobs', RouteNormalizer::normalizeFromSlim('/v2/{name:[a-zA-Z0-9-]+}/blobs')); + } + + public function testSlimOptionalSegmentPresent() + { + $result = RouteNormalizer::normalizeFromSlim('/users/{id}[/{format}]', ['id' => '1', 'format' => 'json']); + $this->assertSame('/users/{id}/{format}', $result); + } + + public function testSlimOptionalSegmentAbsent() + { + $result = RouteNormalizer::normalizeFromSlim('/users/{id}[/{format}]', ['id' => '1']); + $this->assertSame('/users/{id}', $result); + } + + public function testSlimCatchAll() + { + $this->assertSame('/files/{file}', RouteNormalizer::normalizeFromSlim('/files/{file:.+}')); + } + + // ------------------------------------------------------------------------- + // normalizeFromSymfony + // ------------------------------------------------------------------------- + + public function testSymfonySimpleRoute() + { + $this->assertSame('/sleep/{seconds}', RouteNormalizer::normalizeFromSymfony('/sleep/{seconds}')); + } + + public function testSymfonyMixedSegment() + { + // Symfony may produce routes like /posts/{id}.{_format} + $result = RouteNormalizer::normalizeFromSymfony('/posts/{id}.{_format}'); + $this->assertSame('/posts/{id+_format}', $result); + } + + public function testSymfonyStaticOnlyRoute() + { + $this->assertSame('/dump-request', RouteNormalizer::normalizeFromSymfony('/dump-request')); + } + + // ------------------------------------------------------------------------- + // normalizeFromLaminas + // ------------------------------------------------------------------------- + + public function testLaminasSimpleColon() + { + $this->assertSame('/users/{id}', RouteNormalizer::normalizeFromLaminas('/users/:id')); + } + + public function testLaminasOptionalPresent() + { + $result = RouteNormalizer::normalizeFromLaminas('/users/:id[.:format]', ['id' => '1', 'format' => 'json']); + $this->assertSame('/users/{id+format}', $result); + } + + public function testLaminasOptionalAbsent() + { + $result = RouteNormalizer::normalizeFromLaminas('/users/:id[.:format]', ['id' => '1']); + $this->assertSame('/users/{id}', $result); + } + + public function testLaminasLiteralRoute() + { + $this->assertSame('/dump-request', RouteNormalizer::normalizeFromLaminas('/dump-request')); + } + + public function testLaminasWildcard() + { + // Wildcard routes produce '/*' from laminasSegmentPartsToRouteTemplate + $result = RouteNormalizer::normalizeFromLaminas('/*'); + $this->assertSame('/{param1}', $result); + } + + // ------------------------------------------------------------------------- + // normalizeFromCakePHP + // ------------------------------------------------------------------------- + + public function testCakePHPSimpleColon() + { + $this->assertSame('/articles/{id}', RouteNormalizer::normalizeFromCakePHP('/articles/:id')); + } + + public function testCakePHPMixedSegment() + { + $result = RouteNormalizer::normalizeFromCakePHP('/articles/:id.:ext'); + $this->assertSame('/articles/{id+ext}', $result); + } + + public function testCakePHPCatchAll() + { + $this->assertSame('/{catchall}', RouteNormalizer::normalizeFromCakePHP('/*')); + $this->assertSame('/api/{catchall}', RouteNormalizer::normalizeFromCakePHP('/api/**')); + } + + public function testCakePHPStaticRoute() + { + $this->assertSame('/admin/dashboard', RouteNormalizer::normalizeFromCakePHP('/admin/dashboard')); + } + + // ------------------------------------------------------------------------- + // normalizeFromYii + // ------------------------------------------------------------------------- + + public function testYiiSimpleColonPlaceholder() + { + $this->assertSame('/articles/{id}', RouteNormalizer::normalizeFromYii('/articles/:id')); + } + + public function testYiiStaticRoute() + { + $this->assertSame('/site/index', RouteNormalizer::normalizeFromYii('/site/index')); + } + + // ------------------------------------------------------------------------- + // normalizeFromCodeIgniter + // ------------------------------------------------------------------------- + + public function testCodeIgniterLiteralRoute() + { + $this->assertSame('/articles/index', RouteNormalizer::normalizeFromCodeIgniter('articles/index')); + } + + public function testCodeIgniterNumWildcard() + { + $this->assertSame('/blog/{param1}', RouteNormalizer::normalizeFromCodeIgniter('blog/(:num)')); + } + + public function testCodeIgniterAnyWildcard() + { + $this->assertSame('/users/{param1}', RouteNormalizer::normalizeFromCodeIgniter('users/:any')); + } + + public function testCodeIgniterMultipleWildcards() + { + $result = RouteNormalizer::normalizeFromCodeIgniter('posts/(:num)/comments/(:num)'); + $this->assertSame('/posts/{param1}/comments/{param2}', $result); + } + + public function testCodeIgniterCatchAll() + { + // A catch-all in CI is typically :any at the end + $this->assertSame('/{param1}', RouteNormalizer::normalizeFromCodeIgniter(':any')); + } + + // ------------------------------------------------------------------------- + // normalizeFromWordPress + // ------------------------------------------------------------------------- + + public function testWordPressSimpleRegex() + { + $result = RouteNormalizer::normalizeFromWordPress('^blog/([^/]+)/?$'); + $this->assertSame('/blog/{param1}', $result); + } + + public function testWordPressStaticRule() + { + $result = RouteNormalizer::normalizeFromWordPress('^about/?$'); + $this->assertSame('/about', $result); + } + + public function testWordPressMultipleGroups() + { + $result = RouteNormalizer::normalizeFromWordPress('^([^/]+)/([^/]+)/?$'); + $this->assertSame('/{param1}/{param2}', $result); + } + + public function testWordPressRootRule() + { + $result = RouteNormalizer::normalizeFromWordPress('^/?$'); + $this->assertSame('/', $result); + } + + // ------------------------------------------------------------------------- + // RFC examples + // ------------------------------------------------------------------------- + + public function testRfcExampleFastApi() + { + // http.route: /dashboard/shared_widget_update/{id}/{widget_id} + $result = RouteNormalizer::normalizeFromLaravel('/dashboard/shared_widget_update/{id}/{widget_id}'); + $this->assertSame('/dashboard/shared_widget_update/{id}/{widget_id}', $result); + } + + public function testRfcExampleDjangoDumpRequest() + { + // http.route: ^dump-request$ → /dump-request (after regex stripping) + // We test via WordPress normalizer since it handles regex + $result = RouteNormalizer::normalizeFromWordPress('^dump-request$'); + $this->assertSame('/dump-request', $result); + } + + public function testRfcExampleFlaskMixedStaticDynamic() + { + // http.route: /users/user- → /users/{id} + // Flask wraps static+dynamic in same segment; normalizer drops static prefix + $result = RouteNormalizer::normalizeFromLaravel('/users/{id}'); + $this->assertSame('/users/{id}', $result); + } + + public function testRfcExampleRailsMandatoryFormat() + { + // http.route: /photos/:id.:format → /photos/{id+format} + $result = RouteNormalizer::normalizeFromCakePHP('/photos/:id.:format'); + $this->assertSame('/photos/{id+format}', $result); + } + + public function testRfcExampleGoGorilla() + { + // http.route: /v2/{name:[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]}/blobs + $result = RouteNormalizer::normalizeFromSlim('/v2/{name:[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]}/blobs'); + $this->assertSame('/v2/{name}/blobs', $result); + } + + public function testRfcExampleRailsOptionalFormatPresent() + { + // /posts/:id(.:format) with format present → /posts/{id+format} + $result = RouteNormalizer::normalizeFromLaminas('/posts/:id[.:format]', ['id' => '1', 'format' => 'json']); + $this->assertSame('/posts/{id+format}', $result); + } + + public function testRfcExampleRailsOptionalFormatAbsent() + { + // /posts/:id(.:format) without format → /posts/{id} + $result = RouteNormalizer::normalizeFromLaminas('/posts/:id[.:format]', ['id' => '1']); + $this->assertSame('/posts/{id}', $result); + } +} diff --git a/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json b/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json index 94f4baa2036..fce7aa85a49 100644 --- a/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json +++ b/tests/snapshots/integrations.code_igniter.v3_1.no_ci_controller_test.test_scenario_health_check.json @@ -13,6 +13,7 @@ "component": "codeigniter", "http.method": "GET", "http.route": "health_check/ping", + "_dd.appsec.normalized_route": "/health_check/ping", "http.status_code": "200", "http.url": "http://localhost/health_check/ping", "runtime-id": "ca127e85-a20d-46aa-b510-07e9077a14c9", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json index 1da3301fe8b..97202ca6fb8 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_parameterized.json @@ -14,6 +14,7 @@ "component": "codeigniter", "http.method": "GET", "http.route": "parameterized/(:any)", + "_dd.appsec.normalized_route": "/parameterized/{param1}", "http.status_code": "200", "http.url": "http://localhost/parameterized/paramValue", "runtime-id": "26ab24e6-051f-4e1f-9adf-3d609bef6946", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json index 6f874986516..bfc1072c2ad 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_return_string.json @@ -14,6 +14,7 @@ "component": "codeigniter", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "26ab24e6-051f-4e1f-9adf-3d609bef6946", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json index 5170c9dbe32..8fa1be20b54 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route.json @@ -13,6 +13,7 @@ "component": "codeigniter", "http.method": "GET", "http.route": "does_not_exist", + "_dd.appsec.normalized_route": "/does_not_exist", "http.status_code": "404", "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "232799b0-3a18-4d64-9c97-4f6b441bb91e", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json index c50a1bd1bf6..2939c1bed83 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_to_missing_route_cgi.json @@ -13,6 +13,7 @@ "component": "codeigniter", "http.method": "GET", "http.route": "does_not_exist", + "_dd.appsec.normalized_route": "/does_not_exist", "http.status_code": "200", "http.url": "http://localhost/does_not_exist?key=value&", "runtime-id": "0caac976-2232-42e0-a186-9f73b4c27f50", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json index c89ca53ba12..f2fde56386b 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception.json @@ -18,6 +18,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "runtime-id": "91489364-94f6-4030-b3ed-5df886a158a0", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json index 49e57af64c0..ace85d9412f 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_exception_cgi.json @@ -18,6 +18,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "0caac976-2232-42e0-a186-9f73b4c27f50", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json index f1c4002d99b..092ebddb3c5 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.common_scenarios_test.test_scenario_get_with_view.json @@ -14,6 +14,7 @@ "component": "codeigniter", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "26ab24e6-051f-4e1f-9adf-3d609bef6946", diff --git a/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json b/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json index fb342972d71..f1d5e75c6bb 100644 --- a/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json +++ b/tests/snapshots/tests.integrations.code_igniter.v3_1.exit_test.test_scenario_exit.json @@ -14,6 +14,7 @@ "component": "codeigniter", "http.method": "GET", "http.route": "exits", + "_dd.appsec.normalized_route": "/exits", "http.status_code": "200", "http.url": "http://localhost/exits", "runtime-id": "de8ed04e-02e4-40ef-be2a-20aeeb1398ef", diff --git a/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest2xx.json b/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest2xx.json index 781cebdb29d..395db3b8050 100644 --- a/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest2xx.json +++ b/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest2xx.json @@ -12,6 +12,7 @@ "component": "laminas", "http.method": "POST", "http.route": "[/v:version]/datadog-rest-service[/:datadog_rest_service_id]", + "_dd.appsec.normalized_route": "/datadog-rest-service", "http.status_code": "201", "http.url": "http://localhost/datadog-rest-service", "http.version": "1.1", diff --git a/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest4xx.json b/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest4xx.json index bcd9fe56956..cb4e8df27be 100644 --- a/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest4xx.json +++ b/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest4xx.json @@ -12,6 +12,7 @@ "component": "laminas", "http.method": "GET", "http.route": "[/v:version]/datadog-rest-service[/:datadog_rest_service_id]", + "_dd.appsec.normalized_route": "/datadog-rest-service", "http.status_code": "405", "http.url": "http://localhost/datadog-rest-service/1", "http.version": "1.1", diff --git a/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest5xx.json b/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest5xx.json index b12796c2391..b13327c7b80 100644 --- a/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest5xx.json +++ b/tests/snapshots/tests.integrations.laminas.api_tools.latest.rest_test.test_scenario_rest5xx.json @@ -16,6 +16,7 @@ "error.type": "Error", "http.method": "GET", "http.route": "[/v:version]/datadog-rest-service[/:datadog_rest_service_id]", + "_dd.appsec.normalized_route": "/datadog-rest-service", "http.status_code": "500", "http.url": "http://localhost/datadog-rest-service/42", "http.version": "1.1", diff --git a/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_return_string.json index 1bdb15085fc..54850a8a703 100644 --- a/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_return_string.json @@ -12,6 +12,7 @@ "component": "laminas", "http.method": "GET", "http.route": "/simple[/:key][/:pwd]", + "_dd.appsec.normalized_route": "/simple", "laminas.route.name": "simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", diff --git a/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_exception.json index 378a177110c..891908b4440 100644 --- a/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_exception.json @@ -16,6 +16,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "/error[/:key][/:pwd]", + "_dd.appsec.normalized_route": "/error", "laminas.route.name": "error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", diff --git a/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_view.json index 2d16a69aa4d..601c5b1924e 100644 --- a/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laminas.mvc.latest.common_scenarios_test.test_scenario_get_with_view.json @@ -12,6 +12,7 @@ "component": "laminas", "http.method": "GET", "http.route": "/simple_view[/:key][/:pwd]", + "_dd.appsec.normalized_route": "/simple_view", "laminas.route.name": "simpleView", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", diff --git a/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_return_string.json index 1095152bcea..b789c970e72 100644 --- a/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laminas", "http.method": "GET", "http.route": "/simple[/:key][/:pwd]", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "http.version": "1.1", diff --git a/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_exception.json index b7e009b6638..fe7fccb8955 100644 --- a/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "/error[/:key][/:pwd]", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "http.version": "1.1", diff --git a/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_view.json index 901042e3de5..b0b11348c6c 100644 --- a/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laminas.mvc.v3_3.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laminas", "http.method": "GET", "http.route": "/simple_view[/:key][/:pwd]", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "http.version": "1.1", diff --git a/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy.json b/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy.json index 30a75fbd360..89f152f852d 100644 --- a/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy.json +++ b/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy.json @@ -36,6 +36,7 @@ "env": "local-test", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy_exception.json b/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy_exception.json index 1d07dbb6152..cab7e2f2364 100644 --- a/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy_exception.json +++ b/tests/snapshots/tests.integrations.laravel.apigw_test.test_laravel_inferred_proxy_exception.json @@ -44,6 +44,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_return_string.json index 8a232b92304..d5258d457dc 100644 --- a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_exception.json index 8f1d421becd..de30ef1d7f7 100644 --- a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_ignored_exception.json b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_ignored_exception.json index ac0e27db473..93a4818e629 100644 --- a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_ignored_exception.json +++ b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_ignored_exception.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "ignored_exception", + "_dd.appsec.normalized_route": "/ignored_exception", "http.status_code": "500", "http.url": "http://localhost/ignored_exception?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@ignored_exception", diff --git a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_view.json index 2f0e9cc11a9..61b5dc998c2 100644 --- a/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.latest.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", diff --git a/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy.json b/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy.json index b272893f432..1a8fcb1ca38 100644 --- a/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy.json +++ b/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy.json @@ -41,6 +41,7 @@ "http.request.headers.x-dd-proxy-request-time-ms": "1739261376000", "http.request.headers.x-dd-proxy-stage": "aws-prod", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy_exception.json b/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy_exception.json index 419fe5954d0..70fc8fc131d 100644 --- a/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy_exception.json +++ b/tests/snapshots/tests.integrations.laravel.octane.apigw_test.test_inferred_proxy_exception.json @@ -49,6 +49,7 @@ "http.request.headers.x-dd-proxy-request-time-ms": "1739261376000", "http.request.headers.x-dd-proxy-stage": "aws-prod", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json index 4f0313c6f4d..d9cdc8ec821 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json index 83bad2e3d5f..873d1872a29 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_ignored_exception.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_ignored_exception.json index 3233a40a359..fb9482ebd8d 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_ignored_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_ignored_exception.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "ignored_exception", + "_dd.appsec.normalized_route": "/ignored_exception", "http.status_code": "408", "http.url": "http://localhost/ignored_exception?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@ignored_exception", diff --git a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json index 112ac020a34..4f9f5b699b7 100644 --- a/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v10_x.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json index 129eb9bf54e..390c85885a2 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json index 2099de60186..a445e4875cc 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json index 680f92fc800..bd6031cd01c 100644 --- a/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v11_x.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json index 22efd535627..2d6ac0c4bb6 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "dynamic_route/{param01}/static/{param02?}", + "_dd.appsec.normalized_route": "/dynamic_route/{param01}/static/{param02}", "http.status_code": "200", "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@dynamicRoute", diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json index 70b03dbba75..032bf611c97 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json index 2934d7bfe6e..3ee7c0576c6 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json index 20a7a2d853e..f3c5e198fea 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v5_7.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json index 3e1774ade87..5e9c5e46358 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "dynamic_route/{param01}/static/{param02?}", + "_dd.appsec.normalized_route": "/dynamic_route/{param01}/static/{param02}", "http.status_code": "200", "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@dynamicRoute", diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json index ede2a71d263..3ff3e63d553 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json index bd6e68a2a27..c0ea78043e8 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json index e6dd0b6f683..170f307d402 100644 --- a/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v5_8.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json index e2ee39ae9c5..a30102ddd39 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_dynamic_route.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "dynamic_route/{param01}/static/{param02?}", + "_dd.appsec.normalized_route": "/dynamic_route/{param01}/static/{param02}", "http.status_code": "200", "http.url": "http://localhost/dynamic_route/dynamic01/static/dynamic02", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@dynamicRoute", diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json index 1ad5cb004a7..091d23af98d 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json index 0d1c1ee654c..056926e4848 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json index de7b4288e70..6f765f19df8 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", diff --git a/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json b/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json index 66ee49f491e..668f86f62ca 100644 --- a/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json +++ b/tests/snapshots/tests.integrations.laravel.v8_x.queue_test.test_broadcast.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "queue/broadcast", + "_dd.appsec.normalized_route": "/queue/broadcast", "http.status_code": "200", "http.url": "http://localhost/queue/broadcast", "laravel.route.action": "App\\Http\\Controllers\\QueueTestController@broadcast", diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json index 01e28d6b433..a3ee9d20079 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple", + "_dd.appsec.normalized_route": "/simple", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple", diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json index c822b1957aa..171e7435f4d 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "error", + "_dd.appsec.normalized_route": "/error", "http.status_code": "500", "http.url": "http://localhost/error?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@error", diff --git a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json index 34138c0c85d..0f35b0875ed 100644 --- a/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.laravel.v9_x.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "laravel", "http.method": "GET", "http.route": "simple_view", + "_dd.appsec.normalized_route": "/simple_view", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "laravel.route.action": "App\\Http\\Controllers\\CommonSpecsController@simple_view", diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json index a94467e5ad4..4a0424e5fc1 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "b4ee1995-4afb-4457-9e9d-b361460bfa16", diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 942275e9995..1cd0231837c 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "b4ee1995-4afb-4457-9e9d-b361460bfa16", diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json index 4cea0bce0da..74b6b4c2b02 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "(.?.+?)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "b4ee1995-4afb-4457-9e9d-b361460bfa16", diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json index 34e87afcbd1..81014c33efb 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "8fdcf6ef-7cd9-4910-b426-c7c9809f3dd4", diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json index ad6b6999981..40cddd81172 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "8fdcf6ef-7cd9-4910-b426-c7c9809f3dd4", diff --git a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json index 3da297ba272..778f8d9209b 100644 --- a/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v4_8.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "(.?.+?)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "8fdcf6ef-7cd9-4910-b426-c7c9809f3dd4", diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json index 5fe007192cb..0c1c4d74475 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "4ad4333f-2e0b-4278-a6f7-2182e7771b34", diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 323e0bbfd23..d72ec1acfd1 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "4ad4333f-2e0b-4278-a6f7-2182e7771b34", diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json index f74b08cfaf3..cb7425d421b 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "4ad4333f-2e0b-4278-a6f7-2182e7771b34", diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json index b2cc305f4fe..2d967a4883f 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "f188c752-a672-4955-97f7-e41a31d13fe7", diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json index 310b142ac51..b283c6044c1 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "f188c752-a672-4955-97f7-e41a31d13fe7", diff --git a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json index ca8d5fe1348..0568c99e74b 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_5.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "f188c752-a672-4955-97f7-e41a31d13fe7", diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json index ebfef74a265..46e6fbaa20c 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "896f86bc-7139-44f3-a99f-ed35e643f726", diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 27319620a33..bd2e2440b18 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "896f86bc-7139-44f3-a99f-ed35e643f726", diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json index 683d022d4e2..f8cf6506965 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "896f86bc-7139-44f3-a99f-ed35e643f726", diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json index a2f281d70d4..643b28cd516 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "4c46007f-c934-41aa-bcbe-c48ecee2d4cc", diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json index c982920760e..abbd529ddfe 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "4c46007f-c934-41aa-bcbe-c48ecee2d4cc", diff --git a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json index 671eb3f7d1a..c7c1aa69e1a 100644 --- a/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v5_9.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "4c46007f-c934-41aa-bcbe-c48ecee2d4cc", diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json index d965665100a..20ca8883a6b 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "333590aa-cf9b-4804-9dde-1ac7b59c09ab", diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json index 10673208e8e..75cb2133a29 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "333590aa-cf9b-4804-9dde-1ac7b59c09ab", diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json index e055001b0cc..1fad3089c07 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_callbacks_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "333590aa-cf9b-4804-9dde-1ac7b59c09ab", diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json index cfbeb419d9c..55bd806fdb2 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_return_string.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple?key=value&", "runtime-id": "df54db4d-0cc0-4b1c-9fce-8004a54aa78b", diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json index 76919c42279..d20cc4a0e26 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_exception.json @@ -17,6 +17,7 @@ "error.type": "Exception", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/error?key=value&", "runtime-id": "df54db4d-0cc0-4b1c-9fce-8004a54aa78b", diff --git a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json index 4b9c2000682..2c0aced29de 100644 --- a/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json +++ b/tests/snapshots/tests.integrations.word_press.v6_1.common_scenarios_test.test_scenario_get_with_view.json @@ -13,6 +13,7 @@ "component": "wordpress", "http.method": "GET", "http.route": "([^/]+)(?:/([0-9]+))?/?$", + "_dd.appsec.normalized_route": "/{param1}/{param2}", "http.status_code": "200", "http.url": "http://localhost/simple_view?key=value&", "runtime-id": "df54db4d-0cc0-4b1c-9fce-8004a54aa78b",