Skip to content

feat!: TCP/TLS wrap-based interception#770

Open
kettanaito wants to merge 205 commits into
mainfrom
feat/net-v2
Open

feat!: TCP/TLS wrap-based interception#770
kettanaito wants to merge 205 commits into
mainfrom
feat/net-v2

Conversation

@kettanaito

@kettanaito kettanaito commented Feb 23, 2026

Copy link
Copy Markdown
Member

Changes

  • Implements a SocketInterceptor that intercepts the outgoing socket packets on the net.connect()/tls.connect() levels. The interception mostly works via a "server-side socket" instance and patching TCPWrap/TLSWrap JavaScript bindings for C++ network code (socket._handle) to pend the connection until the interceptor decides what to do with it.
  • Implements a new HttpRequestInterceptor to replace ClientRequestInterceptor.

⚠️ Breaking changes

  • getRawRequest utility has been removed. Use the initiator reference directly.
  • /utils/node and getClientRequestBodyStream has been removed. Use the initiator reference directly.

Features

  • Supports CONNECT requests (fixes Support "CONNECT" requests (in proxies) #481, closes feat(wip): support "CONNECT" request #621).
  • Supports Undici fetch and request even when they aren't global.
  • Exposes the request initiator on the "request" event listener. The initiator property points to ClientRequest, XMLHttpRequest, or Request instance that initiated the intercepted request. If no instance can be reliably deduced, points to the raw net.Socket behind the connection.
  • TLS connections now correctly emit the keylog event on the socket.
  • XMLHttpRequest (JSDOM): Correctly performs the OPTIONS preflight request based on the JSDOM implementation (previously, preflight triggered for requests that shouldn't trigger it).
  • XMLHttpRequest: Implements mock response handling in accordance to the WHATWG XMLHttpRequest specification to improve browser compliance.

Bug fixes

  • The library now executes more of the Node.js network that ever before. In fact, it executes the biggest possible amount of Node.js code. We are literally patching before the C++ bindings.
  • ClientRequest using sockets is now properly supported.
  • Now it doesn't matter how you import http/https (e.g. import * as http broke previously since we cannot patch that).
  • You can now end requests in the write callbacks. The documented limitation of Interceptors is gone.
  • Fixes an issue where the automatic Content-Type header added by Undici was not set on mocked responses.
  • XMLHttpRequest: Fixes an issue where redirect responses weren't being followed.
  • XMLHttpRequest: Fixes an issue where the "progress" event for request.upload wasn't dispatched correctly.
  • XMLHttpRequest: total response body length is now computed only from the content-length mocked response header, just like it's done natively.
  • XMLHttpRequest: The progress event is now correctly emitted on the request for chunked responses.

Todos

  • Migrate all the existing tests to the new interceptor.
  • Add a thorough compliance test suite for SocketInterceptor. Cover behaviors like socket re-usage, event emission, properties (especially on TLS sockets), etc.
  • TLS: The session event shouldn't be emitted for reused sessions. Mocks always emit it now. options.session should be respected. Afaik, if provided, that is a pointer to a previously active session.
  • Add a test for CONNECT requests (see Support "CONNECT" requests (in proxies) #481 / feat(wip): support "CONNECT" request #621). Might they be supported now?
  • Finish logging.
  • Add tests to ensure that multiple interceptors using SocketInterceptor can run at the same time without conflicting.
  • Bug: TypeError: Second argument must be a buffer. Reproduce: pnpm run test:node test/modules/http multiple times in a row. Flaky.
  • Add http compliance test that calls req.end() in the socket's connect event.
  • Support global fetch (Undici fetch) in the HttpRequestInterceptor (currently times out).
  • Ensure passthrough HTTPs for global fetch works. Our test server has an expired certificate and fetch doesn't provide the means to set rejectUnauthorized: false so requests fail.
  • Consider if HttpRequestInterceptor needs to have the response decompression and redirect following logic. It likely doesn't. Just wonder how we can implement FetchInterceptor on top of it then.
    • Decompression—no. Undici will do that for us.
    • Redirects—don't know, needs discovery.
  • Implement the initiator property on the request event.
    • http
    • XMLHttpRequest
    • fetch
    • Remove getRawRequest
  • For the browser versions of fetch and XHR interceptors, still provide initiator. Refactor what setRawRequest did before.
  • Export node and inject versions of XHR and fetch interceptors. In Node.js, HttpRequestInterceptor now intercepts everything and more specialized interceptors only provide the initiator via async context. In the browser, however, the /inject version of the interceptors has to be used instead.
  • Remove controller.errorWith() because it's more semantically valid to use socket.destroy(). It provides no additional functionality and is a 1-to-1 replacement.
  • applyPatch: Use Symbol.for() instead of Symbol() to account for isolated environments (e.g. browser extensions (see there are problems while multiple browser extension use new XMLHttpRequestInterceptor() #771).
  • Add export conditions for fetch/XHR interceptors:
    • In the browser, resolve to the patch version.
    • In Node.js, use the /node.ts version (more powerful).
  • "finish" is not emitted when the socket writes end. A bug. Causes the related test to fail and also results in "Premature close" from some XHR scenarios since close while writable means premature close.
  • Add lazy response/unhandledRequest listener forwarding by checking this.emitter.listenerCount(event) inside the request event attached to the nested interceptor singleton.
  • Refactor interceptorRegistry from module scope to globalThis.
  • Modify passthrough HTTP headers only if the request headers were changed in the request listener.
  • BatchInterceptor.symbol reassignment in constructor affects the entire class, not just this instance.
  • Make request processing conditional based on request listener count (the same as response). Will be useful for on-demand interceptors in MSW in the future.
  • Rewrite fetch tests to be neutral like XHR.
  • ⚠️ Migrate to use llhttp the same way Undici uses it. That's the most performant and reliable way of introducing a parser to the stack. (feat: use llhttp parser #781)
  • Bug: Undici doesn't emit free on the socket, it uses an internal pool. SocketInterceptor never transitions into a PENDING state on the subsequent request.

@kettanaito kettanaito marked this pull request as ready for review May 1, 2026 18:03
@socket-security

socket-security Bot commented May 3, 2026

Copy link
Copy Markdown

mikicho and others added 2 commits July 6, 2026 20:33
@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @noble/hashes is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/superagent@10.3.0npm/vitest@4.1.9npm/@noble/hashes@1.8.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@noble/hashes@1.8.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @noble/hashes is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/superagent@10.3.0npm/vitest@4.1.9npm/@noble/hashes@1.8.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@noble/hashes@1.8.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm css-tree is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/vitest@4.1.9npm/css-tree@3.2.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/css-tree@3.2.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm data-urls is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/vitest@4.1.9npm/data-urls@7.0.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/data-urls@7.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm happy-dom is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package.jsonnpm/happy-dom@20.8.3

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/happy-dom@20.8.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm jsdom is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/vitest@4.1.9npm/jsdom@28.1.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/jsdom@28.1.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm jsdom is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/vitest@4.1.9npm/jsdom@28.1.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/jsdom@28.1.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants