Some notes and techniques for reverse engineering Webpack (and a little bit about React/Vue/Angular) apps.
- Webpack (and similar bundlers) Chunk Format
- Runtime Injection / Overrides
- React Internals
- Vue Internals
- Angular Internals
- Chrome Devtools
- See Also
The structure of a webpack chunk file seems to be:
- a top level default assignment to ensure
self.webpackChunk_N_E
exists and is an array .push()
into that an array containing:- an array containing the chunk ID (eg.
[1234]
) - an object containing the module mappings within this chunk (key=moduleId, value='module function')
- the 'module function' generally takes 3 parameters
- module
- exports
- require
- the 'module function' generally takes 3 parameters
- a function for doing 'chunk init' type things
- an array containing the chunk ID (eg.
Program .body -> ExpressionStatement .expression ->
CallExpression
.callee -> MemberExpression
.object -> AssignmentExpression
.left -> MemberExpression
.object -> Identifier .name -> "self"
.property -> Identifier .name -> "webpackChunk_N_E"
.operator -> "="
.right -> LogicalExpression
.left -> MemberExpression
.object -> Identifier .name -> "self"
.property -> Identifier .name -> "webpackChunk_N_E"
.operator -> "||"
.right -> ArrayExpression .elements[] -> <empty array>
.property -> Identifier .name -> "push"
.arguments[0] -> ArrayExpression .elements[]
-> ArrayExpression .elements[0] -> NumericLiteral .value -> <the chunk ID>
-> ObjectExpression .properties[] -> ObjectProperty ... <the modules within this chunk>
-> FunctionExpression -> <used for chunk initialisation/etc>
.params[] -> ...
.body[]
-> VariableDeclaration -> ...
-> ExpressionStatement .expression -> SequenceExpression .expressions[]
-> CallExpression ->
.callee -> ...
.arguments -> ...
-> AssignmentExpression
.left -> Identifier .name -> "_N_E"
.operator -> "="
.right -> CallExpression ...
The following is a very basic minified example showing the basic structure of the chunk file:
(self.webpackChunk_N_E = self.webpackChunk_N_E || []).push([
[1234],
{
1111: function (Y, et, en) { /* module code */ },
2222: function (Y, et, en) { /* module code */ },
},
function (Y) {
var et = function (et) {
return Y((Y.s = et));
};
Y.O(0, [9774, 179], function () {
return et(54885), et(11867);
}),
(_N_E = Y.O());
},
]);
//# sourceMappingURL=_app-abcdef123456.js.map
The following more complete example chunk was generated based on examples seen in the wild + ChatGPT used to refine the improved naming/comments/etc:
(self.webpackChunk_N_E = self.webpackChunk_N_E || []).push([
[1234], // Chunk ID
{ // Module definitions...
// Module 1: Exporting a utility function using `module.exports`
1111: function (module, exports, require) {
// Utility function defined in Module 1
function utilityFunction() {
console.log("Utility function from Module 1.");
}
// Exporting the utility function as the module's public interface
module.exports = utilityFunction;
},
// Module 2: Using `exports` to export functions and `require` to import from Module 1
2222: function (module, exports, require) {
// Requiring the utility function from Module 1
var utilityFunction = require(1111); // Using the module ID to require Module 1
// Function that uses the imported utilityFunction
function functionUsingUtility() {
console.log("Function in Module 2 calling utility function:");
utilityFunction(); // Using the utility function from Module 1
}
// Another standalone function in Module 2
function standaloneFunction() {
console.log("Standalone function in Module 2.");
}
// Exporting both functions as properties of the exports object
exports.functionUsingUtility = functionUsingUtility;
exports.standaloneFunction = standaloneFunction;
},
},
function (module) {
// Initialization function: Sets up the chunk and initializes its modules.
// 'init' function: Loads and executes a specific module by its ID.
var init = function (moduleId) {
// Sets the current module ID ('module.s') and loads the module.
return module((module.s = moduleId));
};
// 'module.O' method: Part of webpack's runtime, handling module and chunk loading.
// First Call to 'module.O': Handles loading of dependent chunks.
module.O(0, [9774, 179], function () {
// This callback is executed once all dependent chunks (9774, 179) are loaded.
// Dependent chunks are other chunks that this chunk needs before it can be initialized.
// For example, these could be vendor chunks or previously split code chunks that contain shared libraries or components.
}).then(function () {
// Second Call to 'module.O': Initializes the entry modules of this chunk.
// Once dependent chunks are loaded, we proceed to initialize this chunk's entry modules (54885, 11867).
// These module IDs represent the starting points or root modules of this chunk, which kickstart the functionality encapsulated within the chunk.
module.O(0, [], function () {
return init(54885), init(11867);
}, 1); // The '1' here might indicate a different operation type, such as initializing entry modules.
// '_N_E' assignment: Marks the chunk as successfully initialized.
// 'module.O()' returns a promise that resolves when the chunk is fully loaded and initialized.
// This assignment can be used for debugging or for chaining further actions once initialization is complete.
(_N_E = module.O());
});
},
]);
After like basically a day of crazy deep webpack knowledge aquisition the hard way around.. I had enough understanding and keywords to find this article, which would have in many ways probably saved me a good chunk of this time/figuring out of things:
But I guess what it doesn't mention is that you can access window.webpackChunk_N_E
from global scope to see all of the loaded modules, and you can tell it to load a new module with window.webpackChunk_N_E.push
, so you can basically arbitrarily define a new module and inject it at runtime with something like:
window.webpackChunk_N_E.push([
[1337],
{31337: (U, B, G) => console.log('chunk[1]', { U, B, G })},
(U) => console.log('chunk[2]', U)
])
Where:
1337
is the module chunk ID (relatively arbitrary choice, though used to reference the module from other code)31337
is a function defined within that module (can define more than 1) whereU, B, G
==module, __webpack_exports__, __webpack_require__
- And the final function I'm not 100% sure about.. I think it might get called to preload any dependencies, or do something else like that... but in any case, it gets called when you first
.push
your module, so you can use it to get access toU
(which I believe is also__webpack_require__
)
And then __webpack_require__
is both a function that can be called, but also has a bunch of helper methods that can be accessed like accessing the modules object (aka __webpack_modules__
), accessing the module cache (aka: installedModules
), being able to access both harmony and __esModule
exports, getting the defaultExport, calling properties on an object's prototype, seeing the module build path, etc.
So I haven't put all that together in a useful/runnable PoC gadget chain yet... but I believe that is basically all of the background knowledge required to be able to do so
In a similar space as the above, it might be worth looking at the following:
Specifically this implemention that is mentioned there:
- https://github.com/GooseMod/GooseMod/blob/master/src/util/discord/webpackModules.js
- This module in particular looks interesting; it seems to be a bunch of helpers for injecting into webpack modules at runtime
The following are a few scripts/methods that should allow overriding/injecting into Webpack modules at runtime (without requiring the use of a debugger/breakpoints/etc to do so).
Note: The following code hasn't been fully tested/evaluated yet.
/**
* Injects wrappers into specified webpack modules to allow manipulation or stubbing.
* @param {Object} overrides - An object where keys are module IDs and values are functions that take the old module function and return a new module function.
*/
function injectWebpackOverrides(overrides) {
window.webpackChunk_N_E.forEach(chunk => {
const [_, modules] = chunk;
Object.keys(modules).forEach(moduleId => {
if (Object.hasOwn(modules, moduleId)) {
const originalModule = modules[moduleId];
// Check if there is an override for the module and apply it
if (overrides.hasOwnProperty(moduleId)) {
// The override function receives the original module and returns a new module function
modules[moduleId] = overrides[moduleId](originalModule);
}
}
});
});
}
/**
* Creates a wrapped require function that can intercept module requests.
* @param {Function} originalRequire - The original require function.
* @param {Object} overrides - The overrides definitions.
* @returns {Function} The wrapped require function.
*/
function createWrappedRequire(originalRequire, overrides) {
return (moduleId) => {
if (Object.hasOwn(overrides, moduleId)) {
return overrides[moduleId](originalRequire(moduleId));
}
return originalRequire(moduleId);
};
}
// Example usage:
injectWebpackOverrides({
'51510': (oldModule) => {
// Create a wrapped require function directly with the specific overrides for modules this module depends on
const wrappedRequire = createWrappedRequire(oldModule.require, {
'48779': (originalModule48779) => {
// Modify or replace the behavior of module 48779
return {
...originalModule48779,
someFunction: () => 'Modified behavior'
};
}
});
// Returns a new function that uses the wrapped require
return (e, t, a) => {
// Call the old module function using the wrappedRequire
return oldModule(e, t, wrappedRequire);
};
},
'48779': (oldModule) => {
// Return a completely new function, replacing the old module
return (e, t, a) => {
// Return directly whatever is needed for the shim
return { /* shimmed module contents */ };
};
}
});
Note: The following code hasn't been fully tested/evaluated yet.
/**
* Injects wrappers into specified webpack modules to allow manipulation or stubbing using Proxies.
* @param {Object} overrides - An object where keys are module IDs and values are functions that take the old module function and return a new module function.
*/
function injectWebpackOverrides(overrides) {
window.webpackChunk_N_E.forEach(chunk => {
const [_, modules] = chunk;
// Create a proxy to handle the modules object
const modulesProxy = new Proxy(modules, {
get(target, moduleId) {
if (overrides.hasOwnProperty(moduleId)) {
// Check if the moduleId is part of the overrides and if the override should be applied
const originalModule = target[moduleId];
// Return the overridden module function without modifying the original target
return overrides[moduleId](originalModule);
}
// Return the original module if there is no override
return target[moduleId];
}
});
chunk[1] = modulesProxy; // Replace the original modules with the proxied version
});
}
Note: The following code hasn't been fully tested/evaluated yet.
/**
* Injects wrappers into specified webpack modules across all chunks using a global Proxy.
* @param {Object} overrides - An object where keys are module IDs and values are functions that take the old module function and return a new module function.
*/
function injectWebpackOverrides(overrides) {
// Wrapping the entire webpackChunk_N_E array with a Proxy
window.webpackChunk_N_E = new Proxy(window.webpackChunk_N_E, {
get(target, prop) {
// Access the chunk
const chunk = target[prop];
if (Array.isArray(chunk)) {
const [chunkId, modules] = chunk;
// Use another Proxy to handle the modules within the chunk
const modulesProxy = new Proxy(modules, {
get(target, moduleId) {
moduleId = moduleId.toString(); // Ensure moduleId is a string
if (overrides.hasOwnProperty(moduleId)) {
const originalModule = target[moduleId];
// Apply override only if not previously applied
if (!originalModule.__proxyWrapped) {
target[moduleId] = overrides[moduleId](originalModule);
target[moduleId].__proxyWrapped = true;
}
}
return target[moduleId];
}
});
// Return the chunk with the proxied modules
return [chunkId, modulesProxy];
}
return chunk;
}
});
}
- Accessing the React internals for the Beeper 'Rooms' component using Beeper / Electron's DevTools
- https://bogdan-lyashenko.github.io/Under-the-hood-ReactJS/
- https://github.com/Bogdan-Lyashenko/Under-the-hood-ReactJS
-
Entire React code base explanation by visual block schemes (Stack version)
-
This repository contains an explanation of inner work of React. In fact, I was debugging through the entire code base and put all the logic on visual block-schemes, analyzed them, summarized and explained main concepts and approaches. I've already finished with Stack version and now I work with the next, Fiber version.
-
I wanted to automate process of “learning and documenting” a complex codebase as much as possible, so I started Codecrumbs project. It will help to build projects like “Under the hood ReactJs” in a shorter time and in a simpler way!
- https://github.com/Bogdan-Lyashenko/codecrumbs
-
Learn, design or document codebase by putting breadcrumbs in source code. Live updates, multi-language support and more.
-
Check out new version of this project as standalone application. Just in a few clicks you can start exploring a codebase in more efficient way, create interactive visual guides and share them with others on your own blog!
-
- https://github.com/Bogdan-Lyashenko/codecrumbs
-
- https://github.com/Bogdan-Lyashenko/Under-the-hood-ReactJS
- https://www.damianmullins.com/inspecting-a-vue-application-in-production/
-
The Vue.js devtools are great to use when in the process of developing a web application. However, once you deploy to production it no longer has access to the code it needs to work. So how can we inspect an application once released to production? In this post, we’ll walk through the options available to you along with some tricks and tips to make the process a little easier.
-
Now refresh the page and once the breakpoint is hit then type
Vue.config.devtools = true
in the devtools console. In Vue.js, by default, thedevtools
config setting is determined by the value ofprocess.env.NODE_ENV
, what we’re doing here is manually setting that value totrue
in order to force Vue.js to initiate the Vue.js devtools. -
Once you have the element selected you can then move to the console panel in devtools and type
$0
.$0
will be a reference to the most recently selected element in the element panel. To see the Vue instance details you can type$0.__vue__
Now that we have a reference to the Vue component instance we can expand it in the console to see what’s inside
-
- https://www.damianmullins.com/logging-vuex-actions-and-mutations-in-the-wild/
-
const store = [...document.querySelectorAll('*')] .find(el => el.__vue__) .__vue__ .$store;
-
const store = document.querySelector('[data-app]') .__vue__ .$store;
-
let allElements = document.querySelectorAll('*');
// Filter for elements with __vue__ property
let vueElements = [...allElements]
.filter(el => el.__vue__)
.map(element => ({
element,
vue: element.__vue__,
}));
console.log(vueElements);
The following is a snippet from Vue devtools (chrome-extension://nhdogjmejiglipccpnnnanhbledajbpd/build/detector.js
)
We can see the unminified source here:
- https://github.com/vuejs/devtools/blob/main/packages/shell-chrome/src/detector.js
// Method 1: Check Nuxt
window.__NUXT__ || window.$nuxt
Vue = window.$nuxt.$root && window.$nuxt.$root.constructor
devtoolsEnabled: (/* Vue 2 */ Vue && Vue.config.devtools) || (/* Vue 3.2.14+ */ window.__VUE_DEVTOOLS_GLOBAL_HOOK__ && window.__VUE_DEVTOOLS_GLOBAL_HOOK__.enabled)
// Method 2: Check Vue 3
window.__VUE__
devtoolsEnabled: /* Vue 3.2.14+ */ window.__VUE_DEVTOOLS_GLOBAL_HOOK__ && window.__VUE_DEVTOOLS_GLOBAL_HOOK__.enabled
// Method 3: Scan all elements inside document
-
const all = document.querySelectorAll('*') let el for (let i = 0; i < all.length; i++) { if (all[i].__vue__) { el = all[i] break } } if (el) { let Vue = Object.getPrototypeOf(el.__vue__).constructor while (Vue.super) { Vue = Vue.super }
devtoolsEnabled: Vue.config.devtools
-
if (detectRemainingTries > 0) { detectRemainingTries-- setTimeout(() => { runDetect() }, delay) delay *= 5 }
-
win.postMessage({ devtoolsEnabled: Vue.config.devtools, vueDetected: true, }, '*')
-
window.addEventListener('message', e => { if (e.source === window && e.data.vueDetected) { chrome.runtime.sendMessage(e.data) } })
-
function n(e) {
let t = 1e3,
n = 10;
function r() {
const o = !(!window.__NUXT__ && !window.$nuxt);
if (o) {
let t;
return (
window.$nuxt &&
(t = window.$nuxt.$root && window.$nuxt.$root.constructor),
void e.postMessage(
{
devtoolsEnabled:
(t && t.config.devtools) ||
(window.__VUE_DEVTOOLS_GLOBAL_HOOK__ &&
window.__VUE_DEVTOOLS_GLOBAL_HOOK__.enabled),
vueDetected: !0,
nuxtDetected: !0,
},
"*"
)
);
}
const i = !!window.__VUE__;
if (i)
return void e.postMessage(
{
devtoolsEnabled:
window.__VUE_DEVTOOLS_GLOBAL_HOOK__ &&
window.__VUE_DEVTOOLS_GLOBAL_HOOK__.enabled,
vueDetected: !0,
},
"*"
);
const s = document.querySelectorAll("*");
let a;
for (let e = 0; e < s.length; e++)
if (s[e].__vue__) {
a = s[e];
break;
}
if (a) {
let t = Object.getPrototypeOf(a.__vue__).constructor;
while (t.super) t = t.super;
e.postMessage(
{
devtoolsEnabled: t.config.devtools,
vueDetected: !0,
},
"*"
);
} else
n > 0 &&
(n--,
setTimeout(() => {
r();
}, t),
(t *= 5));
}
setTimeout(() => {
r();
}, 100);
}
Therefore, we could probably use a snippet like the following to tell Vue devtools that it should enable itself, even on a production site:
let firstVueElement = [...document.querySelectorAll('*')].find(el => el.__vue__);
let Vue = undefined;
if (firstVueElement) {
Vue = Object.getPrototypeOf(firstVueElement.__vue__).constructor
while (Vue.super) {
Vue = Vue.super
}
}
if (Vue) {
console.log('Found Vue', { Vue });
console.log('Vue.config', Vue.config);
console.log('Setting Vue.config.devtools = true');
Vue.config.devtools = true;
console.log('Setting __VUE_DEVTOOLS_GLOBAL_HOOK__ values', { __VUE_DEVTOOLS_GLOBAL_HOOK__ });
__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue = Vue;
__VUE_DEVTOOLS_GLOBAL_HOOK__.enabled = true;
console.log('Signalling to Vue Devtools that it should enable itself');
window.postMessage({
devtoolsEnabled: true,
vueDetected: true,
nuxtDetected: false,
}, '*')
} else {
console.log('Failed to find Vue');
}
The following sections of the Angular Devtools may be relevant in figuring out how to enable them even on production sites:
- https://github.com/angular/angular/blob/b1dffa4abe8321a47d79b2ea29ee32a81acfe031/devtools/projects/shell-browser/src/app/background.ts#L14C27-L25
- https://github.com/angular/angular/blob/b1dffa4abe8321a47d79b2ea29ee32a81acfe031/devtools/projects/shell-browser/src/app/background.ts#L55-L80
- https://github.com/angular/angular/blob/b1dffa4abe8321a47d79b2ea29ee32a81acfe031/devtools/projects/protocol/src/lib/messages.ts#L216-L227
- https://github.com/angular/angular/blob/b1dffa4abe8321a47d79b2ea29ee32a81acfe031/devtools/projects/shell-browser/src/app/content-script.ts#L30-L67
-
const detectAngularMessageBus = new SamePageMessageBus( `angular-devtools-content-script-${location.href}`, `angular-devtools-detect-angular-${location.href}`, );
-
const script = document.createElement('script'); script.src = chrome.runtime.getURL('app/backend_bundle.js'); document.documentElement.appendChild(script); document.documentElement.removeChild(script); backendInstalled = true;
-
- https://github.com/angular/angular/blob/b1dffa4abe8321a47d79b2ea29ee32a81acfe031/devtools/projects/shell-browser/src/app/same-page-message-bus.ts#L15-L55
- https://developer.chrome.com/docs/devtools/search/
-
Search: Find text across all loaded resources
-
Use the Search tab to find text across all loaded resources.
-
The Search tab doesn't show results from network headers and responses. To search across them, follow the steps in Search network headers and responses.
- https://developer.chrome.com/docs/devtools/network/#search
-
Search network headers and responses
-
Use the Search pane when you need to search the HTTP headers and responses of all resources for a certain string or regular expression.
-
- https://developer.chrome.com/docs/devtools/network/#search
-
https://developer.chrome.com/docs/devtools/search/#built-in-search-bars
-
Search for text in a specific tool
-
To narrow your search scope to a resource opened in a specific tool, you can use a built-in search bar if the tool supports it.
-
- https://stackoverflow.com/questions/15944633/can-i-get-chrome-devtools-to-actually-search-all-js-sources
-
There is an option under Settings -> Preferences -> Sources called "Search in anonymous and content scripts".
-
-
- https://developer.chrome.com/docs/devtools/console/utilities/
-
Console Utilities API reference
- https://developer.chrome.com/docs/devtools/console/utilities/#inspect-function
-
inspect(object/function)
opens and selects the specified element or object in the appropriate panel: either the Elements panel for DOM elements or the Profiles panel for JavaScript heap objects.
-
- https://developer.chrome.com/docs/devtools/console/utilities/#getEventListeners-function
-
getEventListeners(object)
returns the event listeners registered on the specified object. The return value is an object that contains an array for each registered event type (click or keydown, for example). The members of each array are objects that describe the listener registered for each type.
-
- https://developer.chrome.com/docs/devtools/console/utilities/#monitor-function
-
When the function specified is called, a message is logged to the console that indicates the function name along with the arguments that are passed to the function when it was called.
-
- https://developer.chrome.com/docs/devtools/console/utilities/#monitorEvents-function
-
When one of the specified events occurs on the specified object, the Event object is logged to the console. You can specify a single event to monitor, an array of events, or one of the generic events "types" mapped to a predefined collection of events.
-
- https://developer.chrome.com/docs/devtools/console/utilities/#queryObjects-function
-
Call queryObjects(Constructor) from the console to return an array of objects that were created with the specified constructor. For example:
-
queryObjects(Promise)
. Returns all instances ofPromise
. -
queryObjects(HTMLElement)
. Returns all HTML elements. -
queryObjects(foo)
, wherefoo
is a class name. Returns all objects that were instantiated vianew foo()
.
-
-
The scope of
queryObjects()` is the currently-selected execution context in the console.
-
- etc
-
- https://developer.chrome.com/docs/devtools/console/reference/
-
Console features reference
- https://developer.chrome.com/docs/devtools/console/reference/#inspect-internal-properties
-
Borrowing the ECMAScript notation, the Console encloses some properties internal to JavaScript in double square brackets. You can't interact with such properties in your code. However, it might be useful to inspect them.
-
Any object has a
[[Prototype]]
-
Primitive wrappers have a
[[PrimitiveValue]]
property -
ArrayBuffer
objects have the following properties:[[Int8Array]]
,[[Uint8Array]]
,[[Int16Array]]
,[[Int32Array]]
,[[ArrayBufferByteLength]]
,[[ArrayBufferData]]
-
In addition to
ArrayBuffer
-specific properties,WebAssembly.Memory
objects have a[[WebAssemblyMemory]]
property. -
Keyed collections (maps and sets) have an
[[Entries]]
property that contains their keyed entries. -
Promise
objects have the following properties:-
[[PromiseState]]
: pending, fulfilled, or rejected -
[[PromiseResult]]
: undefined if pending,<value>
if fulfilled,<reason>
if rejected
-
-
Proxy
objects have the following properties:[[Handler]]
object,[[Target]]
object, and[[isRevoked]]
(switched off or not).
-
-
- https://developer.chrome.com/docs/devtools/console/reference/#inspect-functions
-
To view function properties internal to JavaScript, use the
console.dir()
command.- https://developer.chrome.com/docs/devtools/console/api/#dir
-
Prints a JSON representation of the specified object.
-
-
Functions have the following properties:
-
[[FunctionLocation]]
. A link to the line with the function definition in a source file. -
[[Scopes]]
. Lists values and expressions the function has access to. To inspect function scopes during debugging, see View and edit local, closure, and global properties.
-
-
Bound functions have the following properties:
-
[[TargetFunction]]
. The target ofbind()
-
[[BoundThis]]
. The value ofthis
-
[[BoundArgs]]
. An array of function arguments
-
-
Generator functions are marked with a
[[IsGenerator]]: true
property. -
Generators return iterator objects and they have following properties:
-
[[GeneratorLocation]]
. A link to a line with the generator definition in a source file -
[[GeneratorState]]
: suspended, closed, or running -
[[GeneratorFunction]]
. The generator that returned the object -
[[GeneratorReceiver]]
. An object that receives the value
-
- https://developer.chrome.com/docs/devtools/console/api/#dir
-
- etc
-
- https://developer.chrome.com/docs/devtools/javascript/reference/
-
JavaScript debugging reference
- https://developer.chrome.com/docs/devtools/javascript/reference/#scope
-
View and edit local, closure, and global properties
-
While paused on a line of code, use the Scope pane to view and edit the values of properties and variables in the local, closure, and global scopes.
-
- https://developer.chrome.com/docs/devtools/javascript/reference/#restart-frame
-
Restart a function (frame) in a call stack
-
To observe the behavior of a function and re-run it without having to restart the entire debugging flow, you can restart the execution of a single function when this function is paused. In other words, you can restart the function's frame in the call stack.
-
- https://developer.chrome.com/docs/devtools/javascript/reference/#edit
-
Edit a script
-
When fixing a bug, you often want to test out some changes to your JavaScript code. You don't need to make the changes in an external browser and then reload the page. You can edit your script in DevTools.
-
- https://developer.chrome.com/docs/devtools/javascript/reference/#live-edit
-
While the execution is paused, you can edit the current function and apply changes live with the following limitations:
-
You can edit only the top-most function in the Call Stack.
-
There must be no recursive calls to the same function further down the stack.
-
-
When you apply changes, the debugger restarts the function automatically. So, the limitations of a function restart also apply. You can't restart WebAssembly, async, and generator functions.
-
- etc
-
- https://developer.chrome.com/docs/devtools/network/
-
Inspect network activity
- https://developer.chrome.com/docs/devtools/network/#load
-
Log network activity
-
- https://developer.chrome.com/docs/devtools/network/#filterbox
-
Filter by string, regular expression, or property
- You can use basic string or regex
- You can use a negative filter (eg.
-main.css
) - You can use a domain based filter (eg.
domain:example.com
) - You can use a property based filter
- https://developer.chrome.com/docs/devtools/network/reference/#filter-by-property
-
Filter requests by properties
-
Use the Filter text box to filter requests by properties, such as the domain or size of the request.
-
To invert your filter, check the Invert checkbox next to the Filter text box.
-
You can use multiple properties simultaneously by separating each property with a space. For example,
mime-type:image/gif larger-than:1K
displays all GIFs that are larger than one kilobyte. These multi-property filters are equivalent toAND
operations.OR
operations are currently not supported. - There are ~20 different properties that can be used to filter the network log
-
- https://developer.chrome.com/docs/devtools/network/reference/#filter-by-property
-
- https://developer.chrome.com/docs/devtools/network/reference/#group-by-frames
-
Group requests by inline frames
-
If inline frames on a page initiate a lot of requests, you can make the request log frendlier by grouping them.
-
To group requests by iframes, open Settings Settings. inside the Network panel and check Checkbox. Group by frame.
-
- https://developer.chrome.com/docs/devtools/network/reference/#frames
-
Analyze the messages of a WebSocket connection
-
To view the messages of a WebSocket connection:
-
Under the Name column of the Requests table, click the URL of the WebSocket connection.
-
Click the Messages tab. The table shows the last 100 messages.
-
To refresh the table, re-click the name of the WebSocket connection under the Name column of the Requests table.
-
-
- https://developer.chrome.com/docs/devtools/network/reference/#initiators-dependencies
-
View initiators and dependencies
-
To view the initiators and dependencies of a request, hold Shift and hover over the request in the Requests table. DevTools colors initiators green, and dependencies red.
-
When the Requests table is ordered chronologically, the first green request above the request that you're hovering over is the initiator of the dependency. If there's another green request above that, that higher request is the initiator of the initiator. And so on.
-
- https://developer.chrome.com/docs/devtools/network/reference/#initiator-stack-trace
-
View the stack trace that caused a request
-
When a JavaScript statement causes a resource to be requested, hover over the Initiator column to view the stack trace leading up to the request.
-
- https://developer.chrome.com/docs/devtools/network/reference/#copy
-
Copy one or more requests to the clipboard
-
Under the Name column of the Requests table, right-click a request, hover over Copy, and select one of the copy options
-
- etc
-
- https://stackoverflow.com/questions/41146373/access-function-location-programmatically
- https://github.com/midrissi/func-loc
-
A simple tool that helps you to retrieve the function location from its reference
-
- https://github.com/midrissi/func-loc
- https://chromedevtools.github.io/devtools-protocol/
-
The Chrome DevTools Protocol allows for tools to instrument, inspect, debug and profile Chromium, Chrome and other Blink-based browsers.
- https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getProperties
-
Runtime.getProperties
: Returns properties of a given object. Object group of the result is inherited from the target object.
-
-
- https://developer.chrome.com/docs/extensions/reference/debugger/
-
The
chrome.debugger
API serves as an alternate transport for Chrome's remote debugging protocol. Usechrome.debugger
to attach to one or more tabs to instrument network interaction, debug JavaScript, mutate the DOM and CSS, etc. -
For security reasons, the chrome.debugger API does not provide access to all Chrome DevTools Protocol Domains. The available domains are:
Accessibility
,Audits
,CacheStorage
,Console
,CSS
,Database
,Debugger
,DOM
,DOMDebugger
,DOMSnapshot
,Emulation
,Fetch
,IO
,Input
,Inspector
,Log
,Network
,Overlay
,Page
,Performance
,Profiler
,Runtime
,Storage
,Target
,Tracing
,WebAudio
, andWebAuthn
.
-
- https://github.com/0xdevalias/chatgpt-source-watch : Analyzing the evolution of ChatGPT's codebase through time with curated archives and scripts.
- Deobfuscating / Unminifying Obfuscated Web App Code (0xdevalias' gist)
- Reverse Engineered Webpack Tailwind-Styled-Component (0xdevalias' gist)
- React Server Components, Next.js v13+, and Webpack: Notes on Streaming Wire Format (
__next_f
, etc) (0xdevalias' gist)) - Fingerprinting Minified JavaScript Libraries / AST Fingerprinting / Source Code Similarity / Etc (0xdevalias' gist)
- Bypassing Cloudflare, Akamai, etc (0xdevalias' gist)
- Debugging Electron Apps (and related memory issues) (0xdevalias' gist)
- devalias' Beeper CSS Hacks (0xdevalias' gist)
- Reverse Engineering Golang (0xdevalias' gist)
- Reverse Engineering on macOS (0xdevalias' gist)
🔥