fix: allow cleartext http to loopback only - #9782
Draft
mikhail-dcl wants to merge 1 commit into
Draft
Conversation
…ttps unless the request explicitly opts in (local-scene development) ## Problem `InvalidOperationException: Insecure connection not allowed` on every wearable main-file / profile / content fetch whenever the request URL scheme is `http`. Sentry UNITY-EXPLORER-P42 (105 ev/wk) + P4G (76 ev/wk), ongoing since May. Every sampled event is the Creator Hub / sdk-commands local preview flow (`http://127.0.0.1:8000`): release builds systematically break the officially supported local-preview path for every creator. ## Root cause The client's transport-security policy lives solely in Unity's baked, global `insecureHttpOption: NotAllowed`, which cannot express the policy the product needs — cleartext is fine to loopback (local preview, sidecars), never to real hosts. Not a wearables bug: the wearable frame is just the highest-volume victim of the missing scheme policy at the web-request choke point. ## Fix 1. `ProjectSettings.asset`: `insecureHttpOption` → AlwaysAllowed; enforcement moves into the client layer. 2. `WebRequestUtils`: pure static policy — `EnforceSecureScheme(url)` + `IsForbiddenCleartext(url)`. Non-http schemes untouched (zero-alloc https fast path); http+loopback (`localhost`, 127.0.0.0/8, `[::1]` via `IPAddress.IsLoopback`) allowed; http+any other host rewritten to https (those threw before sending at the pin, so the upgrade can only equal or improve the outcome). Unparsable http URLs fail closed. 3. `RequestEnvelope.InitializedWebRequest`: secure-scheme enforcement applies to the wire URL of **every request, signed and unsigned alike** — default-deny is the property that justifies flipping the player setting to AlwaysAllowed. The single exemption is an explicit per-request opt-in: `CommonArguments.AllowInsecureCleartext` (new readonly field, trailing optional ctor param, default false), set only by the scene `fetch()` API (`SimpleFetchApiImplementation.FetchAsync`) in local-scene-development mode. That module already enforces its own dev-mode-aware gate — https required unless `isLocalSceneDevelopment` — and in dev mode a scene may fetch cleartext http to any host (LAN dev server, remote test endpoint), so the envelope must not rewrite those. Enforcement runs before signing, so a signed request's signature covers the upgraded URL and the identity auth chain never travels over forbidden cleartext; loopback http always passes through, so signed requests against a local preview realm keep working. Per-request composition (the media-converter's embedded http origin query param) is untouched. One warning log per upgrade for support triage. 4. `WebRequestController.SendAsync`: redirect-downgrade guard — `IsCleartextDowngrade(sentUrl, finalUrl)` fires only when the exchange left on an allowed scheme/host (https, or loopback http) and its final (post-redirect) URL is forbidden cleartext; checked on both success and exception paths, so a downgraded exchange is never consumed and never retried. Because every non-opted-in wire URL is upgraded to https before send, the guard covers all of that traffic; an exchange deliberately sent as cleartext (an opted-in local-scene-development fetch) is not a downgrade and is not blocked. This is weaker than the old player setting's pre-send block — Unity's internal redirect handling has already sent the downgraded hop by the time the guard sees it; a true per-hop block would need `redirectLimit = 0` + manual redirect handling for signed requests (follow-up). The guard throws a distinct "Insecure redirect blocked" message so these events don't land in the P42/P4G buckets. 5. `WebRequestUtils.IsLocalhost`: reimplemented on top of the policy's single loopback definition (`Uri`-parsed; http/https + `localhost`/127.0.0.0/8/`[::1]`) — the old prefix check missed 127.0.0.0/8 beyond 127.0.0.1 (rerouting loopback textures through the public media converter, which cannot reach the creator's machine) and false-positived on `localhost.evil.com`. 6. Bypass sites the settings flip would otherwise leave un-gated now apply the same policy: `UrlResolverService` (scene-controlled media URLs — the resolved URL itself is upgraded, so reachability probes and playback agree on one wire URL, and the raw GET probe enforces the scheme at the emit site) and `AbgenSidecar` (the realm root behind its /about + catalyst fetches; loopback LSD preview roots pass through unchanged). Known behavior change: http realms/worlds (#7827 class) now fail as a retried TLS connect error instead of a synchronous "Insecure connection not allowed" — same terminal outcome, different message, slower on probe-style paths. ## Test New EditMode `InsecureSchemePolicyShould` (40 cases): default-deny locks (`UpgradeNonLoopbackHttpToHttpsWhenUnsigned` ×2 and `UpgradeNonConvertedTextureUrlAtTheWire` — unsigned envelope/texture wire URLs are upgraded to https), the dev opt-in lock (`PassNonLoopbackHttpThroughUnchangedWhenCleartextAllowed` ×2 — with `CommonArguments.AllowInsecureCleartext` the URL comes out byte-identical), signed-request upgrade (enforce-before-sign) + signed-loopback pass-through, the `KeepPlayerSettingAlwaysAllowed` lock pairing the settings flip with the client policy, loopback pass-through combinatorial over the opt-in flag (never upgraded either way), https/file pass-through guards, the converter-embedded-origin preservation guard, `IsForbiddenCleartext` + `IsCleartextDowngrade` classification cases (downgrade, deliberate-cleartext, redirect-to-loopback), the loopback-texture-stays-direct guard (ktx enabled), and `IsLocalhost` classification cases (127.0.0.0/8, `localhost.evil.com`). Plus 3 `UrlResolverServiceShould` cases: direct non-loopback http upgraded for probe + playback, loopback http kept cleartext. ## Validation Windows Unity 6000.4 EditMode lane at the pin: RED FAIL 3 as intended (non-loopback http stayed http; setting was NotAllowed) + 7 guards passed / GREEN PASS 16/16 for the original round. Rescope round (security review): GREEN (full fix) passes all 40; RED (same tests with the envelope guard reverted to signed-only) fails exactly the 3 default-deny locks — the coverage the review flagged as dropped. Fixes #3661 Related: #8761, #8762 (same-day auto-closed dupes of P42/P4G), #8080 (MediaStream direct probe — its bypass site now applies the same policy, see Fix 6), #7827 (realm-connect variant; failure message changes as described above) Includes inspection-warning cleanup in all touched files.
Contributor
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.
upgrading other http to ttps unless the request explicitly opts in (local-scene development)
Problem
InvalidOperationException: Insecure connection not allowedon every wearable main-file / profile / content fetch whenever the request URL scheme ishttp. Sentry UNITY-EXPLORER-P42 (105 ev/wk) + P4G (76 ev/wk), ongoing since May. Every sampled event is the Creator Hub / sdk-commands local preview flow (http://127.0.0.1:8000): release builds systematically break the officially supported local-preview path for every creator.Root cause
The client's transport-security policy lives solely in Unity's baked, global
insecureHttpOption: NotAllowed, which cannot express the policy the product needs — cleartext is fine to loopback (local preview, sidecars), never to real hosts. Not a wearables bug: the wearable frame is just the highest-volume victim of the missing scheme policy at the web-request choke point.Fix
ProjectSettings.asset:insecureHttpOption→ AlwaysAllowed; enforcement moves into the client layer.WebRequestUtils: pure static policy —EnforceSecureScheme(url)+IsForbiddenCleartext(url). Non-http schemes untouched (zero-alloc https fast path); http+loopback (localhost, 127.0.0.0/8,[::1]viaIPAddress.IsLoopback) allowed; http+any other host rewritten to https (those threw before sending at the pin, so the upgrade can only equal or improve the outcome). Unparsable http URLs fail closed.RequestEnvelope.InitializedWebRequest: secure-scheme enforcement applies to the wire URL of every request, signed and unsigned alike — default-deny is the property that justifies flipping the player setting to AlwaysAllowed. The single exemption is an explicit per-request opt-in:CommonArguments.AllowInsecureCleartext(new readonly field, trailing optional ctor param, default false), set only by the scenefetch()API (SimpleFetchApiImplementation.FetchAsync) in local-scene-development mode. That module already enforces its own dev-mode-aware gate — https required unlessisLocalSceneDevelopment— and in dev mode a scene may fetch cleartext http to any host (LAN dev server, remote test endpoint), so the envelope must not rewrite those. Enforcement runs before signing, so a signed request's signature covers the upgraded URL and the identity auth chain never travels over forbidden cleartext; loopback http always passes through, so signed requests against a local preview realm keep working. Per-request composition (the media-converter's embedded http origin query param) is untouched. One warning log per upgrade for support triage.WebRequestController.SendAsync: redirect-downgrade guard —IsCleartextDowngrade(sentUrl, finalUrl)fires only when the exchange left on an allowed scheme/host (https, or loopback http) and its final (post-redirect) URL is forbidden cleartext; checked on both success and exception paths, so a downgraded exchange is never consumed and never retried. Because every non-opted-in wire URL is upgraded to https before send, the guard covers all of that traffic; an exchange deliberately sent as cleartext (an opted-in local-scene-development fetch) is not a downgrade and is not blocked. This is weaker than the old player setting's pre-send block — Unity's internal redirect handling has already sent the downgraded hop by the time the guard sees it; a true per-hop block would needredirectLimit = 0+ manual redirect handling for signed requests (follow-up). The guard throws a distinct "Insecure redirect blocked" message so these events don't land in the P42/P4G buckets.WebRequestUtils.IsLocalhost: reimplemented on top of the policy's single loopback definition (Uri-parsed; http/https +localhost/127.0.0.0/8/[::1]) — the old prefix check missed 127.0.0.0/8 beyond 127.0.0.1 (rerouting loopback textures through the public media converter, which cannot reach the creator's machine) and false-positived onlocalhost.evil.com.UrlResolverService(scene-controlled media URLs — the resolved URL itself is upgraded, so reachability probes and playback agree on one wire URL, and the raw GET probe enforces the scheme at the emit site) andAbgenSidecar(the realm root behind its /about + catalyst fetches; loopback LSD preview roots pass through unchanged).Known behavior change: http realms/worlds (#7827 class) now fail as a retried TLS connect error instead of a synchronous "Insecure connection not allowed" — same terminal outcome, different message, slower on probe-style paths.
Test
New EditMode
InsecureSchemePolicyShould(40 cases): default-deny locks (UpgradeNonLoopbackHttpToHttpsWhenUnsigned×2 andUpgradeNonConvertedTextureUrlAtTheWire— unsigned envelope/texture wire URLs are upgraded to https), the dev opt-in lock (PassNonLoopbackHttpThroughUnchangedWhenCleartextAllowed×2 — withCommonArguments.AllowInsecureCleartextthe URL comes out byte-identical), signed-request upgrade (enforce-before-sign) + signed-loopback pass-through, theKeepPlayerSettingAlwaysAllowedlock pairing the settings flip with the client policy, loopback pass-through combinatorial over the opt-in flag (never upgraded either way), https/file pass-through guards, the converter-embedded-origin preservation guard,IsForbiddenCleartext+IsCleartextDowngradeclassification cases (downgrade, deliberate-cleartext, redirect-to-loopback), the loopback-texture-stays-direct guard (ktx enabled), andIsLocalhostclassification cases (127.0.0.0/8,localhost.evil.com). Plus 3UrlResolverServiceShouldcases: direct non-loopback http upgraded for probe + playback, loopback http kept cleartext.Validation
Windows Unity 6000.4 EditMode lane at the pin: RED FAIL 3 as intended (non-loopback http stayed http; setting was NotAllowed) + 7 guards passed / GREEN PASS 16/16 for the original round. Rescope round (security review): GREEN (full fix) passes all 40; RED (same tests with the envelope guard reverted to signed-only) fails exactly the 3 default-deny locks — the coverage the review flagged as dropped.
Fixes #3661
Related: #8761, #8762 (same-day auto-closed dupes of P42/P4G), #8080 (MediaStream direct probe — its bypass site now applies the same policy, see Fix 6), #7827 (realm-connect variant; failure message changes as described above)
Includes inspection-warning cleanup in all touched files.
Pull Request Description
What does this PR change?
Test Instructions
Steps (standard run):
metaforge explorer run XXXX # ← replace with this PR numberExpected result:
Steps (fresh account):
metaforge account create --clear metaforge explorer run XXXX # ← replace with this PR numberExpected result:
Automation (if applicable):
metaforge explorer test XXXXPrerequisites
Test Steps
Additional Testing Notes
Quality Checklist
Code Review Reference
Please review our Branch & PR Standards before submitting. It explains the automated review flow, QA/DEV approval requirements, and what each label does — especially useful for first-time contributors.