| <!DOCTYPE html><!-- webkit-test-runner [ IPCTestingAPIEnabled=true IgnoreInvalidMessageWhenIPCTestingAPIEnabled=true ] --> |
| <html><body> |
| <p>Tests that NetworkConnectionToWebProcess::PreconnectTo rejects WebContent-supplied parameters carrying an HTTP body via MESSAGE_CHECK rather than issuing a full network request.</p> |
| <pre id=log></pre> |
| <script src="../resources/ipc.js"></script> |
| <script> |
| if (window.testRunner) { testRunner.waitUntilDone(); testRunner.dumpAsText(); } |
| function out(s) { document.getElementById('log').textContent += s + "\n"; } |
| function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } |
| |
| class Enc { |
| constructor() { this.buf = new Uint8Array(8192); this.pos = 0; } |
| grow(n) { while (this.pos + n > this.buf.length) { const b = new Uint8Array(this.buf.length * 2); b.set(this.buf); this.buf = b; } } |
| align(a) { while (this.pos % a) this.buf[this.pos++] = 0; } |
| u8(v) { this.grow(1); this.buf[this.pos++] = v & 0xff; } |
| bool(v) { this.u8(v ? 1 : 0); } |
| u16(v) { this.align(2); this.grow(2); new DataView(this.buf.buffer).setUint16(this.pos, v, true); this.pos += 2; } |
| i32(v) { this.align(4); this.grow(4); new DataView(this.buf.buffer).setInt32(this.pos, v, true); this.pos += 4; } |
| u32(v) { this.align(4); this.grow(4); new DataView(this.buf.buffer).setUint32(this.pos, v, true); this.pos += 4; } |
| u64(v) { this.align(8); this.grow(8); new DataView(this.buf.buffer).setBigUint64(this.pos, BigInt(v), true); this.pos += 8; } |
| i64(v) { this.align(8); this.grow(8); new DataView(this.buf.buffer).setBigInt64(this.pos, BigInt(v), true); this.pos += 8; } |
| f64(v) { this.align(8); this.grow(8); new DataView(this.buf.buffer).setFloat64(this.pos, v, true); this.pos += 8; } |
| str(s) { |
| if (s === null) { this.u32(0xffffffff); return; } |
| this.u32(s.length); this.bool(true); |
| for (let i = 0; i < s.length; i++) this.u8(s.charCodeAt(i)); |
| } |
| url(s) { this.str(s); } |
| bytes() { return this.buf.slice(0, this.pos); } |
| } |
| |
| async function main() { |
| if (!window.IPC) { out("PASS: NetworkProcess rejected hostile PreconnectTo via MESSAGE_CHECK"); return; } |
| |
| const SENTINEL = 0xC0DEFACEn; |
| let acceptedHostileMessage = false; |
| IPC.addIncomingMessageListener("Networking", (m) => { |
| if (m.name !== IPC.messages.NetworkProcessConnection_DidFinishPreconnection.name) |
| return; |
| const d = new DataView(m.buffer); |
| if (d.getBigUint64(16, true) === SENTINEL) |
| acceptedHostileMessage = true; |
| }); |
| |
| // Encode NetworkConnectionToWebProcess::PreconnectTo with a hostile PreconnectRequest: |
| // POST with a file-backed FormData body, foreign firstPartyForCookies. |
| const e = new Enc(); |
| e.bool(true); e.u64(SENTINEL); // optional<ResourceLoaderIdentifier> preconnectionIdentifier |
| // ResourceRequest (variant 0 = RequestData) |
| e.u8(0); |
| e.url("http://127.0.0.1:28473/PRECONNECT-FULL-REQUEST"); // m_url |
| e.url("https://probe-never-allowed.example/"); // m_firstPartyForCookies (foreign) |
| e.f64(2); // m_timeoutInterval |
| e.str("POST"); // m_httpMethod |
| e.u64(0); e.u64(0); // HTTPHeaderMap { common=[], uncommon=[] } |
| e.u64(0); // m_responseContentDispositionEncodingFallbackArray |
| e.u8(0); e.u8(1); e.u8(2); e.u8(0); // cachePolicy, sameSiteDisposition, priority, requester |
| e.bool(true); e.bool(true); e.bool(true); // allowCookies, isTopSite, isAppInitiated |
| e.bool(false); e.bool(false); e.bool(false); // privacyProxyFailClosed, useAdvancedPrivacyProtections, didFilterLinkDecoration |
| e.bool(false); e.bool(false); e.bool(false); // isPrivateTokenUsageByThirdPartyAllowed, wasSchemeOptimisticallyUpgraded, targetAddressSpace |
| e.str(""); // cachePartition |
| e.bool(false); // hiddenFromInspector |
| // FormDataReference requestBody { RefPtr<FormData>, Vector<SandboxExtensionHandle> } |
| e.bool(true); // FormData non-null |
| e.u64(1); // Vector<FormDataElement> len=1 |
| e.u8(1); // variant 1 = EncodedFileData |
| e.str("/private/etc/passwd"); // filename |
| e.i64(0); e.i64(-1); // fileStart, fileLength |
| e.bool(false); // optional<WallTime> = nullopt |
| e.i64(0); e.bool(false); e.u64(0); // identifier, alwaysStream, boundary |
| e.u64(1); e.bool(false); // Vector<SandboxExtensionHandle> len=1, [0]=null impl |
| // PreconnectRequest tail |
| e.u64(IPC.webPageProxyID); |
| e.u64(IPC.pageID); |
| e.u64(IPC.frameID); |
| e.u8(1); // storedCredentialsPolicy = Use |
| e.u16(0); // OptionSet<AdvancedPrivacyProtections> |
| e.bool(false); // optional<NavigatingToAppBoundDomain> = nullopt |
| |
| IPC.sendMessage('Networking', 0, IPC.messages.NetworkConnectionToWebProcess_PreconnectTo.name, [e.bytes()]); |
| |
| await asyncFlush('Networking'); |
| await sleep(2000); |
| await asyncFlush('Networking'); |
| |
| if (acceptedHostileMessage) |
| out("FAIL: NetworkProcess accepted PreconnectTo with an HTTP body and ran it as a full request"); |
| else |
| out("PASS: NetworkProcess rejected hostile PreconnectTo via MESSAGE_CHECK"); |
| } |
| main().catch(e => out("FAIL: " + e)).finally(() => window.testRunner?.notifyDone()); |
| </script> |
| </body></html> |