fix: anchor the https scheme downgrade in containerFetch - #250
Open
mattjohnsonpint wants to merge 3 commits into
Open
fix: anchor the https scheme downgrade in containerFetch#250mattjohnsonpint wants to merge 3 commits into
mattjohnsonpint wants to merge 3 commits into
Conversation
The format scripts only cover src/**/*.ts and examples/**/*.{ts,json,jsonc},
so markdown, the prettier config itself, and the plain JS example servers
had drifted from the repo style.
Changes are cosmetic: trailing newlines, blank lines around markdown lists,
quote style, and stray trailing whitespace. The two example server.js files
were verified to parse as ES modules and to be identical modulo quotes,
whitespace, and semicolons.
Point the format scripts at "." and let .prettierignore decide what is excluded, instead of maintaining a narrow glob list that silently let markdown, config, and plain JS drift. .prettierignore already covers node_modules, dist, the lockfile, generated worker types, .wrangler, .changeset, and CHANGELOG.md, so changesets and generated files stay unchecked. This widens the set of files CI enforces from TypeScript plus example JSON to also include markdown, yaml, and JS. The repo is clean under the new scope.
The scheme downgrade applied before forwarding to the container used an unanchored string replace. String.prototype.replace rewrites only the first match, so when the request URL was already http the first https: in the path, query, or fragment was downgraded instead of the scheme. This corrupts parameters carrying an absolute URL, for example /callback?redirect=https://app.example.com, which arrives at the container as redirect=http://app.example.com. Percent-encoded values were unaffected. Relative paths resolve against http://container, so every relative-path call met the condition once #238 made them usable. Anchoring the match leaves the intended https-to-http downgrade intact, since tcpPort.fetch opens a raw TCP connection that does not terminate TLS.
commit: |
Contributor
|
LGTM |
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.
Note
Stacked on #249. Until that merges, the diff here also shows the two formatting commits; it will reduce to the single fix commit after a rebase. Remove this note then.
Summary
https:in query strings and fragmentsWhy
containerFetchdowngrades https to http before forwarding, becausetcpPort.fetchopens a raw TCP connection to the container and nothing terminates TLS. That rewrite used an unanchored string replace:String.prototype.replacewith a string pattern replaces only the first occurrence, and the match isn't anchored to the start. When the URL's scheme is alreadyhttp:, the firsthttps:in the string is somewhere in the path, query, or fragment — and that gets rewritten instead of the scheme.So this:
arrives at the container as:
Any endpoint taking an unencoded absolute URL as a parameter is affected — OAuth
redirect_uri, webhook callbacks, proxy targets, SSORelayState. Percent-encoded values (https%3A%2F%2F…) are unaffected, since they don't contain the literalhttps:.Absolute
https://URLs were never affected: the first match is the scheme, so the replacement did the right thing and the rest of the URL survived.Relationship to #238
The bug is pre-existing, but #238 widened its reach considerably. Relative paths now resolve against
http://container, which means every relative-path call has anhttp:scheme — precisely the condition that triggers this. Before #238, relative paths threw outright, so the corruption was largely unreachable.Approach
Anchoring the regex is the whole fix. I also considered operating on the parsed URL:
new URL(request.url).hrefround-tripsrequest.urlexactly (verified across default ports, empty paths, and percent-encoded characters), so the two are equivalent. The anchored regex wins on being a one-token change with no normalization risk and no extra allocation in the request-proxy hot path.The comment above the line explains why the anchor matters, since the obvious "simplification" back to a string argument reintroduces the bug silently.
Tests
Two cases in
src/tests/container.test.ts:https:in query strings and fragments — the regression. Fails onmain, passes here.https:query param on the same URL. This case was already correct; it's pinned so an over-eager future fix can't disable the downgrade entirely.Only the first fails without the change, which is expected — the second documents behavior that already worked.