Skip to content

Conversation

@andrei-tyk
Copy link
Contributor

@andrei-tyk andrei-tyk commented Dec 10, 2025

Description

Unifies path matching algorithm for OAS-specific middleware (ValidateRequest, MockResponse) to use the same regex-based RxPaths matching as Classic APIs, instead of Gorilla Mux exact matching.
Key changes

gateway/api_definition.go:

• Add OASValidateRequest and OASMockResponse URL status types
• Move OAS initialization before RxPaths compilation
• Add compileOASValidateRequestPathSpec() and compileOASMockResponsePathSpec() to compile OAS operations into URLSpec entries
• Add findPathAndMethodForOperation() helper

gateway/model_urlspec.go:

• Add OASValidateRequestMeta, OASMockResponseMeta, OASMethod, OASPath fields to URLSpec
• Update matchesMethod() and modeSpecificSpec() for OAS status types

gateway/model_apispec.go:

• Add findRouteForOASPath() to find OAS route using path pattern for schema validation
• Add extractPathParams() to extract params from actual request path

gateway/mw_oas_validate_request.go / gateway/mw_mock_response.go:

• Use FindSpecMatchesStatus() instead of findOperation() for path matching
• Add fallback to original behavior for mux-template listen paths

Related Issue

• TT-15856: Unify path matching algorithm for all middleware
• Related: TT-10609, TT-15374, TT-10626

Motivation and Context

Before this fix, OAS middleware used Gorilla Mux which only does exact path matching. This caused:

  1. Subpaths not triggering middleware: Request to /anything/abc would NOT trigger validation/mock defined on /anything
  2. Wildcard paths not working: Paths like /.* or / (used to apply middleware to entire API) did not work for OAS APIs
Endpoint Defined Request Path Classic API OAS API (bug)
/anything /anything/abc Middleware triggered Not triggered
/.* /users/123 Middleware triggered Not triggered

How This Has Been Tested

Unit tests added (Go):
• gateway/mw_oas_validate_request_test.go (354 lines)
• gateway/mw_mock_response_oas_simple_test.go (204 lines)
• gateway/mw_mock_response_versioning_test.go (421 lines)
• gateway/mw_oas_path_matching_regression_test.go (115 lines)

Integration tests added (Python in tyk-analytics):
• 22 tests in tests/api/tests/dashboard_api/oas_path_matching_test.py

Validation:

Test On master (bug) On this branch
test_validate_request_subpath_triggers_middleware FAIL (200) PASS (422)
test_mock_response_subpath_triggers_middleware FAIL (404) PASS (200 mock)
test_validate_request_wildcard_path_matches_all_endpoints FAIL PASS
test_mock_response_wildcard_path_matches_all_endpoints FAIL PASS
All 7 wildcard tests FAIL PASS

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring or add test (improvements in base code or adds test coverage to functionality)

Checklist

  • I ensured that the documentation is up to date
  • I explained why this PR updates go.mod in detail with reasoning why it's required
  • I would like a code coverage CI quality gate exception and have explained why

Ticket Details

TT-15856
Status Closed
Summary [PoC] Unify path matching algorithm for all middleware

Generated at: 2025-12-15 08:49:23

andrei-tyk and others added 4 commits December 9, 2025 15:36
   OAS-specific middleware (validateRequest, mockResponse) now use the standard regex-based path matching algorithm instead of Gorilla Mux, ensuring gateway configurations like EnablePathPrefixMatching work consistently across all API types.
   - Add OASValidateRequest and OASMockResponse URL status types
   - Compile OAS operations into URLSpec entries in RxPaths
   - Update OAS middleware to use FindSpecMatchesStatus for matching
   - Add version fallback for unversioned APIs
   - Add regression test verifying gateway config is respected
   Fixes path matching inconsistencies where OAS APIs ignored gateway-level configuration options.
@probelabs
Copy link

probelabs bot commented Dec 10, 2025

This pull request unifies the path matching algorithm for OpenAPI Specification (OAS) middleware, ensuring that it respects the gateway's standard regex-based routing configurations, such as prefix and suffix matching. This resolves a significant inconsistency where OAS APIs previously used an exact-match mechanism, ignoring global gateway routing rules that applied to classic APIs.

Files Changed Analysis

The changes are focused on the gateway's core API definition and routing logic, with 1516 additions and 23 deletions across 10 files. The vast majority of the new code consists of comprehensive tests (five new test files and significant additions to another), which is appropriate for a change to a critical component like path matching. The core logic modifications are concentrated in gateway/api_definition.go, gateway/model_apispec.go, and gateway/model_urlspec.go, with the corresponding middleware (mw_mock_response.go, mw_oas_validate_request.go) updated to use the new unified system.

