Hi there!

Another weekend, another blog. If you're new here, I go by @iamaangx028 on the internet 👋

Just a small note for continuity…

So far, we've explored several important topics that lay the foundation for understanding web security. If you're new here, check out the earlier blogs for better continuity. Last week, we dug into React optimization techniques — code splitting, lazy loading, bundling, and the security risks of exposed source maps.

This week, we'll start to learn more about JavaScript enumeration. As I mentioned earlier, I'm learning from different resources and building my v1 methodology for JS enumeration. Most of this will eventually be part of that methodology, so blogs might be shorter, but I'll try to include as much detail as possible. This week, we'll touch on the surface of modern bundlers — Vite, Webpack, and Rollup — and dive into Redux state management concepts.

None
Let's Start Week — 14

When you're analyzing a target application, understanding which bundler it uses can reveal a lot about the tech stack, potential vulnerabilities, and even configuration mistakes. Let's break down the three most popular bundlers you'll encounter.

Vite — The Speed Demon

Vite is a modern bundler known for its speed and hot reload capabilities. It's become incredibly popular in recent years, especially in Vue 3 and modern React apps, because it uses native ES modules during development and Rollup under the hood for production builds.​

Think of Vite like a chef with a perfectly organized kitchen — everything is exactly where it needs to be, so cooking (building) happens instantly. While traditional bundlers like Webpack rebuild the entire app on every change, Vite only rebuilds what changed, making development feel instantaneous.​

Webpack — The Classic Workhorse

Webpack is the veteran bundler that's been around for years. It can combine CSS, JS, and images into one or a few bundled files. You'll find Webpack powering most React apps created with Create React App and many Angular projects.​

Webpack is like a factory assembly line — it takes all your raw materials (source files) and processes them through various loaders and plugins, ultimately producing optimized bundles. It's powerful and configurable, but that complexity also means it can be slower than modern alternatives.​

Rollup — The Library Specialist

Rollup is another bundler that can bundle CSS, JS, and images into a few optimized bundles. It's particularly popular for library packaging rather than full applications. Many npm packages you use daily are built with Rollup because it produces cleaner, more efficient code for libraries.​

Think of Rollup like a precision tool — it's designed for one job and does it exceptionally well. While Webpack is a Swiss Army knife, Rollup is more like a surgeon's scalpel.​

Fingerprinting Bundlers from Minified JavaScript

Now here's where it gets interesting for bug hunters. When you're analyzing a target, you can identify which bundler was used just by looking at the minified JavaScript. Each bundler leaves unique fingerprints in the production code.​

Identifying Webpack

Webpack has some very distinctive patterns that make it easy to spot:

Wrapper Function Pattern:

