← WebKit — 2026-W37

944f82d72ae743274528335473aeb376a2d77b4d  Add the Local Network Access check algorithm

severity medium class PolicyBypass confidence 0.3 signal score 14.81 Source/WebCore/dom
Primitive: Local Network Access (RFC1918/PNA) control enforcing secure-context + permissions-policy + permission before public pages reach local/loopback addresses.
Triage note: New LocalNetworkAccess.{h,cpp} plus NetworkResourceLoader::checkLocalNetworkAccess and parameter plumbing implement the LNA address-space check. This is a security boundary but is new feature implementation (new files, new preference, acknowledged FIXMEs), not a silent fix of a specific identified bug.
Structural signals: smartptr_member_added (+6.47), null_guard (+2.59), uaf_lifetime (+4.75), msg_soft (+1.0), area:Source/WebCore/dom (+2.0), large_churn (-2.0)

Commit message

Add the Local Network Access check algorithm
https://bugs.webkit.org/show_bug.cgi?id=319907
rdar://182830329

Reviewed by Alex Christensen.

Feature work for Local Network Access (https://wicg.github.io/local-network-access/).
This adds the spec's Local Network Access check
(https://wicg.github.io/local-network-access/#local-network-access-check) as two functions in a
new WebCore file, and wires it into NetworkResourceLoader for subresource loads so the check
actually runs.

localNetworkAccessPermissionRequestOutcome() decides whether a permission request is
refused, answered from a decision the user already made, or prompted for. The order it
consults its inputs in is the security-critical part, and the tests assert it.

performLocalNetworkAccessCheck() is the check itself: it exempts same-origin
trustworthy requests, compares the connection's address space against the client's, and
consults the permission callback only when the connection is less public. Each refusal
carries its own message so the console names the real cause. The check is asynchronous
because resolving the permission may prompt.

Enforcement covers subresources only: response, redirect and cache hit. Main resources are
skipped, which is correct for top-level navigations but not for iframes -- an iframe navigation
has to be judged against its initiator rather than the document being navigated away from, and
that needs NavigationRequester to carry the initiator's address space. The permissions-policy
features are likewise left at their default of allowed. Both are marked with FIXMEs.

NetworkSession::requestLocalNetworkAccessPermission resolves the decision, and is where the
ordering matters. Two of its four outcomes are not yet reachable in production: nothing writes
the grant map, and every client on this path is a document so prompting is always possible. The
grant store, the prompt, and revocation land next; until they do, an origin that could be
prompted is refused rather than allowed.

One deviation from the spec, and one from the WPT suite:

- IPAddressSpace::Unknown has no spec equivalent, since the spec's classification always
  returns a value and falls back to Public. WebKit needs the distinction because a peer
  address is not always available to classify, and Public would fail open: it is never
  less public than the client, so the check would be skipped.

- Loopback reached from a local-network client requires permission here, per section
  2.1's ordering. The WPT files are written to what current implementations do, which
  section 2.2 records as restricting only public-to-local and public-to-loopback
  requests, so they assert the opposite.

LocalNetworkAccessEnabled becomes a sharedPreferenceForWebProcess so the network process reads
it from the UI process rather than from the web process, which is not trusted to report whether
the feature is on. clientAddressSpace and clientIsSecureContext still come from the web process
because they are the client's policy container state, which the network process cannot reconstruct
without policy container inheritance; the residual gap is recorded as a FIXME against bug 319908.

ipc/loadping-firstpartyforcookies-message-check.html hand-builds every field of
NetworkResourceLoadParameters, so the two new fields have to be listed there too or the
message no longer decodes, as in 318732@main.

* LayoutTests/ipc/loadping-firstpartyforcookies-message-check.html:
* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:
* Source/WebCore/Headers.cmake:
* Source/WebCore/dom/Document.h:
* Source/WebCore/Modules/fetch/LocalNetworkAccess.cpp: Added.
(WebCore::localNetworkAccessPermissionRequestOutcome):
(WebCore::performLocalNetworkAccessCheck):
* Source/WebCore/Modules/fetch/LocalNetworkAccess.h: Added.
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/CMakeLists.txt:
* Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h:
* Source/WebKit/NetworkProcess/NetworkResourceLoadParameters.h:
* Source/WebKit/NetworkProcess/NetworkResourceLoadParameters.serialization.in:
* Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::checkLocalNetworkAccess):
(WebKit::NetworkResourceLoader::didReceiveResponse):
(WebKit::NetworkResourceLoader::continueDidReceiveResponseAfterLocalNetworkAccessCheck):
(WebKit::NetworkResourceLoader::continueWillSendRedirectedRequestAfterContentFiltering):
(WebKit::NetworkResourceLoader::continueWillSendRedirectedRequestAfterLocalNetworkAccessCheck):
(WebKit::NetworkResourceLoader::didRetrieveCacheEntry):
(WebKit::NetworkResourceLoader::continueDidRetrieveCacheEntryAfterLocalNetworkAccessCheck):
* Source/WebKit/NetworkProcess/NetworkResourceLoader.h:
* Source/WebKit/NetworkProcess/NetworkSession.cpp:
(WebKit::NetworkSession::requestLocalNetworkAccessPermission):
* Source/WebKit/NetworkProcess/NetworkSession.h:
* Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp:
(WebKit::addParametersShared):
* Tools/TestWebKitAPI/Tests/WebCore/LocalNetworkAccess.cpp: Added.

Canonical link: https://commits.webkit.org/320971@main

Before / after

Loading diff…