« Nanoskript

requests to localhost and finding open JetBrains IDEs

• 4 minute read

I was browsing the JetBrains plugin marketplace when I was presented with a warning on a plugin page:

"Not compatible with the version of your running IDE"
"Not compatible with the version of your running IDE"

How did it know I had WebStorm open? I wasn't logged into the JetBrains site and I don't have any JetBrains extensions installed on Chrome either. A glance in the network tab of Chrome's Developer Tools reveals the answer:

Network request to localhost
Network request to localhost

The page was sending a network request to localhost! Specifically, http://localhost:63342/api/installPlugin. Consequently, it was sending a network request to some server running on my local computer. Here's the response in reply:

Response from WebStorm
Response from WebStorm

It receives the version and build number of whatever IDE is running. With this, the plugin page is able to tell if the shown plugin is incompatible.11. The actual process may be a little different. The page usually issues a request to /api/installPlugin?action=checkCompatibility and WebStorm sometimes responds with the plugin's compatibility.

what is that port number?

The port number 63342 in the network request is the default port of WebStorm's integrated server. When you open an HTML file from WebStorm, the page is served through this server. That's how WebStorm is able to reload the page when the file is saved. You can see and configure the port by going to Preferences > Build, Execution, Deployment > Debugger:

Built-in server port
Built-in server port

However, this configuration option is misleading. It doesn't matter what port the integrated server is set to, WebStorm and other JetBrains IDEs will listen on 63342 anyway.22. On Unix-based systems, you can check what processes are using a port with lsof -i :$PORT. The only way to prevent WebStorm from advertising itself is to bind another server on the same port.33. If you have Python installed, you can easily start a server with python3 -m http.server $PORT. Note that on macOS, if you also bind to localhost with python3 -m http.server --bind localhost 63342, WebStorm's server will take priority. Just don't change the port to 63343 (one higher than 63342) because the plugin page also issues a request to that port if 63342 fails.

aren't network requests to localhost a security risk?

Imagine you had some web service running locally. Any webpage could send requests to localhost and extract data from those local services or even craft malicious payloads. So what stops a page from doing that?

The same way requests to other websites are allowed or blocked! Through the CORS (cross-origin resource sharing) response headers. Since localhost is considered a separate origin from (for example) malicious-site.com, any responses from the local web service must include a Access-Control-Allow-Origin header with the same origin for it to be accessible:

Access-Control-Allow-Origin response header
Access-Control-Allow-Origin response header

Normally requests that are considered simple do not need an additional preflight request (an initial request sent to ensure that the server understands CORS). However, Chrome sends a special preflight request for localhost origins and requires the server to include the Access-Control-Request-Private-Network header in the response for it to be accessible. As of Chrome 108, this does not seem to be enforced and instead a warning appears in the console if the header is invalid:

Private network request warning
Private network request warning

More details on this behavior can be found in Chrome's feature documentation.

trying it ourselves

What happens when we try to access an open JetBrains IDE's version information? If you have one installed and running you can see for yourself by clicking the button below. Make sure you're using either Chrome or Firefox (sorry Safari users!)44. On Safari, the request errors in the console with "The certificate for this server is invalid." .

A dialog will appear:

JetBrains IDE request dialog
JetBrains IDE request dialog

If you select Yes, the IDE name and build number will be sent back in the response. If you select No, an empty response body will be sent back but the name can still be found in the Server response header. However, since the Server header is not considered a safe CORS header, it cannot be read by page scripts. Importantly, the IDE asks the user before allowing a page from an unknown origin to access its API.

conclusion

In this post we've discussed how the JetBrains plugin marketplace detects what IDE is running, how network requests can be sent to localhost, and what is needed to make them succeed. Now you can try writing your own localhost network requests!

footnotes

  1. The actual process may be a little different. The page usually issues a request to /api/installPlugin?action=checkCompatibility and WebStorm sometimes responds with the plugin's compatibility.

  2. On Unix-based systems, you can check what processes are using a port with lsof -i :$PORT.

  3. If you have Python installed, you can easily start a server with python3 -m http.server $PORT. Note that on macOS, if you also bind to localhost with python3 -m http.server --bind localhost 63342, WebStorm's server will take priority.

  4. On Safari, the request errors in the console with "The certificate for this server is invalid."

contents