hidden-source-map и загрузка карт в Sentry/Bugsnag

В Webpack параметр devtool управляет генерацией source maps — файлов сопоставления минифицированного кода с исходным. В продакшене прямое раскрытие исходников через обычные карты (source-map) создаёт риск утечки бизнес-логики, внутренних API-эндпоинтов и алгоритмов.

Режим hidden-source-map решает задачу разделения: карты генерируются, но не становятся публично доступными через браузер.

Поведение hidden-source-map

При использовании:

module.exports = {
  mode: "production",
  devtool: "hidden-source-map"
};

Webpack:

  • генерирует .map файлы для каждого бандла
  • не добавляет ссылку //# sourceMappingURL= в итоговый JS
  • не раскрывает путь к карте через браузер
  • сохраняет возможность сопоставления ошибок с исходным кодом при наличии внешнего доступа к map-файлам

Таким образом, карта существует как артефакт сборки, но не является частью публичного фронтенд-ответа.

Отличие от source-map и nosources-source-map

Режим Поведение Безопасность Отладка
source-map карта доступна браузеру низкая высокая
hidden-source-map карта не публикуется в JS средняя/высокая высокая (через внешние системы)
nosources-source-map исходники скрыты, только стектрейсы высокая ограниченная

hidden-source-map занимает промежуточную позицию: отладка переносится в сторонние системы ошибок, а не в DevTools конечного пользователя.


Архитектура использования hidden-source-map в продакшене

Типовая схема выглядит следующим образом:

  1. Webpack собирает приложение
  2. Генерируются .map файлы
  3. JS-файлы публикуются на CDN без ссылок на карты
  4. .map файлы загружаются в систему трекинга ошибок
  5. Ошибки на продакшене декодируются через загруженные карты

Ключевая идея — разделение публичного и приватного артефактов.


Интеграция с Sentry

Sentry использует source maps для преобразования minified stack trace в читаемый вид с указанием оригинальных файлов, строк и функций.

Установка плагина

npm install @sentry/webpack-plugin --save-dev

Конфигурация Webpack

const SentryWebpackPlugin = require("@sentry/webpack-plugin");

module.exports = {
  mode: "production",
  devtool: "hidden-source-map",

  plugins: [
    new SentryWebpackPlugin({
      authToken: process.env.SENTRY_AUTH_TOKEN,
      org: "my-org",
      project: "my-project",

      include: "./dist",
      release: process.env.RELEASE_VERSION,

      urlPrefix: "~/"
    })
  ]
};

Логика работы загрузки

Плагин выполняет несколько операций:

  • сканирует dist на наличие .map файлов
  • загружает их в Sentry как artifacts релиза
  • привязывает карты к конкретной версии приложения
  • после загрузки может удалять .map (опционально)

Связывание релизов и source maps

Критический момент — корректная версия сборки.

RELEASE_VERSION=1.2.3 webpack --config webpack.prod.js

И в приложении:

Sentry.init({
  dsn: "...",
  release: "1.2.3"
});

Без совпадения release source maps не будут применяться к ошибкам.


Безопасность при использовании hidden-source-map

Несмотря на отсутствие прямой ссылки на .map, сами файлы остаются чувствительными артефактами.

Риски:

  • восстановление бизнес-логики
  • раскрытие внутренних API
  • утечка feature flags и условий
  • анализ клиентской архитектуры

Практика:

  • хранение .map вне публичного CDN
  • ограниченный доступ по токену
  • автоматическое удаление после загрузки в Sentry
  • использование приватных bucket (S3/GCS)

Интеграция с Bugsnag

Bugsnag поддерживает загрузку source maps через CLI и API.

Установка CLI

npm install @bugsnag/source-maps --save-dev

Загрузка maps

npx bugsnag-source-maps upload \
  --api-key $BUGSNAG_API_KEY \
  --app-version 1.2.3 \
  --minified-url https://cdn.example.com/assets/app.js \
  --source-map dist/app.js.map \
  --minified-file dist/app.js

Связка с Webpack сборкой

Webpack конфигурация:

module.exports = {
  mode: "production",
  devtool: "hidden-source-map",
  output: {
    filename: "app.js",
    path: __dirname + "/dist",
    publicPath: "https://cdn.example.com/assets/"
  }
};

После сборки выполняется отдельный CI-этап загрузки:

webpack --config webpack.prod.js
bugsnag-source-maps upload --app-version $VERSION ...

Практика CI/CD для source maps

Типовой pipeline:

  1. Install dependencies
  2. Build Webpack bundle
  3. Generate hidden-source-map artifacts
  4. Upload maps в Sentry/Bugsnag
  5. Deploy JS без .map в публичную среду

Пример разделения этапов:

npm run build
npm run upload-sourcemaps
npm run deploy

Удаление source maps после загрузки

Для минимизации риска утечек часто применяется постобработка:

find dist -name "*.map" -type f -delete

или через Webpack plugin:

const { CleanWebpackPlugin } = require("clean-webpack-plugin");

Но чаще используется отдельный CI-степ, чтобы гарантировать успешную загрузку перед удалением.


Частые проблемы и их причины

Ошибки не декодируются в Sentry

Причины:

  • mismatch release
  • неверный urlPrefix
  • карты загружены не в тот проект
  • изменён путь файлов после сборки

Карты не находятся Bugsnag

Причины:

  • неверный minified-url
  • несоответствие CDN пути
  • отсутствует версия приложения

Утечка source maps в продакшене

Причины:

  • используется source-map вместо hidden-source-map
  • CDN кеширует .map
  • отсутствует настройка server rules

Альтернативные подходы генерации карт

Webpack поддерживает несколько режимов, используемых в зависимости от среды:

  • eval-source-map — разработка, быстрый rebuild
  • cheap-source-map — ускоренная генерация
  • inline-source-map — встроенные карты
  • hidden-source-map — продакшен с внешней загрузкой

hidden-source-map остаётся наиболее распространённым вариантом для production-инфраструктуры с внешними системами анализа ошибок.


Работа с минификацией и сопоставлением стеков

Source maps включают:

  • mapping между исходным и итоговым кодом
  • оригинальные имена функций
  • номера строк и колонок
  • контекст модулей Webpack

Минификаторы (Terser, SWC) сохраняют mapping через генерацию специальных сегментов .map структуры, которая затем интерпретируется Sentry и Bugsnag для восстановления stack trace.