Skip to content

fix(sdk): preserve base path when building KAS rewrap client URL#3762

Open
elizabethhealy wants to merge 2 commits into
mainfrom
remove-kas-url-stripping
Open

fix(sdk): preserve base path when building KAS rewrap client URL#3762
elizabethhealy wants to merge 2 commits into
mainfrom
remove-kas-url-stripping

Conversation

@elizabethhealy

@elizabethhealy elizabethhealy commented Jul 21, 2026

Copy link
Copy Markdown
Member

Proposed Changes

parseBaseURL in sdk/kas_client.go reduced a KAS URL to scheme://host[:port], discarding the URL path. The result is passed to kasconnect.NewAccessServiceClient as the Connect base URL, and Connect builds each request as baseURL + "/" + procedure. For a KAS served under a base path (e.g. https://host/kas), dropping the path sends Rewrap to https://host/kas.AccessService/Rewrap instead of https://host/kas/kas.AccessService/Rewrap, so the request never reaches the service.

  • preserve u.Path (trailing slash trimmed to avoid a doubled //) when constructing
    the base URL
  • update TestParseBaseUrl cases that asserted the path was stripped, and add coverage
    for base-path and trailing-slash inputs

Background

parseBaseURL began life as getGRPCAddress, which returned a gRPC dial target (host:port) where a path is meaningless — so stripping was correct. The Connect migration (#2200) renamed it and repurposed the return value as an HTTP base URL, but kept the host+port-only logic. A Connect base URL must retain the path, so the path-stripping became a regression. Host-root KAS deployments have no path to lose, which is why it went unnoticed.

Symptom

Because the mis-routed request is rejected by an upstream proxy/LB with a plain HTTP 403, connect-go maps it to the permission_denied code — surfacing as kao unwrap failed ... permission_denied: 403 Forbidden during decrypt, which looks like an access-policy denial rather than a routing error.

Checklist

  • I have added or updated unit tests
  • I have added or updated integration tests (if appropriate)
  • I have added or updated documentation

Testing Instructions

Summary by CodeRabbit

  • Bug Fixes
    • Fixed connectivity for KAS deployments hosted under a URL sub-path.
    • Preserved configured base paths while removing trailing slashes to prevent malformed request URLs.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a routing regression where the KAS client was incorrectly stripping the path component from the base URL. By preserving the path, the client now correctly routes requests to KAS instances deployed under non-root paths, preventing 403 errors that were previously misidentified as permission issues.

Highlights

  • URL Path Preservation: Updated parseBaseURL to include the URL path in the base URL construction, ensuring KAS services deployed under a sub-path are correctly routed.
  • Trailing Slash Handling: Added logic to trim trailing slashes from the path to prevent double slashes when the Connect client appends the procedure name.
  • Test Coverage: Updated existing unit tests and added new test cases to verify path preservation and trailing slash normalization.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


The path was lost in transit flow, / A KAS request with nowhere to go. / We kept the route, the slash we trimmed, / Now traffic flows as we have hymned.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@elizabethhealy, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 11e7eb8e-df9d-42eb-b179-21587061b40a

📥 Commits

Reviewing files that changed from the base of the PR and between 1903ac5 and a2d31ee.

📒 Files selected for processing (2)
  • sdk/kas_client.go
  • sdk/kas_client_test.go
📝 Walkthrough

Walkthrough

parseBaseURL now preserves KAS server base paths while trimming trailing slashes. Tests cover root URLs, nested paths, ports, and trailing-slash normalization.

Changes

KAS base path preservation

Layer / File(s) Summary
Parse and validate KAS base paths
sdk/kas_client.go, sdk/kas_client_test.go
parseBaseURL includes the normalized URL path, and tests verify root, nested, port-based, and trailing-slash inputs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: dmihalcik-virtru

Poem

I’m a bunny with paths in my paws,
Keeping KAS URLs true to their laws.
No slash hops twice,
Base paths stay nice,
I wiggle my nose for the cause.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preserving the KAS base path when constructing the client URL.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch remove-kas-url-stripping

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added comp:sdk A software development kit, including library, for client applications and inter-service communicati size/xs labels Jul 21, 2026
@elizabethhealy
elizabethhealy marked this pull request as ready for review July 21, 2026 16:55
@elizabethhealy
elizabethhealy requested review from a team as code owners July 21, 2026 16:55

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the KAS client to preserve the base path of the KAS URL and trim trailing slashes, with corresponding updates and additions to the test suite. The review feedback recommends validating that the URL scheme is strictly 'http' or 'https' to avoid downstream errors with malformed URLs, and adding test cases to cover these invalid scheme scenarios.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread sdk/kas_client.go Outdated
Comment on lines +326 to +330
// Preserve the base path so a KAS served under one (e.g. https://host/kas)
// still routes; Connect appends "/procedure". Trailing slash trimmed to avoid "//".
path := strings.TrimSuffix(u.Path, "/")

return fmt.Sprintf("%s://%s%s", u.Scheme, addr, path), nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If the input URL does not contain a valid scheme (e.g., example.com/kas), url.Parse may parse the host as part of the path, leaving u.Scheme empty. Returning a URL with an empty scheme (e.g., ://example.com/kas) will cause confusing errors downstream.

We should validate that the scheme is either http or https before constructing the base URL.

	if u.Scheme != "http" && u.Scheme != "https" {
		return "", fmt.Errorf("invalid KAS URL scheme %q, expected http or https", u.Scheme)
	}

	// Preserve the base path so a KAS served under one (e.g. https://host/kas)
	// still routes; Connect appends "/procedure". Trailing slash trimmed to avoid "//".
	path := strings.TrimSuffix(u.Path, "/")

	return fmt.Sprintf("%s://%s%s", u.Scheme, addr, path), nil

Comment thread sdk/kas_client_test.go
Comment on lines +267 to +272
{
name: "Trailing slash is trimmed to avoid doubled path separator",
input: "https://example.com/dsp/",
expected: "https://example.com/dsp",
expectError: false,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To ensure the new scheme validation works correctly, we should add test cases for missing and invalid schemes.

		{
			name:        "Trailing slash is trimmed to avoid doubled path separator",
			input:       "https://example.com/dsp/",
			expected:    "https://example.com/dsp",
			expectError: false,
		},
		{
			name:        "Missing scheme returns error",
			input:       "example.com/dsp",
			expected:    "",
			expectError: true,
		},
		{
			name:        "Invalid scheme returns error",
			input:       "ftp://example.com/dsp",
			expected:    "",
			expectError: true,
		},

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdk/kas_client.go`:
- Around line 326-330: Refactor parseBaseURL to sanitize the parsed url.URL
directly: clear User, RawQuery, and Fragment, trim the trailing slash from Path,
reset RawPath, and return u.String(). Remove the manual Hostname/Port
reconstruction so IPv6 brackets, ports, path escaping, and the preserved base
path are handled by the URL implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 38ac7938-3428-4587-8a75-6742ce9b1d56

📥 Commits

Reviewing files that changed from the base of the PR and between b209678 and 1903ac5.

📒 Files selected for processing (2)
  • sdk/kas_client.go
  • sdk/kas_client_test.go

Comment thread sdk/kas_client.go Outdated
@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 225.945277ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 120.86179ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 423.638202ms
Throughput 236.05 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 51.3783973s
Average Latency 511.670144ms
Throughput 97.32 requests/second

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 218.136042ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 116.70093ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 432.235639ms
Throughput 231.36 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 49.783877467s
Average Latency 496.318628ms
Throughput 100.43 requests/second

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • examples
  • otdfctl
  • sdk
  • service
  • lib/fixtures
  • tests-bdd

See the workflow run for details.

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

Labels

comp:sdk A software development kit, including library, for client applications and inter-service communicati size/xs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant