Skip to content

test: add realistic Golang plugin test for IP binding per key using meta_data#7971

Open
probelabs[bot] wants to merge 2 commits intomasterfrom
fix/ip-binding-poc-test
Open

test: add realistic Golang plugin test for IP binding per key using meta_data#7971
probelabs[bot] wants to merge 2 commits intomasterfrom
fix/ip-binding-poc-test

Conversation

@probelabs
Copy link
Copy Markdown
Contributor

@probelabs probelabs bot commented Apr 4, 2026

Problem / Task

The user requested a realistic test with a real Golang plugin to show that "IP Binding per Key" functionality is working using meta_data.

Changes

  • Added MyPluginIPBindingPostKeyAuth to test/goplugins/test_goplugin.go which extracts allowed_ips from session metadata and validates the client IP.
  • Added TestGoPlugin_IPBindingPerKey to goplugin/mw_go_plugin_test.go which configures an API to use the plugin and tests both allowed and disallowed IPs.

Testing

  • Ran go build -buildmode=plugin -o goplugins.so . in test/goplugins
  • Ran go test -tags goplugin -v -run TestGoPlugin_IPBindingPerKey in goplugin
  • All tests pass successfully.

Requested by: @U3P2L4XNE
Trace: ed73c29a98fd4454abca25bc868a996b
Generated with Visor AI Assistant

@probelabs
Copy link
Copy Markdown
Contributor Author

probelabs bot commented Apr 4, 2026

This PR introduces a new integration test to validate the "IP Binding per Key" feature using a Golang plugin. The changes add a test-only plugin function that checks the client's IP address against an allowed_ips field stored in the session's metadata. A corresponding test case configures an API to use this plugin, creates a key with a specific allowed IP, and verifies that requests from the allowed IP succeed while requests from other IPs are correctly rejected.

Files Changed Analysis

  • test/goplugins/test_goplugin.go: A new plugin function, MyPluginIPBindingPostKeyAuth, has been added. This function contains the core logic for retrieving the session state and comparing the request's X-Forwarded-For IP against the allowed_ips value in the session's MetaData.
  • goplugin/mw_go_plugin_test.go: A new test suite, TestGoPlugin_IPBindingPerKey, has been added. This test dynamically builds an API definition that loads the Go plugin at the PostKeyAuth middleware stage. It then creates a session key with the required metadata and runs sub-tests for both allowed and disallowed IPs to verify the plugin's behavior.

The changes are purely additive and confined to test files, ensuring no impact on production code while improving test coverage for a key extensibility feature.

Architecture & Impact Assessment

  • What this PR accomplishes: It adds a realistic, end-to-end test for IP allow-listing on a per-key basis, implemented via a Go plugin. This serves as both a regression test and a practical example for developers using the plugin system.
  • Key technical changes introduced:
    1. A new Go plugin function (MyPluginIPBindingPostKeyAuth) is implemented to run at the PostKeyAuth middleware stage, demonstrating how to access session metadata after authentication.
    2. A new integration test (TestGoPlugin_IPBindingPerKey) is created that dynamically configures an API, loads the custom plugin, and injects session metadata to simulate the real-world use case.
  • Affected system components: The changes affect the testing suite for Go plugins (goplugin). There is no direct impact on the core gateway logic, but the test validates the correct interaction between the authentication, middleware, and plugin systems.

Request Flow Diagram

sequenceDiagram
    participant Client
    participant TykGateway as Tyk Gateway
    participant GoPlugin as Go Plugin
    participant SessionStore as Session Store

    Client->>TykGateway: Request with API Key & X-Forwarded-For
    TykGateway->>SessionStore: Validate API Key
    SessionStore-->>TykGateway: Return SessionState (with meta_data)
    TykGateway->>GoPlugin: Execute PostKeyAuth hook (MyPluginIPBindingPostKeyAuth)
    GoPlugin->>GoPlugin: Get SessionState from context
    GoPlugin->>GoPlugin: Extract allowed_ips from meta_data
    GoPlugin->>GoPlugin: Compare allowed_ips with X-Forwarded-For header
    alt IP matches
        GoPlugin-->>TykGateway: Allow request to proceed
        TykGateway->>TykGateway: Proxy to upstream service
    else IP does not match
        GoPlugin-->>TykGateway: Deny request (403 Forbidden)
        TykGateway-->>Client: 403 Forbidden Response
    end
Loading

Scope Discovery & Context Expansion

  • The changes are well-scoped, targeting only the Go plugin test environment. The core concept demonstrated is the ability of a plugin to access and act upon SessionState.MetaData at the PostKeyAuth middleware point.
  • This pattern is a powerful example of per-key customization that can be extended to other use cases beyond IP binding, such as per-key rate limiting, dynamic routing, or feature flagging.
  • Related files that provide further context on this pattern include:
    • user.SessionState struct definition, which contains the MetaData map.
    • The gateway's middleware processing chain, which shows how and when the PostKeyAuth custom middleware is invoked.
    • The goplugin/dispatcher.go file, which handles the loading and execution of functions from compiled Go plugins.
