├── .gitignore ├── README.md ├── examples └── basics.js ├── package.json ├── poc.gif └── src └── autobreakpointer.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js 2 | node_modules/ 3 | npm-debug.log 4 | 5 | # Logs 6 | logs 7 | *.log 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Coverage 16 | lib-cov 17 | coverage 18 | 19 | # IDEs 20 | .idea/ 21 | .vscode/ 22 | *.swp 23 | *.swo 24 | 25 | # OS generated files 26 | .DS_Store 27 | Thumbs.db -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoBreakpointer 2 | 3 | AutoBreakpointer (beta v1.1.0) is a Chrome DevTools Protocol based tool that automatically sets breakpoints for specified strings or patterns in JavaScript code. It's particularly useful for debugging and monitoring specific JavaScript functions or properties across multiple files when you test your targets! 4 | 5 | ## Installation 6 | 7 | ```bash 8 | git clone https://github.com/m4ll0k/autobreakpointer.git 9 | cd autobreakpointer 10 | npm install 11 | ``` 12 | 13 | ## Usage 14 | 15 | ### CLI Usage 16 | ```bash 17 | # Run your chrome 18 | 19 | google-chrome --remote-debugging-port=9222 20 | 21 | # Track specific string 22 | node src/autobreakpointer.js "document.cookie" 23 | 24 | # Default tracking location.search 25 | node src/autobreakpointer.js 26 | 27 | # Track multiple strings 28 | node src/autobreakpointer.js "location.search|.innerHTML|document.write" 29 | ``` 30 | 31 | ### demo 32 | 33 | ![poc](https://github.com/user-attachments/assets/356cc887-60ac-4dfa-85c4-f6d77ac24307) 34 | 35 | 36 | ### Programmatic Usage 37 | ```javascript 38 | const AutoBreakpointer = require('./src/autobreakpointer'); 39 | 40 | const debugger = new AutoBreakpointer({ 41 | target: 'localStorage.getItem', 42 | autoResume: true, 43 | caseSensitive: true, 44 | jsFilesOnly: true, 45 | urlPattern: '.js' 46 | }); 47 | 48 | debugger.start(); 49 | ``` 50 | 51 | ## Configuration Options 52 | 53 | - `target`: String to track (default: 'location.search') 54 | - `autoResume`: Automatically resume after hitting breakpoint (default: false) 55 | - `caseSensitive`: Case sensitive search (default: true) 56 | - `jsFilesOnly`: Only track .js files (default: true) 57 | - `urlPattern`: File pattern to match (default: '.js') 58 | 59 | ## Requirements 60 | 61 | - Node.js >= 12 62 | - Chrome/Chromium browser 63 | - chrome-remote-interface 64 | 65 | ## License 66 | 67 | MIT 68 | -------------------------------------------------------------------------------- /examples/basics.js: -------------------------------------------------------------------------------- 1 | const AutoBreakpointer = require('../src/autobreakpointer'); 2 | 3 | // Example 1: Basic usage with default settings 4 | const basic = new AutoBreakpointer({ 5 | target: 'console.log' 6 | }); 7 | basic.start(); 8 | 9 | // Example 2: Advanced configuration 10 | const advanced = new AutoBreakpointer({ 11 | target: 'localStorage.getItem', 12 | autoResume: true, 13 | caseSensitive: true, 14 | jsFilesOnly: true, 15 | urlPattern: '.js' 16 | }); 17 | advanced.start(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto-breakpointer", 3 | "version": "1.1.0", 4 | "description": "Automated JavaScript Debugging Tool using Chrome DevTools Protocol", 5 | "main": "src/autobreakpointer.js", 6 | "bin": { 7 | "auto-breakpointer": "./src/autobreakpointer.js" 8 | }, 9 | "scripts": { 10 | "start": "node src/autobreakpointer.js", 11 | "example": "node examples/basic.js" 12 | }, 13 | "keywords": [ 14 | "debugging", 15 | "chrome", 16 | "devtools", 17 | "breakpoints", 18 | "automation" 19 | ], 20 | "author": "m4ll0k", 21 | "license": "MIT", 22 | "dependencies": { 23 | "chrome-remote-interface": "^0.32.1" 24 | }, 25 | "engines": { 26 | "node": ">=12" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/m4ll0k/autobreakpointer.git" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/m4ll0k/autobreakpointer/issues" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /poc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4ll0k/autobreakpointer/0c5a1df3edf7bb1f231ea3290ff8b618d95c2600/poc.gif -------------------------------------------------------------------------------- /src/autobreakpointer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AutoBreakpointer 3 | * Automated JavaScript Debugging Tool using Chrome DevTools Protocol 4 | * Automatically sets breakpoints for specified strings/patterns in JavaScript code 5 | * Version: beta 1.1.0 6 | * License: MIT 7 | */ 8 | 9 | /** usage: 10 | * run: google-chrome --remote-debugging-port=9222 11 | * then: node autobreakpointer "location.search|.innerHTML|document.write" 12 | */ 13 | 14 | const CDP = require('chrome-remote-interface'); 15 | 16 | class AutoBreakpointer { 17 | constructor(options = {}) { 18 | this.existingBreakpoints = new Set(); 19 | this.client = null; 20 | this.config = { 21 | targets: Array.isArray(options.target) 22 | ? options.target 23 | : options.target.split('|').map(t => t.trim()), 24 | caseSensitive: options.caseSensitive || true, 25 | autoResume: options.autoResume || false, 26 | jsFilesOnly: options.jsFilesOnly || true, 27 | urlPattern: options.urlPattern || '.js' 28 | }; 29 | } 30 | 31 | async connect() { 32 | try { 33 | this.client = await CDP(); 34 | const { Debugger, Runtime } = this.client; 35 | 36 | await Promise.all([ 37 | Debugger.enable(), 38 | Runtime.enable() 39 | ]); 40 | 41 | console.log('AutoBreakpointer: Initialized for patterns:', this.config.targets); 42 | return true; 43 | } catch (error) { 44 | console.error('AutoBreakpointer: Connection error:', error); 45 | return false; 46 | } 47 | } 48 | 49 | async start() { 50 | if (!await this.connect()) { 51 | return; 52 | } 53 | 54 | const { Debugger, Runtime } = this.client; 55 | 56 | Debugger.scriptParsed(async (script) => { 57 | if (!this.config.jsFilesOnly || script.url.endsWith(this.config.urlPattern)) { 58 | console.log('Script detected:', script.url); 59 | 60 | for (const target of this.config.targets) { 61 | try { 62 | const searchResult = await Debugger.searchInContent({ 63 | scriptId: script.scriptId, 64 | query: target, 65 | caseSensitive: this.config.caseSensitive, 66 | isRegex: false 67 | }); 68 | 69 | if (searchResult.result?.length > 0) { 70 | console.log(`Match found for "${target}":`, searchResult); 71 | for (const match of searchResult.result) { 72 | await this.setBreakpoint(script, match, target); 73 | } 74 | } 75 | } catch (error) { 76 | if (!error.message.includes('already exists')) { 77 | console.error(`AutoBreakpointer: Search error for "${target}":`, error); 78 | } 79 | } 80 | } 81 | } 82 | }); 83 | 84 | Debugger.paused(async ({ callFrames }) => { 85 | await this.handleBreakpoint(callFrames); 86 | }); 87 | 88 | this.setupCleanup(); 89 | } 90 | 91 | async setBreakpoint(script, match, target) { 92 | const { Debugger } = this.client; 93 | const { lineNumber, lineContent } = match; 94 | const columnNumber = lineContent.indexOf(target); 95 | const breakpointKey = `${script.url}-${lineNumber}-${columnNumber}`; 96 | 97 | if (!this.existingBreakpoints.has(breakpointKey)) { 98 | try { 99 | const breakpoint = await Debugger.setBreakpointByUrl({ 100 | urlRegex: script.url, 101 | lineNumber: lineNumber, 102 | columnNumber: columnNumber 103 | }); 104 | 105 | this.existingBreakpoints.add(breakpointKey); 106 | 107 | console.log({ 108 | message: 'AutoBreakpointer: New breakpoint set', 109 | pattern: target, 110 | scriptId: script.scriptId, 111 | line: lineNumber, 112 | column: columnNumber, 113 | breakpointId: breakpoint.breakpointId, 114 | code: lineContent.trim(), 115 | url: script.url 116 | }); 117 | } catch (error) { 118 | console.error('AutoBreakpointer: Breakpoint error:', error); 119 | } 120 | } 121 | } 122 | 123 | async handleBreakpoint(callFrames) { 124 | const { Runtime, Debugger } = this.client; 125 | const frame = callFrames[0]; 126 | 127 | try { 128 | // Try to evaluate all targets at the breakpoint 129 | const evaluations = await Promise.all( 130 | this.config.targets.map(async target => { 131 | const result = await Runtime.evaluate({ 132 | expression: target 133 | }).catch(() => ({ result: { value: 'Unable to evaluate' } })); 134 | 135 | return { 136 | target, 137 | value: result.result.value 138 | }; 139 | }) 140 | ); 141 | 142 | console.log('AutoBreakpointer: Break', { 143 | url: frame.url, 144 | line: frame.location.lineNumber, 145 | column: frame.location.columnNumber, 146 | evaluations, 147 | stackTrace: this.formatStackTrace(callFrames) 148 | }); 149 | 150 | if (this.config.autoResume) { 151 | await Debugger.resume(); 152 | } 153 | } catch (error) { 154 | console.error('AutoBreakpointer: Evaluation error:', error); 155 | } 156 | } 157 | 158 | formatStackTrace(callFrames) { 159 | return callFrames.map(frame => ({ 160 | functionName: frame.functionName || '', 161 | url: frame.url, 162 | line: frame.location.lineNumber, 163 | column: frame.location.columnNumber 164 | })); 165 | } 166 | 167 | setupCleanup() { 168 | process.on('SIGINT', async () => { 169 | if (this.client) { 170 | const { Debugger } = this.client; 171 | console.log('\nAutoBreakpointer: Cleaning up...'); 172 | 173 | for (const breakpointKey of this.existingBreakpoints) { 174 | try { 175 | const [url, line, column] = breakpointKey.split('-'); 176 | await Debugger.removeBreakpoint({ 177 | breakpointId: `${url}:${line}:${column}` 178 | }); 179 | } catch (error) { 180 | console.log('AutoBreakpointer: Cleanup error:', error); 181 | } 182 | } 183 | 184 | await this.client.close(); 185 | console.log('AutoBreakpointer: Shutdown complete'); 186 | } 187 | process.exit(); 188 | }); 189 | } 190 | } 191 | 192 | // CLI handling 193 | if (require.main === module) { 194 | const target = process.argv[2] || 'location.search'; 195 | const options = { 196 | target, 197 | autoResume: false, 198 | caseSensitive: true, 199 | jsFilesOnly: true 200 | }; 201 | 202 | const breakpointer = new AutoBreakpointer(options); 203 | breakpointer.start(); 204 | } 205 | 206 | module.exports = AutoBreakpointer; 207 | --------------------------------------------------------------------------------