Skip to content

Fix SSRF: enforce host/scheme match in HTTPIntegrationConnector.Execute#170

Merged
intel352 merged 3 commits intomainfrom
copilot/fix-uncontrolled-data-integration
Feb 26, 2026
Merged

Fix SSRF: enforce host/scheme match in HTTPIntegrationConnector.Execute#170
intel352 merged 3 commits intomainfrom
copilot/fix-uncontrolled-data-integration

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

CodeQL flagged module/integration.go:290 — user-controlled action path flows into url.JoinPath and directly into http.Client.Do, with only a DNS-based private-IP check as a guard. DNS-based validation is bypassable via rebinding; there was no structural guarantee the constructed URL still targeted the configured host.

Changes

  • module/integration.go — adds validateURLHost(reqURL, baseURL string) error, a pure URL-structural check comparing Scheme and Host of the constructed URL against the configured baseURL. Called in Execute() immediately after url.JoinPath, before any DNS lookup or HTTP call:
reqURL, err := url.JoinPath(c.baseURL, path)
// ...
if err := validateURLHost(reqURL, c.baseURL); err != nil {
    return nil, fmt.Errorf("invalid request URL: %w", err)
}
  • module/integration_test.go — adds TestValidateURLHost (table-driven: matching host/scheme allowed, mismatched host or scheme blocked, invalid URL blocked) and TestHTTPIntegrationConnector_ExecuteHostInjectionBlocked (verifies a crafted path cannot contact a different server).

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 26, 2026 09:14
… SSRF

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix uncontrolled data issue in network request Fix SSRF: enforce host/scheme match in HTTPIntegrationConnector.Execute Feb 26, 2026
@intel352 intel352 marked this pull request as ready for review February 26, 2026 21:25
Copilot AI review requested due to automatic review settings February 26, 2026 21:25
@intel352 intel352 closed this Feb 26, 2026
@intel352 intel352 reopened this Feb 26, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a critical SSRF vulnerability identified by CodeQL where user-controlled action paths could flow into HTTP requests without proper validation. The fix adds structural URL validation to ensure constructed URLs match the configured base URL's scheme and host.

Changes:

  • Adds validateURLHost() function to perform scheme and host validation before HTTP requests
  • Integrates the validation into HTTPIntegrationConnector.Execute() immediately after URL construction
  • Adds comprehensive unit and integration tests to verify the protection works correctly

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
module/integration.go Implements new validateURLHost() function and integrates it into the Execute() method to validate constructed URLs match the configured base URL before HTTP requests
module/integration_test.go Adds TestValidateURLHost with table-driven test cases and TestHTTPIntegrationConnector_ExecuteHostInjectionBlocked to verify host injection is prevented
example/go.mod Updates indirect dependencies (AWS SDK, DigitalOcean, miniredis, etc.) - routine dependency updates unrelated to the security fix
example/go.sum Corresponding checksum updates for the dependency changes in go.mod

Comment on lines +154 to +200
func TestValidateURLHost(t *testing.T) {
tests := []struct {
name string
reqURL string
baseURL string
wantErr bool
}{
{
name: "matching host and scheme",
reqURL: "https://api.example.com/v1/resource",
baseURL: "https://api.example.com",
wantErr: false,
},
{
name: "matching host with path in base",
reqURL: "https://api.example.com/v1/resource",
baseURL: "https://api.example.com/v1",
wantErr: false,
},
{
name: "different host",
reqURL: "https://evil.com/steal",
baseURL: "https://api.example.com",
wantErr: true,
},
{
name: "different scheme",
reqURL: "http://api.example.com/resource",
baseURL: "https://api.example.com",
wantErr: true,
},
{
name: "invalid request URL",
reqURL: "://bad-url",
baseURL: "https://api.example.com",
wantErr: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := validateURLHost(tc.reqURL, tc.baseURL)
if (err != nil) != tc.wantErr {
t.Errorf("validateURLHost(%q, %q) error = %v, wantErr %v", tc.reqURL, tc.baseURL, err, tc.wantErr)
}
})
}
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validateURLHost tests should include test cases for port-related edge cases. Important scenarios to cover:

  1. Different explicit ports (e.g., baseURL with :8080, reqURL with :9090)
  2. Explicit port vs default port (e.g., "https://example.com:443" vs "https://example.com")
  3. Missing port in baseURL but explicit port in reqURL

The Go url.Parse Host field includes ports, so "example.com:8080" and "example.com:9090" would be correctly detected as different hosts. However, the edge case where one URL has an explicit default port (":443" for https, ":80" for http) and the other omits it could be problematic depending on url.Parse behavior. This should be tested to ensure the validation correctly handles these cases.

Copilot uses AI. Check for mistakes.
@intel352 intel352 merged commit e5daf58 into main Feb 26, 2026
18 checks passed
@intel352 intel352 deleted the copilot/fix-uncontrolled-data-integration branch February 26, 2026 21:36
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.

3 participants