In March 2026, a researcher downloaded a single 59.8 MB file from Anthropic's public npm package and reconstructed 512,000 lines of proprietary TypeScript. No exploit. No authentication. Just one missing line in .npmignore.
The JS bundle delivered to every visitor is the most overlooked component of an organization's attack surface. It exposes internal API endpoints, authentication flows, and sometimes credentials to anyone who reads it, before a single authenticated request is made. The relevant question is not whether the bundle leaks information: it's how much of that surface defenders have already mapped before an attacker does.
Minification Is a Disguise, Not a Protection
Minification removes whitespace and renames local variables. It does not encrypt logic, does not hide strings, and does not obfuscate endpoint paths. Any attacker with DevTools open and access to public tooling can reverse it trivially.
JS beautifiers like jsbeautify restore the readable structure of a minified bundle in under a second. Literal strings such as API base URLs, endpoint paths, error messages, and service names survive minification unchanged. webpack injects comments with dependency names and versions into the runtime, enabling CVE fingerprinting without a single request to the server.
Tools like LinkFinder and SecretFinder operate on regex patterns, not variable names. Variable renaming only slows human review: for automated endpoint and secret scanners, a minified bundle is as readable as the original source. Error handler branches in the bundle reveal internal service names, API gateway host patterns, and stack traces even when the production error page suppresses every detail. The gap between minified code and real obfuscation represents decades of specialized development, and most web applications ship only the former.
Source Maps Hand Over the Original Repository
A .map file deployed to production is architecturally equivalent to publishing an entire TypeScript codebase to a public CDN. Build tool defaults make this mistake trivially easy to commit without noticing.
On March 31, 2026, the @anthropic-ai/claude-code package shipped cli.js.map (59.8 MB) publicly via npm. The root cause was Bun issue #28001: the bundler generates source maps by default even when development: false is set, and *.map was absent from .npmignore. The result was 512,000 lines of exposed TypeScript, 44 internal feature flags including KAIROS (a background agent), ULTRAPLAN, and BUDDY, plus complete architectural documentation accessible without authentication.
Atlassian's Bitbucket Cloud had the entire product frontend source available via Chrome DevTools on bitbucket.org. The researcher reported via Bugcrowd in May 2020, received a $200 bounty, and Atlassian accepted it as a "business risk" (P4). The sourcemapper tool by denandz reconstructs complete file trees from .map files, including internal directory structures not visible in any documented product API.
The Sentry Security Blog documents the full chain: .map file exposed in production, updateUserData function identified in the reconstructed source, undocumented endpoint /user/update-user-data located, account enumeration via /account/user-lookup/, hardcoded Stripe secret keys discovered in the reconstructed codebase. The fix is one line: devtool: false in webpack, or GENERATE_SOURCEMAP=false in CRA.
What the Bundle Reveals Before Any Authentication
Beyond secrets, the JS payload contains a complete map of endpoints, authentication flows, and business logic branches. Attackers get a detailed roadmap of the backend before any credential is exchanged.
HackerOne #991718 (DoD): a plaintext password for authentication on a .mil domain was stored directly in a production JavaScript file. No source map, no reverse engineering: just a string search. HackerOne #508024 (Omise): the payment processor's public and secret keys were in client-side JS, exposing the ability to create unauthorized payment tokens. HackerOne #983331 (Stripo): public and secret authentication keys in publicly accessible JavaScript, the same pattern replicated at a different company.
GraphQL code with Apollo embeds query and mutation definitions that expose the full schema: types, field names, and relationships, without requiring the /graphql endpoint to have introspection enabled. The LaunchDarkly client-side SDK exposes feature flag names and targeting rules to any user who inspects the bundle. Flags reveal unreleased product capabilities, internal A/B test endpoints, and beta segments, a business logic leak that most security programs leave out of their threat models.
Three Surfaces Most Scanners Miss
Source map exploitation is the documented and most discussed case. Service workers, distributed npm packages, and GraphQL fragment registries are three JS recon surfaces that most automated scanners and pentest checklists consistently miss.
Service workers (sw.js) are cached by the browser independently of the main bundle. They contain URL allowlists, cache manifests with every endpoint the application accesses, and in some cases authentication headers stored in browser Cache-Storage. A pentest that only inspects main.js leaves this surface entirely uncovered.
npm packages extend the attack surface beyond hosted web applications: any package that ships a production build can inadvertently include source maps. Scanners that monitor CDN hostnames do not detect source maps distributed via npm, exactly the vector in the Anthropic incident, and there is no reason to believe that case is isolated. GraphQL clients with Apollo or URQL frequently ship query fragment registries in the bundle: named fragments expose complete type hierarchies, revealing the schema even when introspection is disabled on the server.
intel.mago.team scans JS bundles, service workers, and npm packages automatically, identifying exposed endpoints, secrets, and source maps before attackers find them. Tools like SecretFinder and TruffleHog do pattern-matching for secrets in JS at CDN scale: what a researcher finds manually, automated scanners find across thousands of targets in parallel.
Eliminating the Surface Without Losing Observability
Removing source maps from public production deploys is a one-line change in any major build tool. Organizations still shipping .map files publicly are choosing an operational convenience that no modern error monitoring workflow actually requires.
Sentry, Rollbar, and Datadog support private source map uploads via authenticated API at build time. The maps never need to be served publicly for stack trace symbolication to work correctly. webpack: devtool: false in the production config. CRA: GENERATE_SOURCEMAP=false environment variable. For npm packages: the *.map entry in .npmignore is what was missing in the Anthropic incident and must be present in any package that publishes a production build.
Server-side deny rules for *.map extensions provide defense in depth when build configs are applied inconsistently across services in the same system. Secret scanning in CI with TruffleHog or gitleaks against the compiled build artifact, not just the source, catches hardcoded credentials before the bundle is published. Scanning the compiled artifact catches what injected dependencies introduce: AWS keys, Stripe tokens, and Slack webhooks present in third-party libraries never surface in scans that only cover application source.
Any attack surface management program that enumerates subdomains, scans ports, and checks certificate transparency logs while ignoring the JavaScript bundles themselves has a blind spot the size of the entire frontend. The bundle is not a black box: it is a documented artifact, served publicly, that maps the attack surface in detail and is updated with every deploy. Treating the bundle as opaque is not a gap in attacker knowledge. It is a gap in defender awareness.
Top comments (0)