(()=>{var __webpack_modules__ = ({ ... });
(() => { var __webpack_exports__ = {};

Chunk Loading Pattern:

self["webpackChunkappname"] = self["webpackChunkappname"] || []).push([...])
(self.webpackChunk || []).push(...)

Source Map Comments:

//# sourceMappingURL=main.abcdef123456.js.map

File Structure:

static/js/main.[hash].js

If you see __webpack_require__ or self["webpackChunk"] anywhere in the minified code, you're definitely looking at a Webpack bundle. The /static/js/ folder structure is another dead giveaway — that's the default output directory for Create React App, which uses Webpack.​

Identifying Vite

Vite bundles look quite different because they're built with Rollup and use ES modules:

ES Module Style:

import { a as t } from "./chunk-XYZ.js";
export { something as default };

Dev Server Hints:

/assets/index-abc123.js
/assets/chunk-xyz456.js

Vite-Specific Variables:

import.meta.hot
import.meta.env

Both import.meta.hot and import.meta.env are Vite-specific features. If you see these in production code (they shouldn't be there, but sometimes they are), you know Vite was used. The /assets/ folder structure is another strong indicator.​

Identifying Rollup

Rollup produces clean, straightforward bundles:

"use strict" Header:

"use strict";var t=Object.defineProperty;...

ES Module Wrapper:

function t(e){return e*2}export{t as double}

Notice the direct export statements in a single file with no __webpack_* stuff. This is classic Rollup output.​

Dynamic Imports:

import("./chunk-123abc.js");

Dynamic imports with filenames like chunk-xxx.js but without webpackChunk References usually indicate Rollup (or Vite, which uses Rollup internally).​

The Bundler Comparison Table

Here's a quick reference table to identify bundlers at a glance:

Quick tip: Check folder names first:

  • /assets/ → Vite
  • /static/js/ → Webpack (Create React App)
  • /dist/ → Rollup​

Redux — State Management for Complex Apps

Now let's shift gears to Redux. When applications grow beyond a few components, managing state becomes messy. Redux solves this by creating a centralized state store that all components can access.​

Think of Redux like a bank vault. Instead of every person (component) keeping their money (state) in their pockets, where it can get lost or stolen, everyone puts their money in one secure location. When someone needs money, they don't ask each person individually — they just go to the vault.​

Understanding Redux Core Concepts

Store: The Store contains the state of the entire application. State is nothing but the data your application needs to function — user information, cart items, authentication status, etc. There's typically one store per application.​

Reducer: This is a function provided by Redux. The reducer takes the current state of the application as input, applies the logic you write, updates the state accordingly, and returns the new state. Reducers are pure functions — the same input always produces the same output.​

Think of a reducer like a cashier at the bank. You hand them your account details (current state) and tell them what you want to do (action), and they process it and give you updated information (new state).​

Dispatch: This is also a function. Whenever we call dispatch, it internally calls the reducer function. You should never call the reducer directly — always use dispatch. This maintains predictability and allows Redux to properly track state changes.​

Component: The dispatch function is called from the components. Components are where user interactions happen, and they're the ones that need to trigger state updates.​

Action: Actions are the inputs we pass to dispatch. They're plain JavaScript objects that describe what happened. For example: { type: 'INCREMENT_COUNTER', payload: 1 }

Redux Flow — How It All Works Together

Here's the complete flow:

  1. User interacts with a Component (clicks a button)
  2. Component calls dispatch with an Action
  3. Dispatch sends the Action to the Reducer
  4. Reducer processes the current State and the Action, and creates a new State
  5. New State gets stored in the Store
  6. Component re-renders with updated State​

This might seem like a lot of ceremony for simple state management, but for complex applications with dozens of components that need to share state, Redux keeps everything predictable and debuggable.​

With this topic, I think we have enough knowledge (in bug hunter pov) about react and how apps function behind the scenes. Before moving on to other frameworks, let's work on javascript enumeration, as that can give us our first Critical in very unpredictable way.

JavaScript Enumeration — What to Focus On

When you're analyzing JavaScript files during bug bounty hunting, here's what to prioritize:

Critical Findings:

  • API endpoints and routes
  • Input fields and parameters
  • Hard-coded credentials, API keys, secret tokens, access keys, personal tokens, JWT secrets
  • Internal IP addresses, internal routes
  • Storage buckets, CDNs, any other cloud-related assets​

Hidden Features:

  • Functions or features not visible in the UI but with logic present in JS code
  • Hardcoded roles, admin-only endpoints
  • Feature flags or debug modes​

Technical Artifacts:

  • Source map files (.js.map)
  • AJAX calls: GET, POST, send, await, async
  • Variable names that hint at functionality​

DOM-Based Vulnerabilities — Sources and Sinks

DOM-based vulnerabilities require you to look for DOM sinks and DOM sources. A DOM source is where your arbitrary input comes from (such as location.hash, location.search, or a query parameter). When the application is vulnerable, it passes your unsafe input directly to a DOM sink — a function call that supports dynamic client-side code execution.​

Common DOM Sinks:

  • eval()
  • location.href
  • innerHTML
  • document.write()
  • setTimeout() with a string argument
  • new Function()

Common DOM Sources:

  • location.hash
  • location.search
  • document.URL
  • document.referrer
  • window.name

When reviewing JavaScript files, trace data flow from sources to sinks. If user-controlled data reaches a dangerous sink without proper sanitization, you've found a DOM XSS vulnerability.​

When to Focus on Reading JavaScript Files

Focus your JS enumeration efforts when:

  • The application heavily relies on JavaScript (SPAs, React/Vue/Angular apps)
  • The application looks like it's using outdated JS libraries (check version comments, CDN URLs)
  • You notice source maps are exposed in production
  • The application has complex client-side routing or state management​

Don't waste time on simple server-rendered sites with minimal JS — your time is better spent elsewhere.​

Some Final Chit-Chats

This week, we covered how to identify bundlers (Webpack, Vite, Rollup) from minified JavaScript and explored Redux state management concepts. As I mentioned earlier, I'm building my complete JS enumeration methodology, and these concepts will be part of it. Next week, I'll continue exploring more JavaScript security concepts and enumeration techniques.

Got feedback or want to share your JS enumeration tricks? Hit me up on X!