Last November Apple launched the new web version of the App Store with source maps switched on. Within hours somebody had the whole Svelte and TypeScript frontend on GitHub. Nobody hacked anything. The developer who posted it used a browser extension that saves all resources of a page and the maps were simply among them. My first thought was that this is the kind of slip one team makes once. I believed the production build of any serious shop strips the maps or at least keeps them off the public server. I never checked that belief, so this time I decided to count.
A source map is a JSON file the bundler writes next to the compiled JavaScript. With it the browser shows real file names and line numbers when something throws. The compiled file names its map in its last line, //# sourceMappingURL=app.js.map. If the map also has a sourcesContent field, it carries the full text of every original file. Then anyone with curl gets the frontend back, with folders and internal names.
The setup is small. I took the first 1000 domains of Tranco list K25GW, a pinned snapshot from 2023, so the run can be repeated on the same list. Then I wrote a crawler of about 160 lines in Python 3.12 with requests 2.31 and BeautifulSoup 4.15. For each domain it fetches the homepage and takes up to 12 external scripts. In each script it looks for sourceMappingURL, fetches the map and checks whether sourcesContent has anything in it. The run takes about 4.5 minutes in 24 threads. I kept the paths of the files and did not keep their contents.
Of the 1000 domains, 686 answered with a page and 581 of those served external JavaScript. 206 shipped at least one sourceMappingURL, that is 30 percent of the sites that answered. The HTTP Archive counted 17 to 18 percent across their crawl in 2019. They counted only first party scripts and I counted every script on the page, so it is not the same measurement. 87 sites gave me a map with source text inside. Some maps are served under several domains of one owner. Counted once each, there are 199 maps, together about 182 megabytes of source text in 35466 files.
Where my count fell apart
87 looked like a clean headline, but it was wrong. The first map I opened from Trello had files named <anon> and <prelude>, which is bundler glue. So I added a rule. A site leaks its own code if a map has files under src/, app/, components/, pages/ or lib/. That gave 77. Then I noticed the rule also matched folders inside node_modules. After I dropped those together with vendor folders and public CDN hosts it became 67. I almost wrote 67 into this text.
Then I read the paths of the 67 site by site. The rule did not hold. Among Coursera’s “own” files were ../../src/client.ts and ../../src/eventbuilder.ts, served from browser.sentry-cdn.com. That is the Sentry SDK with its own src folder. Coursera does have its own code in other maps, but these 2 files were not it. The University of Washington was Bootstrap and Popper only. Mirror.co.uk was the client part of Next.js. The EPA and NOAA each had 1 file, src/index.js. It came from a small library that Drupal ships in its core.
So I sorted all 87 by hand, by the host that served the map and by whose code the paths are. The verdicts are in a file next to the crawler, one line per site, so I can argue with myself later. A second pass moved 6 sites between groups, which shows how soft this split is.
- 55 sites serve maps with their own code, from their own domain or their own CDN.
- 19 sites only leak through an embedded vendor script. Sentry’s CDN on HBO Max and savefrom.net, SpeedCurve’s monitoring script on the LA Times and Nikkei, a Trustpilot widget, a HubSpot menu, ad scripts.
- 13 sites only expose public open source libraries or framework code.
Several of the 55 belong to one owner. Atlassian serves the same bundle on 3 of its domains, bitbucket.org among them, about 3400 files with paths like services/wac-web/src/utils/feature-flags/check-feature-flag.ts. Trello’s maps come from the same Atlassian infrastructure. BuzzFeed and HuffPost share private @buzzfeed packages. Counted by owner it is 47. The 55 sites are 8.0 percent of the sites that answered.
Who it is
Among them are GitHub, Atlassian, Trello, Goodreads, Eventbrite, Wix, Tencent, JD.com, Nvidia, Siemens, AMD. Also Red Hat, the Daily Mail, the Guardian, Deutsche Welle, weather.com and a few universities. GitHub’s maps from githubassets.com have 786 files, with paths like packages/client-env/client-env.ts from their monorepo. The Daily Mail paths still contain the working folder of a TeamCity build agent. These are big companies with their own build infrastructure, TeamCity agents and monorepos, but I do not know how the maps got to the public server on any of them. My guess is the usual one: the map is built for an error tracker and then deployed together with the bundle. But I did not check this.
The crawler cannot read intent either. freeCodeCamp and WordPress serve maps of code that is public anyway. For them it may be a deliberate choice. The Bootstrap site is the same case. Font Awesome’s maps carry the source of its paid Pro icon packages. Some of the others probably also decided that a frontend is not a secret, which is a reasonable position. But the 55 mixes both cases and I cannot separate them from outside.
The server does not help. Leaking maps came back with 7 different content types, from application/json to text/plain. One map had no type at all. None of them needed cookies or a login.
What I did not check
Only the homepage and at most 12 scripts per site. Inner pages and lazy chunks were not touched, so 55 is a floor. Nothing behind a login was touched either. I did not look inside the files for keys or tokens, because I wanted the size of the exposure. The crawl ran once from one machine. A site can switch its maps off any day.
The check for my own deploys is one request. Take any script from the production page and read its last line. If there is a sourceMappingURL, try to fetch the map. A 404 or a map without sourcesContent is fine. If the map has text in sourcesContent, my crawler would have counted the site too.