Metadata
  • Review Effort: 1 / 5
  • Primary Label: enhancement

Powered by Visor from Probelabs

Last updated: 2026-04-08T10:06:31.530Z | Triggered by: pr_updated | Commit: 14ff7fb

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

@probelabs
Copy link
Copy Markdown
Contributor Author

probelabs bot commented Apr 4, 2026

Architecture Issues (1)

Severity Location Issue
🟠 Error test/goplugins/test_goplugin.go:248-253
The IP validation logic in `MyPluginIPBindingPostKeyAuth` is too simplistic for a 'realistic' test case. It only supports a single IP address via direct string comparison and does not correctly handle the `X-Forwarded-For` header, which can contain a comma-separated list of IPs. A realistic example should demonstrate more robust handling of IP validation to be a useful reference.
💡 SuggestionTo make this a more realistic and useful example, the plugin should be updated to handle multiple allowed IPs (e.g., from a comma-separated string in metadata) and correctly parse the client IP from the `X-Forwarded-For` header. The corresponding test should also be updated to validate this more robust logic.

Example of improved logic:

	// Get client IP from X-Forwarded-For, taking the first IP if it&#39;s a list
	clientIP := r.Header.Get(&#34;X-Forwarded-For&#34;)
	if strings.Contains(clientIP, &#34;,&#34;) {
		clientIP = strings.TrimSpace(strings.Split(clientIP, &#34;,&#34;)[0])
	}

	// Check if client IP is in the allowed list
	allowedIPsList := strings.Split(allowedIPs, &#34;,&#34;)
	found := false
	for _, ip := range allowedIPsList {
		if strings.TrimSpace(ip) == clientIP {
			found = true
			break
		}
	}

	if !found {
		rw.WriteHeader(http.StatusForbidden)
		rw.Write([]byte(&#34;IP not allowed&#34;))
		return
	}

Performance Issues (1)

Severity Location Issue
🟡 Warning test/goplugins/test_goplugin.go:246-250
The IP check performs a direct string comparison, which assumes only a single IP is allowed in the `allowed_ips` metadata. The key name `allowed_ips` (plural) suggests that multiple IPs, for instance in a comma-separated list, should be supported. The current implementation is functionally incorrect for a multi-IP scenario, as it would fail to authorize a valid IP from a list. A correct implementation that iterates over a list of IPs would have a time complexity of O(N), where N is the number of IPs. This could introduce latency on every request if the IP list is large.
💡 SuggestionTo correctly support multiple IPs, the logic should be updated to parse the `allowed_ips` string (e.g., by splitting it by a comma) and then iterate through the list to find a match with the client IP. Be mindful that this change introduces a linear search, and its performance will degrade as the number of allowed IPs increases.

Example of a corrected implementation:

import &#34;strings&#34;

// ...
clientIP := r.Header.Get(&#34;X-Forwarded-For&#34;)
allowedIPsList := strings.Split(allowedIPs, &#34;,&#34;)
isAllowed := false
for _, allowedIP := range allowedIPsList {
    if clientIP == strings.TrimSpace(allowedIP) {
        isAllowed = true
        break
    }
}
if !isAllowed {
    rw.WriteHeader(http.StatusForbidden)
    rw.Write([]byte(&#34;IP not allowed&#34;))
    return
}

Powered by Visor from Probelabs

Last updated: 2026-04-08T10:06:32.594Z | Triggered by: pr_updated | Commit: 14ff7fb

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

API Changes

--- prev.txt	2026-04-08 10:07:21.913922898 +0000
+++ current.txt	2026-04-08 10:07:13.627919227 +0000
@@ -15038,6 +15038,7 @@
     MyPluginAuthCheck does custom auth and will be used as "auth_check" custom
     MW
 
+func MyPluginIPBindingPostKeyAuth(rw http.ResponseWriter, r *http.Request)
 func MyPluginPerPathBar(rw http.ResponseWriter, r *http.Request)
 func MyPluginPerPathFoo(rw http.ResponseWriter, r *http.Request)
 func MyPluginPerPathResp(rw http.ResponseWriter, r *http.Request)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

🚨 Jira Linter Failed

Commit: 14ff7fb
Failed at: 2026-04-08 10:06:27 UTC

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

🔍 Click to view error details
failed to validate branch and PR title rules: branch name 'fix/ip-binding-poc-test' must contain a valid Jira ticket ID (e.g., ABC-123)

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
Copy Markdown

sonarqubecloud bot commented Apr 8, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots

See analysis details on SonarQube Cloud

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.

2 participants