Architecture & Impact Assessment

  • What this PR accomplishes: It aligns the behavior of OAS APIs with classic APIs by making OAS-specific middleware (ValidateRequest, MockResponse) honor the gateway's global path matching settings (EnablePathPrefixMatching, EnablePathSuffixMatching). This makes routing behavior consistent and predictable across all API types.

  • Key technical changes introduced:

    1. Compile-Time Path Integration: During API loading, OAS middleware paths are now compiled into the gateway's standard URLSpec structures. A regex is generated for each path that respects the gateway's matching rules, while the original OAS path pattern (e.g., /users/{id}) is preserved within the URLSpec for later use.
    2. Two-Step Runtime Resolution: When a request arrives, the gateway first uses its standard regex engine to find a matching URLSpec. If the matched spec is for an OAS middleware, it then uses a new findRouteForOASPath function to look up the precise operation in the OAS router using the stored original path. This hybrid approach ensures gateway matching rules are respected while still allowing for correct OAS schema validation and path parameter extraction.
  • Affected system components:

    • API Definition Loading (gateway/api_definition.go)
    • Gateway Request Path Matching Engine
    • OAS Mock Response Middleware (gateway/mw_mock_response.go)
    • OAS Request Validation Middleware (gateway/mw_oas_validate_request.go)
  • **```mermaid
    graph TD
    subgraph sg1 [


Powered by Visor from Probelabs

Last updated: 2025-12-15T08:52:40.794Z | Triggered by: pr_updated | Commit: 153adeb

💡 TIP: You can chat with Visor using /visor ask <your question>

@github-actions
Copy link
Contributor

github-actions bot commented Dec 10, 2025

API Changes

--- prev.txt	2025-12-15 08:50:01.752347421 +0000
+++ current.txt	2025-12-15 08:49:51.375307843 +0000
@@ -11618,6 +11618,8 @@
 	StatusRequestNotTracked               RequestStatus = "Request Not Tracked"
 	StatusValidateJSON                    RequestStatus = "Validate JSON"
 	StatusValidateRequest                 RequestStatus = "Validate Request"
+	StatusOASValidateRequest              RequestStatus = "OAS Validate Request"
+	StatusOASMockResponse                 RequestStatus = "OAS Mock Response"
 	StatusInternal                        RequestStatus = "Internal path"
 	StatusGoPlugin                        RequestStatus = "Go plugin"
 	StatusPersistGraphQL                  RequestStatus = "Persist GraphQL"
@@ -12237,8 +12239,16 @@
 	GoPluginMeta              GoPluginMiddleware
 	PersistGraphQL            apidef.PersistGraphQLMeta
 	RateLimit                 apidef.RateLimitMeta
+	OASValidateRequestMeta    *oas.ValidateRequest
+	OASMockResponseMeta       *oas.MockResponse
 
 	IgnoreCase bool
+	// OASMethod stores the HTTP method for OAS-specific middleware
+	// This is needed because OAS operations are method-specific
+	OASMethod string
+	// OASPath stores the original OAS path pattern (e.g., "/users/{id}")
+	// This is used for matching against the OAS router when needed
+	OASPath string
 	// Has unexported fields.
 }
     URLSpec represents a flattened specification for URLs, used to check if
@@ -12269,10 +12279,12 @@
 	RequestTracked
 	RequestNotTracked
 	ValidateJSONRequest
+	OASValidateRequest
 	Internal
 	GoPlugin
 	PersistGraphQL
 	RateLimit
+	OASMockResponse
 )
     Enums representing the various statuses for a VersionInfo Path match during
     a proxy request

@probelabs
Copy link

probelabs bot commented Dec 10, 2025

Security Issues (2)

Severity Location Issue
🟡 Warning gateway/mw_oas_validate_request.go:71-73
Inconsistent handling of mux-template listen paths between OAS validation and mock response middleware. The validation middleware includes a fallback to the old `findOperation` logic for such paths, citing unreliability of `StripListenPath`, while the mock response middleware does not have this fallback and uses the new logic. This can lead to divergent path matching behavior for the same request, potentially causing security controls like validation to be bypassed unexpectedly.
💡 SuggestionUnify the handling of mux-template listen paths across all OAS middleware. If `StripListenPath` is indeed unreliable for these patterns, the fallback logic present in `mw_oas_validate_request.go` should also be implemented in `mw_mock_response.go`. Alternatively, if the special handling within `findRouteForOASPath` is sufficient, the explicit fallback in the validation middleware should be removed to ensure consistent behavior.
🟡 Warning gateway/model_apispec.go:133
The `extractPathParams` function splits the request path by `/` with no limit on the number of segments. A request with a very long path containing thousands of slashes could cause `strings.Split` to allocate a large slice, leading to excessive memory usage per request and a potential denial-of-service vulnerability.
💡 SuggestionIntroduce a reasonable limit on the number of path segments processed by `extractPathParams`. If a request path exceeds this limit, it should be rejected or handled gracefully without excessive memory allocation. For example, add a check like `if len(actualParts) > 256 { return empty_map }` after splitting the path.

Architecture Issues (2)

Severity Location Issue
🟠 Error gateway/mw_oas_validate_request.go:72-74
The handling of mux-template listen paths is inconsistent between the OAS `ValidateRequest` and `MockResponse` middleware. `ValidateRequest` explicitly falls back to the old `findOperation` logic for these paths, citing unreliability of `StripListenPath`. However, `MockResponse` proceeds with the new unified logic, which uses `StripListenPath`. This creates a divergence in behavior for the same API configuration and undermines the goal of unifying path matching. If `StripListenPath` is indeed unreliable for these paths, then `MockResponse` will likely be buggy.
💡 SuggestionThe logic should be made consistent. Either both middleware should use the fallback to the old behavior for mux-template listen paths, or the new logic (specifically `StripListenPath` and `findRouteForOASPath`) should be fixed to reliably handle these paths so that no fallback is necessary. A test case covering OAS middleware with a mux-template listen path should also be added to prevent regressions.
🟡 Warning gateway/api_definition.go:1391-1403
The function `findPathAndMethodForOperation` performs a linear scan of all paths and methods in the OAS specification to find the path and method for a given `operationID`. This function is called within a loop over all operations that have middleware enabled. This results in a potentially high-complexity operation during API loading, which could be slow for large OAS specifications.
💡 SuggestionTo optimize this, pre-compute a map from `operationID` to its corresponding path and method once, when the API is being loaded. This would change the lookup to an O(1) operation inside the loop, significantly improving performance for APIs with many paths and operations.

Performance Issues (2)

Severity Location Issue
🟡 Warning gateway/api_definition.go:1408-1422
The function `findPathAndMethodForOperation` performs an inefficient lookup by iterating through all paths and methods of an OAS specification to find an operation by its ID. This function is called within a loop over all middleware-enabled operations during API loading, resulting in a time complexity of approximately O(O * P * M) where O is the number of operations with middleware, P is the number of paths, and M is the number of methods. For large OAS specifications, this can significantly slow down API loading and gateway reload times.
💡 SuggestionTo optimize this, create a reverse mapping from `operationID` to its corresponding path and method once, immediately after the OAS spec is loaded and initialized. This map can then be used for O(1) lookups within the `compileOAS...PathSpec` functions, reducing the overall complexity to O(P*M + O) and improving startup/reload performance.
🟡 Warning gateway/model_apispec.go:199-232
The `findRouteForOASPath` function, which is executed on the hot path for every request matching an OAS middleware rule, allocates a new `url.URL` and `http.Request` object on every invocation. In a high-throughput environment, these frequent, per-request allocations can put significant pressure on the garbage collector, potentially increasing request latency and overall CPU usage.
💡 SuggestionConsider using a `sync.Pool` to pool and reuse `http.Request` objects. A pooled object can be retrieved, its fields reset and populated for the `oasRouter.FindRoute` call, and then returned to the pool. This would substantially reduce memory allocations and GC pressure under heavy load.

Quality Issues (2)

Severity Location Issue
🟠 Error gateway/mw_mock_response.go:94
The `mockResponse` middleware does not include the fallback logic for mux-template listen paths that is present in the `ValidateRequest` middleware. The `ValidateRequest` middleware (in `mw_oas_validate_request.go`) explicitly falls back to the old `findOperation` method for such paths, citing unreliability in `StripListenPath`. The lack of this fallback in `mockResponse` leads to inconsistent path matching behavior between different OAS middlewares on the same API, where validation might use exact matching while mocking uses the new regex-based matching.
💡 SuggestionApply the same fallback logic to the `mockResponse` middleware to ensure consistent behavior for APIs with mux-template listen paths. This involves adding a check for `httputil.IsMuxTemplate(m.Spec.Proxy.ListenPath)` and executing the original `findOperation`-based logic if it returns true.
🟡 Warning gateway/mw_oas_validate_request.go:70-72
The pull request introduces special handling for APIs with mux-template listen paths (e.g., `/api/{version:.*}`), including a fallback to the old behavior in this file and a special logic branch in `model_apispec.go#findRouteForOASPath`. However, none of the new or modified tests cover this specific scenario. This is a significant test coverage gap for a complex edge case that is explicitly handled in the code.
💡 SuggestionAdd tests for an OAS API that uses a mux-template listen path to verify that both the fallback mechanism in `ValidateRequest` and the special handling in `mockResponse` and `findRouteForOASPath` work as expected.

Powered by Visor from Probelabs

Last updated: 2025-12-15T08:52:43.565Z | Triggered by: pr_updated | Commit: 153adeb

💡 TIP: You can chat with Visor using /visor ask <your question>

@github-actions
Copy link
Contributor

🚨 Jira Linter Failed

Commit: 153adeb
Failed at: 2025-12-15 08:49:25 UTC

The Jira linter failed to validate your PR. Please check the error details below:

🔍 Click to view error details
failed to validate Jira issue: jira ticket TT-15856 has status 'Closed' but must be one of: In Dev, In Code Review, Ready For Dev, Dod Check

Next Steps

  • Ensure your branch name contains a valid Jira ticket ID (e.g., ABC-123)
  • Verify your PR title matches the branch's Jira ticket ID
  • Check that the Jira ticket exists and is accessible

This comment will be automatically deleted once the linter passes.

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants