fix: preserve KNOWBE4_BASE_URL path prefix when building request URLs#46
Merged
Conversation
apiRequest() built its URL with `new URL(path, creds.baseUrl)`. The
two-arg URL constructor does RFC 3986 relative resolution, not string
concatenation, and every call site passes a path-absolute reference
("/api/v1/account", ...) — which by that rule replaces the base URL's
own path entirely.
The built-in region defaults are bare origins with no path, so the bug
is invisible by default. It only bites self-hosters who point
KNOWBE4_BASE_URL at a proxy or gateway mounted under a path
(https://proxy.corp.example/knowbe4/), where the prefix was silently
dropped and every request went to the wrong place.
Join the base and path by explicit slash normalization instead, with a
comment explaining the trap. Adds regression tests that stub fetch and
assert the exact URL for bare-origin, prefixed (with and without
trailing slash), doubled-slash, and query-param cases.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
src/utils/client.tsbuilt every outbound request URL with:The two-argument
URLconstructor performs RFC 3986 relative resolution, not string concatenation. Every call site in this repo passes a path-absolute reference ("/api/v1/account","/api/v1/phishing/security_tests", …), and by that rule a path-absolute reference replaces the base URL's path component entirely.So with
KNOWBE4_BASE_URL=https://proxy.corp.example/knowbe4/:https://proxy.corp.example/knowbe4/api/v1/accounthttps://proxy.corp.example/api/v1/accountThe
/knowbe4prefix is gone and every request goes to the wrong place — silently, with no error.Why it's invisible by default
The built-in region defaults (
https://us.api.knowbe4.com,eu.,ca.,uk.,de.) are bare origins with no path, so there is nothing for relative resolution to discard. Every existing test and every default deployment is unaffected, which is exactly why this survived.It only bites self-hosters who set
KNOWBE4_BASE_URLto a reverse proxy or API gateway mounted under a path — the case the README documents as "Custom base URL (overrides region)".The fix
Explicit slash normalization plus concatenation, so the base's path survives regardless of leading/trailing slashes on either side:
The comment is there on purpose — this reads like a line begging to be "simplified" back.
Regression tests
New
describe("apiRequest URL construction")block insrc/__tests__/client.test.ts. It stubsglobalThis.fetchviavi.stubGlobal, calls the real exportedapiRequest, and asserts the exact URL stringfetchwas handed. Coverage:Env vars are set in
beforeEachand restored inafterEachalongsidevi.unstubAllGlobals().Evidence the test actually catches the bug
The fix was temporarily reverted to the original one-liner and the suite re-run:
All four path-prefix cases fail against the pre-fix code. The bare-origin case passes both before and after, as expected — that's the whole reason the bug went unnoticed.
One case is worse than "prefix dropped":
new URL("//api/v1/users", "https://proxy.corp.example/knowbe4//")resolves tohttps://api/v1/users. A leading//is a protocol-relative reference, so it replaces the host too, not just the path.With the fix restored: 64 passed (64).
Verification
The 5 lint warnings are pre-existing unused-import warnings in
src/domains/{phishing,training,users}.ts, untouched by this change. Zero errors.Scope
Swept
src/for other two-argnew URL(calls. The only other instance issrc/index.ts:627:That's inbound request parsing where relative resolution is the correct and intended behaviour. Left alone.