-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathcontent.js
449 lines (375 loc) · 15.6 KB
/
content.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
// content.js
// Get Browser API (Chrome or Firefox)
const browserAPI = typeof browser !== "undefined" ? browser : chrome;
/**
* Utility function to extract query string parameter keys from a URL.
* @param {string} url - The URL to extract parameters from.
* @returns {Array} - An array of query string parameter keys.
*/
function extractQueryStringKeys(url) {
const params = new URL(url).searchParams;
return Array.from(params.keys());
}
/**
* Utility function to find variable names in JavaScript code.
* @param {string} body - The HTML or script body to search.
* @returns {Array} - An array of variable names.
*/
function findVariableNames(body) {
const variableRegex = /(let|const|var)\s+([\w\s,]+)/g;
const variableNames = [];
let match;
while ((match = variableRegex.exec(body)) !== null) {
const names = match[2].split(',').map(name => name.trim());
variableNames.push(...names);
}
return variableNames;
}
/**
* Utility function to find keys in a JSON object from JavaScript code.
* @param {string} body - The JavaScript code containing JSON data.
* @returns {Array} - An array of JSON keys.
*/
function findJsonKeys(body) {
const jsonKeyRegex = /["']([\w\-]+)["']\s*:/g;
const jsonKeys = [];
let match;
while ((match = jsonKeyRegex.exec(body)) !== null) {
jsonKeys.push(match[1]);
}
return jsonKeys;
}
/**
* Utility function to find variables inside string formatting in JavaScript.
* @param {string} body - The JavaScript code to search for string format variables.
* @returns {Array} - An array of string format variables.
*/
function findStringFormatVariables(body) {
const stringFormatRegex = /\${(\s*[\w\-]+)\s*}/g;
const stringFormats = [];
let match;
while ((match = stringFormatRegex.exec(body)) !== null) {
stringFormats.push(match[1]);
}
return stringFormats;
}
/**
* Utility function to find function parameters in JavaScript.
* @param {string} body - The JavaScript code to search for function parameters.
* @returns {Array} - An array of function parameter names.
*/
function findFunctionParameters(body) {
const funcParamsRegex = /\(\s*["']?([\w\-]+)["']?\s*(,\s*["']?([\w\-]+)["']?\s*)*(,\s*["']?([\w\-]+)["']?\s*)*\)/g;
const functionParams = [];
let match;
while ((match = funcParamsRegex.exec(body)) !== null) {
for (let i = 1; i < match.length; i += 2) {
if (match[i]) {
functionParams.push(match[i]);
}
}
}
return functionParams;
}
/**
* Utility function to find dynamic path parameters (e.g., /{id}) in a URL.
* @param {string} body - The JavaScript code to search for path parameters.
* @returns {Array} - An array of path parameter names.
*/
function findPathParameters(body) {
const pathParamRegex = /\/\{(.*?)\}/g;
const pathParams = [];
let match;
while ((match = pathParamRegex.exec(body)) !== null) {
pathParams.push(match[1]);
}
return pathParams;
}
/**
* Utility function to find query string keys inside the HTML body.
* @param {string} body - The body of the page to search for query parameters.
* @returns {Array} - An array of query string parameter keys.
*/
function findQueryStringKeys(body) {
const queryStringRegex = /(\?([\w\-]+)=)|(\&([\w\-]+)=)/g;
const queryKeys = [];
let match;
while ((match = queryStringRegex.exec(body)) !== null) {
if (match[2]) queryKeys.push(match[2]);
if (match[4]) queryKeys.push(match[4]);
}
return queryKeys;
}
/**
* Utility function to find specific HTML attributes (name, id) in the body.
* @param {string} body - The body of the page to search.
* @param {string} attribute - The HTML attribute to search for ('name' or 'id').
* @returns {Array} - An array of attribute values.
*/
function findHtmlAttributes(body, attribute) {
const regex = new RegExp(`${attribute}\\s*=\\s*["|']([\\w\\-]+)["|']`, 'g');
const attributes = [];
let match;
while ((match = regex.exec(body)) !== null) {
attributes.push(match[1]);
}
return attributes;
}
async function correctUrls(arr) {
// Check if arr is an array and exists
if (Array.isArray(arr)) {
return arr.map(item => {
if (item.startsWith('https://') || item.startsWith('http://')) {
// If the URL starts with "https://" or "http://", leave it as is
return item;
} else if (item.startsWith('/')) {
// If the URL starts with "/", remove the "/" and add window.location.origin to the start
return window.location.origin + item;
} else {
// If the URL doesn't start with "/" or "https://", add window.location.origin to the start
return window.location.origin + '/' + item;
}
});
}
// Return undefined or the original value if arr is not provided or not an array
return arr;
}
async function findJSFiles(body) {
const regex = /<script[^>]*\s+src=["']([^"']+\.js)["'][^>]*>/gi;
let match;
const sources = [];
// Extract all .js file sources from the HTML
while ((match = regex.exec(body)) !== null) {
sources.push(match[1]); // match[1] contains the .js file source
}
try {
// Wait for corrected URLs (if correctUrls is asynchronous)
const correctedSources = await correctUrls(sources);
// Check if correctedSources is a valid array and not empty
if (Array.isArray(correctedSources) && correctedSources.length > 0) {
// Iterate over each URL and fetch its content
for (const url of correctedSources) {
try {
const response = await fetch(url);
if (response.ok) {
const body = await response.text();
extractParameters(body); // Pass the body to your function
} else {
console.error(`Failed to fetch ${url}: ${response.status}`);
}
} catch (error) {
console.error(`Error fetching ${url}: ${error}`);
}
}
} else {
console.log('No valid URLs to process');
}
} catch (error) {
console.error('Error processing corrected URLs:', error);
}
}
// Global array for storing parameters
let params = [];
/**
* Main function to extract parameters from the page (URL and body).
* @returns {Promise} - A promise that resolves after parameters are saved to storage.
*/
async function extractParameters(body) {
const url = window.location.href;
const allParameters = [];
// Extract various types of parameters
allParameters.push(...extractQueryStringKeys(url));
allParameters.push(...findVariableNames(body));
allParameters.push(...findJsonKeys(body));
allParameters.push(...findStringFormatVariables(body));
allParameters.push(...findFunctionParameters(body));
allParameters.push(...findPathParameters(body));
allParameters.push(...findQueryStringKeys(body));
// Check content type and decide if HTML attributes should be included
const contentType = document.contentType || ""; // Replace with actual logic to check headers
if (contentType !== "application/javascript") {
allParameters.push(...findHtmlAttributes(body, 'name'));
allParameters.push(...findHtmlAttributes(body, 'id'));
}
// Remove duplicates and filter out empty values
const uniqueParameters = [...new Set(allParameters.filter(param => param))];
// Add only unique parameters to the global params array
uniqueParameters.forEach(param => {
if (!params.includes(param)) { // Check if the param is not already in the array
params.push(param);
}
});
// Restore checkbox state
browserAPI.storage.local.get(`regex_checkbox_${window.location.hostname}`, async function(result){
const regexCheckBoxState = result[`regex_checkbox_${window.location.hostname}`];
if(regexCheckBoxState !== null){
if(regexCheckBoxState === true){
// Get regex pattern
await browserAPI.storage.local.get(`regex_pattern_${window.location.hostname}`, (result) => {
const regexPattern = result[`regex_pattern_${window.location.hostname}`];
if(regexPattern !== null){
params = params.filter(param => param.match(regexPattern));
// Save matched parameters to storage, keyed by hostname
const key = `${window.location.hostname}_all`;
browserAPI.storage.local.set({ [key]: params });
console.log(`Parameters saved for ${key}`);
} else {
// Save parameters to storage, keyed by hostname
const key = `${window.location.hostname}_all`;
browserAPI.storage.local.set({ [key]: params });
console.log(`Parameters saved for ${key}`);
}
});
} else {
// Save parameters to storage, keyed by hostname
const key = `${window.location.hostname}_all`;
browserAPI.storage.local.set({ [key]: params });
console.log(`Parameters saved for ${key}`);
}
} else {
// Save parameters to storage, keyed by hostname
const key = `${window.location.hostname}_all`;
browserAPI.storage.local.set({ [key]: params });
console.log(`Parameters saved for ${key}`);
}
// Retrieve the checkbox state from chrome.storage.local
await browserAPI.storage.local.get(`ref_checkbox_${window.location.hostname}`, function (result) {
const refCheckBoxIsChecked = result[`ref_checkbox_${window.location.hostname}`];
// Auto reflection test if the checkbox is checked
if (refCheckBoxIsChecked === true){
setTimeout(async () => await sendRequests(params, window.location.href), 0);
}
});
});
// Check if passive log checkbox is checked
await browserAPI.storage.local.get(`log_checkbox_${window.location.hostname}`, async function(result){
const logCheckBoxIsChecked = result[`log_checkbox_${window.location.hostname}`];
if(logCheckBoxIsChecked !== null){
if(logCheckBoxIsChecked === true){
// Log params
passiveLog();
}
}
});
}
/**
* Generate a random alphanumeric string of the given length.
* @param {number} length - The length of the random string to generate.
* @returns {string} - A random string of the specified length.
*/
function generateRandomString(length) {
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
/**
* Function to send requests with generated random parameters.
* @param {Array} parameters - The list of parameters to include in the request.
* @param {string} baseUrl - The base URL to append parameters to.
*/
async function sendRequests(parameters, baseUrl) {
const chunkSize = 30;
const reflections = [];
// Split parameters into chunks and send requests
for (let i = 0; i < parameters.length; i += chunkSize) {
const chunk = parameters.slice(i, i + chunkSize);
// Create random values for each parameter
const paramValues = chunk.map(param => {
const randomValue = generateRandomString(5);
return { param, randomValue };
});
// Construct the query string with random values for the GET request
const queryString = paramValues.map(({ param, randomValue }) => `${encodeURIComponent(param)}=${encodeURIComponent(randomValue)}`).join('&');
const url = `${baseUrl}?${queryString}`;
console.log(url);
// GET request
try {
const responseGet = await fetch(url);
const textGet = await responseGet.text();
// Check for parameter reflection in the GET response
paramValues.forEach(({ param, randomValue }) => {
if (textGet.includes(randomValue)) {
reflections.push(param);
}
});
} catch (error) {
console.error(`Error fetching GET URL: ${url}`, error);
}
// POST request
const formData = new URLSearchParams();
paramValues.forEach(({ param, randomValue }) => {
formData.append(param, randomValue);
});
try {
const responsePost = await fetch(url, {
method: 'POST',
body: formData
});
const textPost = await responsePost.text();
// Check for parameter reflection in the POST response
paramValues.forEach(({ param, randomValue }) => {
if (textPost.includes(randomValue)) {
// Check if the parameter is already in the reflections array
if (!reflections.includes(param)) {
reflections.push(param); // Add the parameter if it's not already in the array
}
}
});
} catch (error) {
console.error(`Error sending POST request to: ${url}`, error);
}
}
// Save reflections to storage
const key = `${window.location.hostname}_refs`;
browserAPI.storage.local.set({ [key]: reflections });
console.log('Reflections saved');
// Notify the popup about the status
const port = browserAPI.runtime.connect({ name: "content-to-popup" });
port.postMessage({ state: "checked" });
}
// Passively logs parameters into an array
async function passiveLog() {
// Get current logged parameters
await browserAPI.storage.local.get(`logged_params_${window.location.hostname}`, async function(result) {
// Ensure result is an object and handle undefined/null safely
let currentLogs = result[`logged_params_${window.location.hostname}`];
console.log(currentLogs);
if (currentLogs === undefined || currentLogs === null) {
currentLogs = {}; // Initialize to an empty object if null or undefined
}
// Check if currentLogs is an empty object
if (Object.keys(currentLogs).length !== 0) {
// Save parameters for this page
currentLogs[`${window.location}`] = params; // Ensure 'params' is defined
await browserAPI.storage.local.set({[`logged_params_${window.location.hostname}`]: currentLogs});
} else {
const loggedParams = {};
loggedParams[`${window.location}`] = params; // Ensure 'params' is defined
console.log(loggedParams);
await browserAPI.storage.local.set({[`logged_params_${window.location.hostname}`]: loggedParams});
}
});
}
// Listen for messages from the popup
browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Checking
if (message.type === 'sendMessageToContent' && message.message === "check") {
setTimeout(() => sendRequests(params, window.location.href), 0);
}
});
const body = document.documentElement.innerHTML;
// Execute the extractParameters function when the page is fully loaded
window.addEventListener('load', extractParameters(body));
// Clear storage when the page is unloaded
window.addEventListener('beforeunload', async function () {
const refsKey = `${window.location.hostname}_refs`;
const allKey = `${window.location.hostname}_all`;
// Remove stored parameters and reflections
await browserAPI.storage.local.remove(refsKey);
await browserAPI.storage.local.remove(allKey);
console.log('Storage cleared');
});
findJSFiles(body);