From minified
to magnified

🔍

What is a source map?

Source map

  • maps the minified code  to the original
  • created during a build process

Visualization

Format

{
  "version": 3,
  "sources": [
    "/Users/martin.nuc/programming/temp/sourcemaps/src/second.ts",
    "/Users/martin.nuc/programming/temp/sourcemaps/src/index.ts"
  ],
  "sourcesContent": [
    "export const result = 1 + 5;",
    "import { result } from './second';\n\nconsole.log('ahoj', result)\n\nsetName({}, 'Martin');\nsetName({}, 'Lukas');\n\nfunction setName(person, name) {\n  let magicNumber = 12345;\n  person.number = magicNumber;\n  magicNumber++\n  person.name = name + magicNumber;\n}"
  ],
  "names": [],
  "mappings": "AAAO,IAAM,SAAS;ACOtB,SAAS,EAAQ,CAAM,EAAE,CAAI;IAC3B,IAAI,IAAc;IAClB,EAAO,MAAM,GAAG,GAChB,KACA,EAAO,IAAI,GAAG,IAAO;AACvB;AAVA,QAAQ,GAAG,CAAC,iBAEZ,EAAQ,CAAC,GAAG,WACZ,EAAQ,CAAC,GAAG"
}

Mappings

  • ; = separates lines
  • , = separates segments
  • segments are 1, 4 or 5 size long
  • build on top of the previous segment
  • encodes the file, line and column using Base64 VLQ
AAAO,IAAM,SAAS;ACOtB,SAAS,EAAQ,CAAM,EAAE,CAAI;IAC3B,IAAI,IAAc;IAClB,EAAO,MAAM,GAAG,GAChB,KACA,EAAO,IAAI,GAAG,IAAO;AACvB;AAVA

Size of source map

  • mappings is a big string
  • v1 ~10x the generated code
  • v2 ~5x the generated code
  • v3 ~2.5x the generated code

How to use source maps?

1. comment

... generated file ...
//# sourceMappingURL=/path/to/file.js.map

2. HTTP header

SourceMap: <url>

Configure build tool

new webpack.SourceMapDevToolPlugin({
  // append: '\n//# sourceMappingURL=https://example.com/file.js',
  publicPath: 'https://example.com/',
  filename: '[file].map',
});

Source maps are language agnostic

Be careful

‼️

‼️

Never make source maps public!

{
  "version": 3,
  "sources": [
    "/Users/martin.nuc/programming/temp/sourcemaps/src/second.ts",
    "/Users/martin.nuc/programming/temp/sourcemaps/src/index.ts"
  ],
  "sourcesContent": [
    "export const result = 1 + 5;",
    "import { result } from './second';\n\nconsole.log('ahoj', result)\n\nsetName({}, 'Martin');\nsetName({}, 'Lukas');\n\nfunction setName(person, name) {\n  let magicNumber = 12345;\n  person.number = magicNumber;\n  magicNumber++\n  person.name = name + magicNumber;\n}"
  ],
  "names": [],
  "mappings": "AAAO,IAAM,SAAS;ACOtB,SAAS,EAAQ,CAAM,EAAE,CAAI;IAC3B,IAAI,IAAc;IAClB,EAAO,MAAM,GAAG,GAChB,KACA,EAAO,IAAI,GAAG,IAAO;AACvB;AAVA,QAAQ,GAAG,CAAC,iBAEZ,EAAQ,CAAC,GAAG,WACZ,EAAQ,CAAC,GAAG"
}

‼️

Our approach: VPN

  • hide sourcemaps in S3 bucket accessible via VPN

Browser

S3 with sourcemaps

HTML, JS, CSS

Accessible only via VPN

The end of our story

  1. Impersonation
  2. Source maps via VPN

References

From minified to magnified

By Martin Nuc

From minified to magnified

  • 120