У лістападзе мінулага года Apple запусціла новую вэб-версію App Store з уключанымі source maps. Праз некалькі гадзін увесь фронтэнд на Svelte і TypeScript ляжаў на GitHub. Ніхто нічога не ўзломваў. Распрацоўшчык, які яго выклаў, скарыстаўся пашырэннем для браўзера, што захоўвае ўсе рэсурсы старонкі. Карты проста апынуліся сярод іх. Спачатку я падумаў, што гэта разавы промах адной каманды. Я быў упэўнены, што зборка для прадакшна ў любой сур’ёзнай кампаніі выкідвае карты або хаця б не кладзе іх на публічны сервер. Гэтую ўпэўненасць я ніколі не правяраў, так што цяпер вырашыў палічыць.
Source map (карта зыходнікаў) гэта JSON-файл, які бандлер піша побач з сабраным JavaScript. З яго дапамогай браўзер паказвае сапраўдныя імёны файлаў і нумары радкоў, калі вылятае памылка. У апошнім радку сабранага файла пазначана яго карта, //# sourceMappingURL=app.js.map. Калі ў карце ёсць яшчэ і поле sourcesContent, у ім ляжыць поўны тэкст кожнага зыходнага файла. Тады любы, у каго ёсць curl, аднаўляе фронтэнд цалкам, з папкамі і ўнутранымі імёнамі.
Схема простая. Я ўзяў першыя 1000 даменаў спіса Tranco K25GW. Гэта замацаваны здымак 2023 года, так што прагон можна паўтарыць на тым самым спісе. Потым напісаў краўлер на Python 3.12, прыкладна 160 радкоў, з requests 2.31 і BeautifulSoup 4.15. Для кожнага дамена ён загружае галоўную старонку і бярэ не больш за 12 знешніх скрыптоў. У кожным скрыпце шукае sourceMappingURL, спампоўвае карту і глядзіць, ці ёсць што-небудзь у sourcesContent. Прагон займае каля 4,5 хвіліны ў 24 патокі. Я захоўваў шляхі файлаў, а іх змесціва не захоўваў.
З 1000 даменаў старонкай адказалі 686. З іх 581 аддаў знешні JavaScript. У 206 быў хаця б адзін sourceMappingURL, гэта 30 працэнтаў тых, хто адказаў. У HTTP Archive у 2019 годзе налічылі ў сваім абыходзе ад 17 да 18 працэнтаў. Яны лічылі толькі скрыпты з дамена самога сайта, а я лічыў усе скрыпты на старонцы, так што гэта розныя замеры. 87 сайтаў аддалі мне карту з зыходным тэкстам унутры. Некаторыя карты ляжаць адразу на некалькіх даменах аднаго ўладальніка. Калі лічыць кожную адзін раз, атрымліваецца 199 карт, разам каля 182 мегабайт зыходнага тэксту ў 35 466 файлах.
Дзе мой лік разваліўся
Лічба 87 выглядала добрым загалоўкам, але была няправільнай. У першай карце Trello, якую я адкрыў, былі файлы з імёнамі <anon> і <prelude>, а гэта службовая абвязка бандлера. Тады я дадаў правіла. Сайт аддае свой код, калі ў карце ёсць файлы ў папках src/, app/, components/, pages/ або lib/. Выйшла 77. Потым я заўважыў, што правіла ловіць і такія папкі ўнутры node_modules. Калі я выкінуў іх разам з вендарнымі папкамі і публічнымі CDN, засталося 67. Я ледзь не напісаў 67 у гэты тэкст.
Затым я прачытаў шляхі гэтых 67 сайт за сайтам. Правіла не вытрымала. Сярод “сваіх” файлаў Coursera былі ../../src/client.ts і ../../src/eventbuilder.ts, аддадзеныя з browser.sentry-cdn.com. Гэта Sentry SDK са сваёй папкай src. Свой код у Coursera ў іншых картах ёсць, але гэтыя 2 файлы не іхнія. У Вашынгтонскага ўніверсітэта былі толькі Bootstrap і Popper. Mirror.co.uk аддаў кліенцкую частку самога Next.js. EPA і NOAA аддалі па 1 файле src/index.js. Гэта была маленькая бібліятэка, якая ўваходзіць у ядро Drupal.
Таму я разабраў усе 87 рукамі. Глядзеў на хост, з якога аддадзена карта, а яшчэ на тое, чый код ляжыць па гэтых шляхах. Вердыкты ляжаць у файле побач з краўлерам, па радку на сайт, каб потым можна было паспрачацца з самім сабой. Пры другім праходзе 6 сайтаў пераехалі ў іншыя групы. Значыць, мяжа паміж групамі ўмоўная.
- 55 сайтаў аддаюць карты са сваім кодам, са свайго дамена або свайго CDN.
- У 19 сайтаў код уцякае толькі праз чужы ўбудаваны скрыпт. Гэта CDN Sentry у HBO Max і savefrom.net, скрыпт маніторынгу SpeedCurve у LA Times і Nikkei, віджэт Trustpilot, меню HubSpot і рэкламныя скрыпты.
- 13 сайтаў паказваюць толькі публічныя бібліятэкі з адкрытым кодам або код фрэймворка.
Некаторыя з 55 маюць агульнага ўладальніка. Atlassian аддае адзін і той жа бандл на 3 сваіх даменах, сярод іх bitbucket.org, каля 3400 файлаў са шляхамі накшталт services/wac-web/src/utils/feature-flags/check-feature-flag.ts. Карты Trello прыходзяць з той самай інфраструктуры Atlassian. BuzzFeed і HuffPost карыстаюцца агульнымі прыватнымі пакетамі @buzzfeed. Па ўладальніках выходзіць 47. 55 сайтаў гэта 8,0 працэнта тых, хто адказаў.
Хто гэта
Сярод іх ёсць GitHub, Atlassian, Trello, Goodreads, Eventbrite, Wix, Tencent, JD.com, Nvidia, Siemens, AMD, а яшчэ Red Hat, Daily Mail, Guardian, Deutsche Welle, weather.com і некалькі ўніверсітэтаў. У картах GitHub з githubassets.com 786 файлаў са шляхамі накшталт packages/client-env/client-env.ts з іх монарэпазіторыя. У шляхах Daily Mail дагэтуль відаць працоўная папка агента зборкі TeamCity. Гэта вялікія кампаніі са сваёй інфраструктурай зборкі, агентамі TeamCity і монарэпазіторыямі, але як карты трапілі на публічны сервер, я не ведаю ні пра адну з іх. Мая здагадка звычайная: карту збіраюць для трэкера памылак, а потым яна трапляе ў дэплой разам з бандлам. Але я гэтага не правяраў.
Намер краўлер таксама не чытае. freeCodeCamp і WordPress аддаюць карты коду, які і так адкрыты. У іх гэта можа быць свядомым выбарам. З сайтам Bootstrap тое самае. У картах Font Awesome ляжыць зыходнік іх платных пакетаў іконак Pro. Частка астатніх, мабыць, таксама вырашыла, што фронтэнд не сакрэт. І гэта разумная пазіцыя. Але ў 55 змяшаныя абодва выпадкі, а звонку я іх не раздзялю.
Па адказе сервера іх таксама не адрозніць. Карты, што ўцяклі, прыходзілі з 7 рознымі тыпамі змесціва, ад application/json да text/plain, а адна наогул без тыпу. Ні адной не спатрэбіліся кукі або ўваход у акаўнт.
Чаго я не правяраў
Я глядзеў толькі галоўную старонку і не больш за 12 скрыптоў на сайт. Унутраныя старонкі і кавалкі бандла з адкладзенай загрузкай я не чапаў, так што 55 гэта ніжняя мяжа. За лагін я таксама не заходзіў. Унутры файлаў я не шукаў ключы і токены, бо мне быў патрэбны памер уцечкі. Прагон прайшоў адзін раз з адной машыны. Сайт можа выключыць свае карты ў любы дзень.
Праверка сваіх дэплояў займае адзін запыт. Узяць любы скрыпт з рабочай старонкі сайта і прачытаць яго апошні радок. Калі там ёсць sourceMappingURL, паспрабаваць спампаваць карту. 404 або карта без sourcesContent гэта нармальна. Калі ў sourcesContent ёсць тэкст, мой краўлер палічыў бы і гэты сайт.