@shriyanss/js-recon 1.0.0 → 1.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.api_gateway_config.json +1 -0
- package/.github/workflows/npm-publish.yml +35 -0
- package/.github/workflows/prettier.yaml +44 -0
- package/.prettierignore +2 -0
- package/.prettierrc +4 -0
- package/.resp_cache.json +1 -0
- package/.vscode/launch.json +27 -0
- package/CHANGELOG.md +40 -0
- package/README.md +35 -140
- package/build/api_gateway/checkFeasibility.js +32 -0
- package/build/api_gateway/checkFeasibility.js.map +1 -0
- package/build/api_gateway/checkFireWallBlocking.js +24 -0
- package/build/api_gateway/checkFireWallBlocking.js.map +1 -0
- package/build/api_gateway/genReq.js +202 -0
- package/build/api_gateway/genReq.js.map +1 -0
- package/build/api_gateway/index.js +277 -0
- package/build/api_gateway/index.js.map +1 -0
- package/build/endpoints/gen_report/gen_json.js +22 -0
- package/build/endpoints/gen_report/gen_json.js.map +1 -0
- package/build/endpoints/gen_report/gen_markdown.js +66 -0
- package/build/endpoints/gen_report/gen_markdown.js.map +1 -0
- package/build/endpoints/gen_report/utility/iterate_n_store.js +46 -0
- package/build/endpoints/gen_report/utility/iterate_n_store.js.map +1 -0
- package/build/endpoints/index.js +89 -0
- package/build/endpoints/index.js.map +1 -0
- package/build/endpoints/next_js/client_jsFilesHref.js +91 -0
- package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -0
- package/build/endpoints/next_js/client_jsonParse.js +75 -0
- package/build/endpoints/next_js/client_jsonParse.js.map +1 -0
- package/build/endpoints/next_js/client_subsequentRequests.js +199 -0
- package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -0
- package/build/endpoints/next_js/getWebpacks.js +45 -0
- package/build/endpoints/next_js/getWebpacks.js.map +1 -0
- package/build/globalConfig.js +11 -0
- package/build/globalConfig.js.map +1 -0
- package/build/index.js +166 -0
- package/build/index.js.map +1 -0
- package/build/lazyLoad/downloadFilesUtil.js +128 -0
- package/build/lazyLoad/downloadFilesUtil.js.map +1 -0
- package/build/lazyLoad/downloadLoadedJsUtil.js +51 -0
- package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -0
- package/build/lazyLoad/globals.js +25 -0
- package/build/lazyLoad/globals.js.map +1 -0
- package/build/lazyLoad/index.js +171 -0
- package/build/lazyLoad/index.js.map +1 -0
- package/build/lazyLoad/next_js/next_GetJSScript.js +94 -0
- package/build/lazyLoad/next_js/next_GetJSScript.js.map +1 -0
- package/build/lazyLoad/next_js/next_GetLazyResources.js +202 -0
- package/build/lazyLoad/next_js/next_GetLazyResources.js.map +1 -0
- package/build/lazyLoad/next_js/next_SubsequentRequests.js +120 -0
- package/build/lazyLoad/next_js/next_SubsequentRequests.js.map +1 -0
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js +188 -0
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -0
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +75 -0
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -0
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +94 -0
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js +68 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -0
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +95 -0
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -0
- package/build/map/index.js +58 -0
- package/build/map/index.js.map +1 -0
- package/build/map/next_js/getFetchInstances.js +105 -0
- package/build/map/next_js/getFetchInstances.js.map +1 -0
- package/build/map/next_js/getWebpackConnections.js +224 -0
- package/build/map/next_js/getWebpackConnections.js.map +1 -0
- package/build/map/next_js/interactive.js +32 -0
- package/build/map/next_js/interactive.js.map +1 -0
- package/build/map/next_js/interactive_helpers/commandHandler.js +190 -0
- package/build/map/next_js/interactive_helpers/commandHandler.js.map +1 -0
- package/build/map/next_js/interactive_helpers/commandHelpers.js +91 -0
- package/build/map/next_js/interactive_helpers/commandHelpers.js.map +1 -0
- package/build/map/next_js/interactive_helpers/helpMenu.js +11 -0
- package/build/map/next_js/interactive_helpers/helpMenu.js.map +1 -0
- package/build/map/next_js/interactive_helpers/keybindings.js +80 -0
- package/build/map/next_js/interactive_helpers/keybindings.js.map +1 -0
- package/build/map/next_js/interactive_helpers/printer.js +17 -0
- package/build/map/next_js/interactive_helpers/printer.js.map +1 -0
- package/build/map/next_js/interactive_helpers/ui.js +81 -0
- package/build/map/next_js/interactive_helpers/ui.js.map +1 -0
- package/build/map/next_js/resolveFetch.js +201 -0
- package/build/map/next_js/resolveFetch.js.map +1 -0
- package/build/run/index.js +62 -0
- package/build/run/index.js.map +1 -0
- package/build/strings/index.js +235 -0
- package/build/strings/index.js.map +1 -0
- package/build/strings/openapi.js +55 -0
- package/build/strings/openapi.js.map +1 -0
- package/build/strings/permutate.js +55 -0
- package/build/strings/permutate.js.map +1 -0
- package/build/strings/secrets.js +89 -0
- package/build/strings/secrets.js.map +1 -0
- package/build/techDetect/index.js +224 -0
- package/build/techDetect/index.js.map +1 -0
- package/build/utility/ai.js +69 -0
- package/build/utility/ai.js.map +1 -0
- package/build/utility/globals.js +84 -0
- package/build/utility/globals.js.map +1 -0
- package/build/utility/interfaces.js +2 -0
- package/build/utility/interfaces.js.map +1 -0
- package/build/utility/makeReq.js +265 -0
- package/build/utility/makeReq.js.map +1 -0
- package/build/utility/resolvePath.js +44 -0
- package/build/utility/resolvePath.js.map +1 -0
- package/{utility → build/utility}/runSandboxed.js +10 -13
- package/build/utility/runSandboxed.js.map +1 -0
- package/{utility → build/utility}/urlUtils.js +9 -11
- package/build/utility/urlUtils.js.map +1 -0
- package/docs/README.md +20 -0
- package/docs/api-gateway.md +68 -0
- package/docs/endpoints.md +49 -0
- package/docs/example-scenario.md +258 -0
- package/docs/interactive-mode.md +76 -0
- package/docs/lazyload.md +56 -0
- package/docs/map.md +53 -0
- package/docs/run.md +54 -0
- package/docs/strings.md +75 -0
- package/endpoints.json +77 -0
- package/extracted_urls-openapi.json +225 -0
- package/extracted_urls-swagger.json +225 -0
- package/extracted_urls.json +47 -0
- package/extracted_urls.txt +296 -0
- package/mapped.json +3413 -0
- package/output/ss0x00.com/_next/data/k7xKVnxmboK4SktY2dZWt/index.json +971 -0
- package/output/ss0x00.com/_next/static/chunks/12.7e6d2ac6e1808fc2.js +247 -0
- package/output/ss0x00.com/_next/static/chunks/128.160aa801ef0445bc.js +1074 -0
- package/output/ss0x00.com/_next/static/chunks/132.55df84f7707fc278.js +102 -0
- package/output/ss0x00.com/_next/static/chunks/142.77038c55d9ec10ba.js +96 -0
- package/output/ss0x00.com/_next/static/chunks/215.321479e91d330bfa.js +228 -0
- package/output/ss0x00.com/_next/static/chunks/229.097c396d86b4a882.js +458 -0
- package/output/ss0x00.com/_next/static/chunks/257.5fd052aa4ef06ef9.js +1327 -0
- package/output/ss0x00.com/_next/static/chunks/268.72cb3779f66db70b.js +10520 -0
- package/output/ss0x00.com/_next/static/chunks/320.57d528b0e9bf86f0.js +186 -0
- package/output/ss0x00.com/_next/static/chunks/325.302a44b604c35f17.js +88 -0
- package/output/ss0x00.com/_next/static/chunks/328.e4a0307a4fddf318.js +248 -0
- package/output/ss0x00.com/_next/static/chunks/432.3621f17504ef18f2.js +443 -0
- package/output/ss0x00.com/_next/static/chunks/44.e90dd963003a3d43.js +1094 -0
- package/output/ss0x00.com/_next/static/chunks/442.8c054f100f9e5e50.js +1082 -0
- package/output/ss0x00.com/_next/static/chunks/460.f8db9a5142598e2c.js +466 -0
- package/output/ss0x00.com/_next/static/chunks/487.05ca55420459c002.js +78 -0
- package/output/ss0x00.com/_next/static/chunks/567.1909a6b0a920114b.js +1374 -0
- package/output/ss0x00.com/_next/static/chunks/586.802fc9214d87fb29.js +752 -0
- package/output/ss0x00.com/_next/static/chunks/620.a2a3a6b94d30a4c8.js +1037 -0
- package/output/ss0x00.com/_next/static/chunks/642.6b3e487c9604cbb8.js +1628 -0
- package/output/ss0x00.com/_next/static/chunks/673.e5d77887e5c6a68c.js +1045 -0
- package/output/ss0x00.com/_next/static/chunks/684.8b8e52baca70524b.js +96 -0
- package/output/ss0x00.com/_next/static/chunks/686.79480519e5ccfb77.js +296 -0
- package/output/ss0x00.com/_next/static/chunks/756.7a3878a2e6765be7.js +504 -0
- package/output/ss0x00.com/_next/static/chunks/761.7bea7516c5d22b2a.js +1485 -0
- package/output/ss0x00.com/_next/static/chunks/794.e079ef369b41a3c5.js +1350 -0
- package/output/ss0x00.com/_next/static/chunks/826.31ba213e1d023c68.js +1031 -0
- package/output/ss0x00.com/_next/static/chunks/847.d8397a73efc81848.js +1068 -0
- package/output/ss0x00.com/_next/static/chunks/848.5feaeee1e2624aea.js +132 -0
- package/output/ss0x00.com/_next/static/chunks/850.ecc7c3c3d787ee03.js +1472 -0
- package/output/ss0x00.com/_next/static/chunks/853.50b9eb60b7d0e83c.js +1472 -0
- package/output/ss0x00.com/_next/static/chunks/856.7a7bb6c3c7bfc2ba.js +1320 -0
- package/output/ss0x00.com/_next/static/chunks/859.df4bd45c03a65f53.js +1306 -0
- package/output/ss0x00.com/_next/static/chunks/867.e8633955a147c978.js +541 -0
- package/output/ss0x00.com/_next/static/chunks/921.c3123f20a4c8d53c.js +96 -0
- package/output/ss0x00.com/_next/static/chunks/932.25cb421c466c99cb.js +999 -0
- package/output/ss0x00.com/_next/static/chunks/framework-64ad27b21261a9ce.js +9189 -0
- package/output/ss0x00.com/_next/static/chunks/main-710ab85aa9a8f10d.js +6583 -0
- package/output/ss0x00.com/_next/static/chunks/pages/5D-af5a23529ce3c337.js +486 -0
- package/output/ss0x00.com/_next/static/chunks/pages/_app-c449865c8af1faa0.js +39 -0
- package/output/ss0x00.com/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js +6383 -0
- package/output/ss0x00.com/_next/static/chunks/webpack-efff35ee26971294.js +271 -0
- package/output/ss0x00.com/_next/static/k7xKVnxmboK4SktY2dZWt/_buildManifest.js +8 -0
- package/output/ss0x00.com/_next/static/k7xKVnxmboK4SktY2dZWt/_ssgManifest.js +3 -0
- package/output/ss0x00.com/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js +82 -0
- package/package.json +50 -38
- package/strings.json +7407 -0
- package/api_gateway/checkFeasibility.js +0 -25
- package/api_gateway/checkFireWallBlocking.js +0 -17
- package/api_gateway/genReq.js +0 -214
- package/api_gateway/index.js +0 -325
- package/endpoints/index.js +0 -7
- package/globalConfig.js +0 -12
- package/index.js +0 -69
- package/lazyLoad/downloadFilesUtil.js +0 -122
- package/lazyLoad/downloadLoadedJsUtil.js +0 -54
- package/lazyLoad/globals.js +0 -15
- package/lazyLoad/index.js +0 -167
- package/lazyLoad/next_js/next_GetJSScript.js +0 -99
- package/lazyLoad/next_js/next_GetLazyResources.js +0 -201
- package/lazyLoad/next_js/next_SubsequentRequests.js +0 -138
- package/lazyLoad/nuxt_js/nuxt_astParse.js +0 -194
- package/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +0 -77
- package/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +0 -99
- package/research/firewall_bypass.md +0 -38
- package/research/next_js.md +0 -116
- package/research/nuxt_js.md +0 -125
- package/research/vue_js.md +0 -9
- package/strings/index.js +0 -145
- package/techDetect/index.js +0 -156
- package/utility/globals.js +0 -6
- package/utility/makeReq.js +0 -179
- package/utility/resolvePath.js +0 -43
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { getURLDirectory } from "../../utility/urlUtils.js";
|
|
5
|
-
// custom request module
|
|
6
|
-
import makeRequest from "../../utility/makeReq.js";
|
|
7
|
-
|
|
8
|
-
let queue = [];
|
|
9
|
-
let max_queue;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Given a string of JS content, it finds all the static files used in the
|
|
13
|
-
* file, and returns them as an array.
|
|
14
|
-
*
|
|
15
|
-
* @param {string} js_content - The string of JS content to search through.
|
|
16
|
-
*
|
|
17
|
-
* @returns {string[]} An array of strings, each string being a static file
|
|
18
|
-
* path.
|
|
19
|
-
*/
|
|
20
|
-
const findStaticFiles = async (js_content) => {
|
|
21
|
-
// do some regex-ing
|
|
22
|
-
const matches = [
|
|
23
|
-
...js_content.matchAll(/\/?static\/chunks\/[a-zA-Z0-9\._\-\/]+\.js/g),
|
|
24
|
-
];
|
|
25
|
-
// return matches
|
|
26
|
-
|
|
27
|
-
let toReturn = [];
|
|
28
|
-
|
|
29
|
-
for (const match of matches) {
|
|
30
|
-
toReturn.push(match[0]);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return toReturn;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const getURLDirectoryServer = (urlString) => {
|
|
37
|
-
const url = new URL(urlString);
|
|
38
|
-
const pathParts = url.pathname.split("/").filter(Boolean); // ['business', 'api']
|
|
39
|
-
pathParts.pop(); // Remove 'api'
|
|
40
|
-
|
|
41
|
-
const newPath = "/" + pathParts.join("/"); // '/business'
|
|
42
|
-
return `${url.origin}${newPath}`; // 'http://something.com/business'
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const subsequentRequests = async (url, urlsFile, threads, output, js_urls) => {
|
|
46
|
-
max_queue = threads;
|
|
47
|
-
let staticJSURLs = [];
|
|
48
|
-
|
|
49
|
-
console.log(chalk.cyan(`[i] Fetching JS files from subsequent requests`));
|
|
50
|
-
|
|
51
|
-
// open the urls file, and load the paths (JSON)
|
|
52
|
-
const endpoints = JSON.parse(fs.readFileSync(urlsFile, "utf-8")).paths;
|
|
53
|
-
|
|
54
|
-
let js_contents = {};
|
|
55
|
-
|
|
56
|
-
// make requests to all of them with the special header
|
|
57
|
-
const reqPromises = endpoints.map(async (endpoint) => {
|
|
58
|
-
const reqUrl = url + endpoint;
|
|
59
|
-
try {
|
|
60
|
-
// delay in case over the thread count
|
|
61
|
-
while (queue >= max_queue) {
|
|
62
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
63
|
-
}
|
|
64
|
-
queue++;
|
|
65
|
-
|
|
66
|
-
const res = await makeRequest(reqUrl, {
|
|
67
|
-
headers: {
|
|
68
|
-
RSC: "1",
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
res &&
|
|
74
|
-
res.status === 200 &&
|
|
75
|
-
res.headers.get("content-type").includes("text/x-component")
|
|
76
|
-
) {
|
|
77
|
-
const text = await res.text();
|
|
78
|
-
js_contents[endpoint] = text;
|
|
79
|
-
|
|
80
|
-
const { host, directory } = getURLDirectory(reqUrl);
|
|
81
|
-
|
|
82
|
-
// save the contents to "___subsequent_requests/"
|
|
83
|
-
// make the subsequent_requests directory if it doesn't exist
|
|
84
|
-
|
|
85
|
-
const output_path = path.join(
|
|
86
|
-
output,
|
|
87
|
-
host,
|
|
88
|
-
"___subsequent_requests",
|
|
89
|
-
directory,
|
|
90
|
-
);
|
|
91
|
-
if (!fs.existsSync(output_path)) {
|
|
92
|
-
fs.mkdirSync(output_path);
|
|
93
|
-
}
|
|
94
|
-
fs.writeFileSync(path.join(output_path, "index.js"), text);
|
|
95
|
-
|
|
96
|
-
// find the static ones from the JS resp
|
|
97
|
-
const staticFiles = await findStaticFiles(text);
|
|
98
|
-
|
|
99
|
-
// go through each file and get the absolute path of those
|
|
100
|
-
const absolutePaths = staticFiles.map((file) => {
|
|
101
|
-
// go through existing JS URLs found
|
|
102
|
-
let js_path_dir;
|
|
103
|
-
for (const js_url of js_urls) {
|
|
104
|
-
if (
|
|
105
|
-
!js_path_dir &&
|
|
106
|
-
new URL(js_url).host === new URL(url).host &&
|
|
107
|
-
new URL(js_url).pathname.includes("static/chunks/")
|
|
108
|
-
) {
|
|
109
|
-
js_path_dir = js_url.replace(/\/[^\/]+\.js.*$/, "");
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return js_path_dir.replace("static/chunks", "") + file;
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Filter out paths that are already in js_urls before pushing to staticJSURLs
|
|
116
|
-
const newPaths = absolutePaths.filter(path => !js_urls.includes(path));
|
|
117
|
-
if (newPaths.length > 0) {
|
|
118
|
-
staticJSURLs.push(...newPaths);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
queue--;
|
|
123
|
-
} catch (e) {
|
|
124
|
-
queue--;
|
|
125
|
-
console.log(chalk.red(`[!] Error fetching ${reqUrl}: ${e}`));
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
await Promise.all(reqPromises);
|
|
130
|
-
|
|
131
|
-
staticJSURLs = [...new Set(staticJSURLs)];
|
|
132
|
-
|
|
133
|
-
console.log(chalk.green(`[✓] Found ${staticJSURLs.length} JS chunks from subsequent requests`));
|
|
134
|
-
|
|
135
|
-
return staticJSURLs;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
export default subsequentRequests;
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import parser from "@babel/parser";
|
|
2
|
-
import _traverse from "@babel/traverse";
|
|
3
|
-
const traverse = _traverse.default;
|
|
4
|
-
import execFunc from "../../utility/runSandboxed.js";
|
|
5
|
-
import makeRequest from "../../utility/makeReq.js";
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
import inquirer from "inquirer";
|
|
8
|
-
import t from "@babel/types";
|
|
9
|
-
import resolvePath from "../../utility/resolvePath.js";
|
|
10
|
-
|
|
11
|
-
const nuxt_astParse = async (url) => {
|
|
12
|
-
let filesFound = [];
|
|
13
|
-
const resp = await makeRequest(url);
|
|
14
|
-
const body = await resp.text();
|
|
15
|
-
|
|
16
|
-
let ast;
|
|
17
|
-
try {
|
|
18
|
-
ast = parser.parse(body, {
|
|
19
|
-
sourceType: "module",
|
|
20
|
-
plugins: ["jsx", "typescript"],
|
|
21
|
-
});
|
|
22
|
-
} catch (error) {
|
|
23
|
-
console.log(chalk.red("[!] Error parsing JS file: ", url));
|
|
24
|
-
return filesFound;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let functions = [];
|
|
28
|
-
|
|
29
|
-
traverse(ast, {
|
|
30
|
-
FunctionDeclaration(path) {
|
|
31
|
-
functions.push({
|
|
32
|
-
name: path.node.id?.name || "(anonymous)",
|
|
33
|
-
type: "FunctionDeclaration",
|
|
34
|
-
source: body.slice(path.node.start, path.node.end),
|
|
35
|
-
});
|
|
36
|
-
},
|
|
37
|
-
FunctionExpression(path) {
|
|
38
|
-
functions.push({
|
|
39
|
-
name: path.parent.id?.name || "(anonymous)",
|
|
40
|
-
type: "FunctionExpression",
|
|
41
|
-
source: body.slice(path.node.start, path.node.end),
|
|
42
|
-
});
|
|
43
|
-
},
|
|
44
|
-
ArrowFunctionExpression(path) {
|
|
45
|
-
functions.push({
|
|
46
|
-
name: path.parent.id?.name || "(anonymous)",
|
|
47
|
-
type: "ArrowFunctionExpression",
|
|
48
|
-
source: body.slice(path.node.start, path.node.end),
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
ObjectMethod(path) {
|
|
52
|
-
functions.push({
|
|
53
|
-
name: path.node.key.name,
|
|
54
|
-
type: "ObjectMethod",
|
|
55
|
-
source: body.slice(path.node.start, path.node.end),
|
|
56
|
-
});
|
|
57
|
-
},
|
|
58
|
-
ClassMethod(path) {
|
|
59
|
-
functions.push({
|
|
60
|
-
name: path.node.key.name,
|
|
61
|
-
type: "ClassMethod",
|
|
62
|
-
source: body.slice(path.node.start, path.node.end),
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// iterate through the functions, and find out the one that ends with ".js"
|
|
68
|
-
|
|
69
|
-
for (const func of functions) {
|
|
70
|
-
if (func.source.match(/"\.js".{0,15}$/)) {
|
|
71
|
-
console.log(
|
|
72
|
-
chalk.green(`[✓] Found JS chunk having the following source:`),
|
|
73
|
-
);
|
|
74
|
-
console.log(chalk.yellow(func.source));
|
|
75
|
-
|
|
76
|
-
// ask for confirmation, then proceed
|
|
77
|
-
const askCorrectFuncConfirmation = async () => {
|
|
78
|
-
const { value } = await inquirer.prompt([
|
|
79
|
-
{
|
|
80
|
-
type: "confirm",
|
|
81
|
-
name: "value",
|
|
82
|
-
message: "Is this the correct function?",
|
|
83
|
-
default: true,
|
|
84
|
-
},
|
|
85
|
-
]);
|
|
86
|
-
return value;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const user_verified = await askCorrectFuncConfirmation();
|
|
90
|
-
if (user_verified === true) {
|
|
91
|
-
console.log(
|
|
92
|
-
chalk.cyan(
|
|
93
|
-
"[i] Proceeding with the selected function to fetch files",
|
|
94
|
-
),
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
// get the value of the unknown vars
|
|
98
|
-
// first, get the name of the unknown function
|
|
99
|
-
const unknownVarAst = parser.parse(`(${func.source})`, {
|
|
100
|
-
sourceType: "script",
|
|
101
|
-
plugins: ["jsx", "typescript"],
|
|
102
|
-
});
|
|
103
|
-
let memberExpressions = [];
|
|
104
|
-
traverse(unknownVarAst, {
|
|
105
|
-
MemberExpression(path) {
|
|
106
|
-
// Only collect identifiers like f.p (not obj["x"])
|
|
107
|
-
if (
|
|
108
|
-
t.isIdentifier(path.node.object) &&
|
|
109
|
-
t.isIdentifier(path.node.property) &&
|
|
110
|
-
!path.node.computed // ignore obj["x"]
|
|
111
|
-
) {
|
|
112
|
-
const objName = path.node.object.name;
|
|
113
|
-
const propName = path.node.property.name;
|
|
114
|
-
memberExpressions.push(`${objName}.${propName}`);
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const unknownVar = memberExpressions[0].split(".");
|
|
120
|
-
|
|
121
|
-
// now, resolve the value of this unknown var
|
|
122
|
-
let unknownVarValue;
|
|
123
|
-
|
|
124
|
-
traverse(ast, {
|
|
125
|
-
AssignmentExpression(path) {
|
|
126
|
-
const { left, right } = path.node;
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
t.isMemberExpression(left) &&
|
|
130
|
-
t.isIdentifier(left.object, { name: unknownVar[0] }) &&
|
|
131
|
-
t.isIdentifier(left.property, { name: unknownVar[1] }) &&
|
|
132
|
-
!left.computed
|
|
133
|
-
) {
|
|
134
|
-
if (t.isStringLiteral(right)) {
|
|
135
|
-
unknownVarValue = right.value;
|
|
136
|
-
} else {
|
|
137
|
-
// fallback to source snippet
|
|
138
|
-
unknownVarValue = func.source.slice(right.start, right.end);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// replace the unknown var with the value
|
|
145
|
-
const funcSource = func.source.replace(
|
|
146
|
-
new RegExp(`${unknownVar[0]}.${unknownVar[1]}`),
|
|
147
|
-
`"${unknownVarValue}"`,
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
// continue to executing the function with all possible numbers
|
|
151
|
-
const urlBuilderFunc = `(() => (${funcSource}))()`;
|
|
152
|
-
let js_paths = [];
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
// rather than fuzzing, grep the integers from the func code
|
|
156
|
-
const integers = funcSource.match(/\d+/g);
|
|
157
|
-
if (integers) {
|
|
158
|
-
// Check if integers were found
|
|
159
|
-
// iterate through all integers, and get the output
|
|
160
|
-
for (const i of integers) {
|
|
161
|
-
const output = execFunc(urlBuilderFunc, parseInt(i));
|
|
162
|
-
if (output.includes("undefined")) {
|
|
163
|
-
continue;
|
|
164
|
-
} else {
|
|
165
|
-
js_paths.push(output);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
} catch (error) {
|
|
170
|
-
console.log(chalk.red("[!] Error executing function: ", error));
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (js_paths.length > 0) {
|
|
174
|
-
// iterate through the files, and resolve them
|
|
175
|
-
for (const js_path of js_paths) {
|
|
176
|
-
const resolvedPath = await resolvePath(url, js_path);
|
|
177
|
-
filesFound.push(resolvedPath);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
} else {
|
|
181
|
-
console.log(chalk.red("[!] Not executing function."));
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (filesFound.length > 0) {
|
|
188
|
-
console.log(chalk.green(`[✓] Found ${filesFound.length} JS chunks`));
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return filesFound;
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
export default nuxt_astParse;
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import makeRequest from "../../utility/makeReq.js";
|
|
3
|
-
import { getJsUrls, pushToJsUrls } from "../globals.js";
|
|
4
|
-
import * as cheerio from "cheerio";
|
|
5
|
-
|
|
6
|
-
const nuxt_getFromPageSource = async (url) => {
|
|
7
|
-
console.log(chalk.cyan("[i] Analyzing page source"));
|
|
8
|
-
|
|
9
|
-
// get the page source
|
|
10
|
-
const res = await makeRequest(url);
|
|
11
|
-
const pageSource = await res.text();
|
|
12
|
-
|
|
13
|
-
// cheerio to parse the page source
|
|
14
|
-
const $ = cheerio.load(pageSource);
|
|
15
|
-
|
|
16
|
-
// find all link tags
|
|
17
|
-
const linkTags = $("link");
|
|
18
|
-
|
|
19
|
-
// go through them, and find the ones which have `as=script` attr
|
|
20
|
-
for (const linkTag of linkTags) {
|
|
21
|
-
const asAttr = $(linkTag).attr("as");
|
|
22
|
-
if (asAttr === "script") {
|
|
23
|
-
const hrefAttr = $(linkTag).attr("href");
|
|
24
|
-
if (hrefAttr) {
|
|
25
|
-
// see if it starts with /_nuxt
|
|
26
|
-
if (hrefAttr.startsWith("/_nuxt")) {
|
|
27
|
-
// get the URL root, and append the hrefAttr to it
|
|
28
|
-
const urlRoot = new URL(url).origin;
|
|
29
|
-
pushToJsUrls(urlRoot + hrefAttr);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// now, search all the script tags
|
|
36
|
-
const scriptTags = $("script");
|
|
37
|
-
for (const scriptTag of scriptTags) {
|
|
38
|
-
const src = $(scriptTag).attr("src");
|
|
39
|
-
if (
|
|
40
|
-
src !== undefined &&
|
|
41
|
-
src.match(/(https:\/\/[a-zA-Z0-9_\_\.]+\/.+\.js\??.*|\/.+\.js\??.*)/)
|
|
42
|
-
) {
|
|
43
|
-
if (src.startsWith("http")) {
|
|
44
|
-
if (!getJsUrls().includes(src)) {
|
|
45
|
-
pushToJsUrls(src);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// if the src starts with /, like `/static/js/a.js` find the absolute URL
|
|
49
|
-
else if (src.startsWith("/")) {
|
|
50
|
-
const absoluteUrl = new URL(url).origin + src;
|
|
51
|
-
if (!getJsUrls().includes(absoluteUrl)) {
|
|
52
|
-
pushToJsUrls(absoluteUrl);
|
|
53
|
-
}
|
|
54
|
-
} else if (src.match(/^[^/]/)) {
|
|
55
|
-
// if the src is a relative URL, like `static/js/a.js` find the absolute URL
|
|
56
|
-
// Get directory URL (origin + path without filename)
|
|
57
|
-
const pathParts = new URL(url).pathname.split("/");
|
|
58
|
-
pathParts.pop(); // remove the filename from the path
|
|
59
|
-
const directory = new URL(url).origin + pathParts.join("/") + "/";
|
|
60
|
-
|
|
61
|
-
if (!getJsUrls().includes(directory + src)) {
|
|
62
|
-
pushToJsUrls(directory + src);
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
console.log(
|
|
71
|
-
chalk.green(`[✓] Found ${getJsUrls().length} JS files from the page source`),
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
return getJsUrls();
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export default nuxt_getFromPageSource;
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import makeRequest from "../../utility/makeReq.js";
|
|
3
|
-
import { getJsUrls, pushToJsUrls } from "../globals.js";
|
|
4
|
-
import resolvePath from "../../utility/resolvePath.js";
|
|
5
|
-
|
|
6
|
-
// for parsing
|
|
7
|
-
import parser from "@babel/parser";
|
|
8
|
-
import _traverse from "@babel/traverse";
|
|
9
|
-
const traverse = _traverse.default;
|
|
10
|
-
|
|
11
|
-
let analyzedFiles = [];
|
|
12
|
-
let filesFound = [];
|
|
13
|
-
|
|
14
|
-
const parseJSFileContent = async (content) => {
|
|
15
|
-
try {
|
|
16
|
-
const ast = parser.parse(content, {
|
|
17
|
-
sourceType: "module",
|
|
18
|
-
plugins: ["jsx", "typescript"],
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
let foundJsFiles = {};
|
|
22
|
-
|
|
23
|
-
traverse(ast, {
|
|
24
|
-
StringLiteral(path) {
|
|
25
|
-
const value = path.node.value;
|
|
26
|
-
if (value.startsWith("./") && value.endsWith(".js")) {
|
|
27
|
-
foundJsFiles[value] = value;
|
|
28
|
-
} else if (value.startsWith("../") && value.endsWith(".js")) {
|
|
29
|
-
foundJsFiles[value] = value;
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return foundJsFiles;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const nuxt_stringAnalysisJSFiles = async (url) => {
|
|
41
|
-
console.log(chalk.cyan("[i] Analyzing strings in the files found"));
|
|
42
|
-
|
|
43
|
-
while (true) {
|
|
44
|
-
// get all the JS URLs
|
|
45
|
-
const js_urls = getJsUrls();
|
|
46
|
-
|
|
47
|
-
if (js_urls.length === 0) {
|
|
48
|
-
console.log(chalk.red("[!] No JS files found for string analysis"));
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// if js_urls have everything that is in analyzedFiles, break
|
|
53
|
-
let everythingAnalyzed = true;
|
|
54
|
-
for (const url of js_urls) {
|
|
55
|
-
// if the url is not in analyzedFiles, set everythingAnalyzed to false
|
|
56
|
-
if (!analyzedFiles.includes(url)) {
|
|
57
|
-
everythingAnalyzed = false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// break if everything is analyzed
|
|
62
|
-
if (everythingAnalyzed) {
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// iterate through the JS URLs
|
|
67
|
-
for (const js_url of js_urls) {
|
|
68
|
-
if (analyzedFiles.includes(js_url)) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const response = await makeRequest(js_url);
|
|
73
|
-
const respText = await response.text();
|
|
74
|
-
const foundJsFiles = await parseJSFileContent(respText);
|
|
75
|
-
|
|
76
|
-
// iterate through the foundJsFiles and resolve the paths
|
|
77
|
-
for (const [key, value] of Object.entries(foundJsFiles)) {
|
|
78
|
-
const resolvedPath = await resolvePath(js_url, value);
|
|
79
|
-
if (analyzedFiles.includes(resolvedPath)) {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
pushToJsUrls(resolvedPath);
|
|
83
|
-
filesFound.push(resolvedPath);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
analyzedFiles.push(js_url);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
filesFound = [...new Set(filesFound)];
|
|
91
|
-
|
|
92
|
-
console.log(
|
|
93
|
-
chalk.green(`[✓] Found ${filesFound.length} JS files from string analysis`),
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
return filesFound;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export default nuxt_stringAnalysisJSFiles;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Firewall Bypass
|
|
2
|
-
While the development of this tool, the tester came across several websites using firewall, majorly Cloudflare. A technique has to be developed to bypass this blockage, and to download the JS files.
|
|
3
|
-
|
|
4
|
-
## Cloudflare
|
|
5
|
-
### Detection
|
|
6
|
-
Whenever the client is restricted by Cloudflare, it returns a HTML page with a JS challenge to be solved. However, the JS is can't be executed by Node.JS. It would need a browser environment. Sample HTML page is shown below:
|
|
7
|
-
|
|
8
|
-
```html
|
|
9
|
-
<meta http-equiv="refresh"
|
|
10
|
-
content="5; URL='/?bm-verify=--snip--'" />
|
|
11
|
-
--snip--
|
|
12
|
-
<script> var i = 1749580415; var j = i + Number("7341" + "47171"); </script>
|
|
13
|
-
--snip--
|
|
14
|
-
<script> var xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.addEventListener("loadend", function () { try { var data = JSON.parse(xhr.responseText); if (data.hasOwnProperty('reload')) { if (data["reload"] == true) { window.location.replace(window.location.href.replace(/[&?]bm-verify=[^#]*/, "")); if (window.location.hash) { window.location.reload(); } } } else if (data.hasOwnProperty(--snip </script>
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
### Bypass
|
|
18
|
-
#### #1
|
|
19
|
-
The most common way to bypass this is to use a browser environment, such as a headless browser.
|
|
20
|
-
|
|
21
|
-
To achive this task, a detection mechanism is added in the custom function to make HTTP requests. If CF is detected, it will load the page in a headless browser, and then return the content.
|
|
22
|
-
|
|
23
|
-
#### #2
|
|
24
|
-
Utilizing a browser plugin to fetch the contents is a more advanced way to bypass this. The user can install the plugin in their testing browser/proxy, and as they navigate the site, the plugin will get the contents and send then to the server for processing (it would be like a caching system).
|
|
25
|
-
|
|
26
|
-
#### #3
|
|
27
|
-
The `makeRequest()` can be modified to send headers that a browser would send. These include:
|
|
28
|
-
- `User-Agent` of a browser
|
|
29
|
-
- `Accept`
|
|
30
|
-
- `Accept-Language`
|
|
31
|
-
- `Sec-Fetch-Site: same-origin`
|
|
32
|
-
- `Sec-Fetch-Mode: cors`
|
|
33
|
-
- `Sec-Fetch-Dest: empty`
|
|
34
|
-
- `Referer`
|
|
35
|
-
- `Origin`
|
|
36
|
-
|
|
37
|
-
#### #4
|
|
38
|
-
Utilizing AWS API Gateway or similar services to rotate IP addresses can help in bypassing the firewall. This, however, could be blocked by the by CF if it is configured to block requests from AWS or similar ISPs.
|
package/research/next_js.md
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# Next.js Research
|
|
2
|
-
## Tech Detection
|
|
3
|
-
To detect Next.js, following technique(s) can be used:
|
|
4
|
-
- Iterate through all the HTML tags, and find `src`, `srcSet` or `imageSrcSet` attribute with value starting with`/_next/` in the page source
|
|
5
|
-
|
|
6
|
-
## Embedded JS files
|
|
7
|
-
A lot of JS files were identified from the page source by inspecting the value of `src` attribute of `script` tags. These included files like `main.js`, `polyfills.js`, `webpack.js` etc.
|
|
8
|
-
|
|
9
|
-
## Lazy Loaded Files
|
|
10
|
-
### Analysis of [Vercel Docs](https://vercel.com/docs)
|
|
11
|
-
Upon analysis of page source and HTTP requests, it was found that the webpack filename had a pattern of `webpack-<hash>.js`. So, webpack JS files were identified and fetched.
|
|
12
|
-
|
|
13
|
-
Upon inspection of the webpack JS file, it was found that code was distributed into several functions. It was observed that the function responsible for returning the JS path ended with `".js"`. This observation was seen multiple times in the Next.js apps by the developer (aka web researcher), hence it bacame a standard method to get the path of the JS files.
|
|
14
|
-
|
|
15
|
-
### Analysis of [X.ai](https://x.ai)
|
|
16
|
-
It was found that most of the things were done in a similar way, however, some extra chars were present after `".js"` in the function responsible for returning JS path in the webpack JS file.
|
|
17
|
-
|
|
18
|
-
To handle this, the regex was modified to also include some additional characters at the end.
|
|
19
|
-
```js
|
|
20
|
-
.match(/"\.js".{0,15}$/)
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Also, it was found that very less URLs were present in the `webpack.js` file (4 at the time of analysis). Upon further inspection, it was found that the URLs are present in the inline `<script>` tags returned in the page source. For example:
|
|
24
|
-
```
|
|
25
|
-
self.__next_f.push([1,"14:I[41498,[\"3867\",\"static/chunks/b6d67c9f-618ea7c61a79562d.js\",\"5017\",\"static/chunks/622eaf3d-350d2142e11f9e13.js\",\"1889\",\"static/chunks/0fd4459a-4e25f772815e6f19.js\",\"4406\",\"static/chunks/4406-fdbbb31c90e98725.js\",\"8627\",\"static/chunks/8627-54829c23d6b1a53f.js\",\"6520\",\"static/chunks/6520-f51ddcfa4e30ebc8.js\",\"1296\",\"static/chunks/1296-b8c2dd03773a40db.js\",\"8922\",\"static/chunks/8922-f01acc4fc5fecfad.js\",\"6929\",\"static/chunks/6929-a636a59ffdb51b17.js\",\"2601\"......snip....../chunks/b6d67c9f-"])
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
To handle this, a feature was implemented to get the JS files from the inline `<script>` tags as well.
|
|
29
|
-
|
|
30
|
-
Upon researching further, it was found that additional JS files were being returned in the subsequent HTTP requests. For example, the file at `https://x.ai/_next/static/chunks/app/careers/page-9e04dce6fed05790.js` was loaded from a request to `https://x.ai/careers?_rsc=jwl50`.
|
|
31
|
-
|
|
32
|
-
Request to `https://x.ai/careers?_rsc=jwl50` should be made with a special header called `RSC: 1` in order to get access to additional JS file paths. This request can be also sent to `/` with the same header to get more files.
|
|
33
|
-
|
|
34
|
-
Possible solution: To handle this, the strings can be extracted from the page sources, and then requests to them can be made along with this request header. If it returns a `200 OK`, then it can be also added to the list of JS files.
|
|
35
|
-
|
|
36
|
-
### Analysis of [OpenAI](https://openai.com)
|
|
37
|
-
Upon inspection of HTTP requests, a similar behavior of sending the requests with `RSC: 1` header to get additional JS files was found.
|
|
38
|
-
|
|
39
|
-
However, a slight difference was found. When sending the request to `/business` with the same header, the server returned a different (`308 Permanent Redirect` to `/business/`) response. This can be handled by sending the request to `/business/` with the same header.
|
|
40
|
-
|
|
41
|
-
Additionally, it was noticed that when the requests were sent without the `RSC: 1` header, the request got blocked by Cloudflare, and it resulted in a `403 Forbidden` response with a message `Just a moment...`. This could indicate a potential firewall bypass
|
|
42
|
-
|
|
43
|
-
## Client-Side Paths/URLs
|
|
44
|
-
Client-side paths/URLs are web addresses handled by the browser using JavaScript, usually without reloading the page. They are used for navigation, API requests, and loading resources dynamically within the client environment.
|
|
45
|
-
|
|
46
|
-
### Analysis of [X.ai](https://x.ai)
|
|
47
|
-
Upon inspection of the client side paths, it was found that they are present in `href` across JS chunks.
|
|
48
|
-
|
|
49
|
-
These are a part of a list, which contains objects with keys like `href` (string), `label` (string), `active` (boolean) and `children` (array of objects of the same type).
|
|
50
|
-
|
|
51
|
-
For instance, here's a example of such a list:
|
|
52
|
-
```js
|
|
53
|
-
let L = [
|
|
54
|
-
{
|
|
55
|
-
href: "/grok",
|
|
56
|
-
label: "Grok",
|
|
57
|
-
active: e.startsWith("/grok"),
|
|
58
|
-
children: [
|
|
59
|
-
{ href: "/grok", label: "For Everyone", active: "/grok" == e },
|
|
60
|
-
{
|
|
61
|
-
href: "/grok/business",
|
|
62
|
-
label: "For Business",
|
|
63
|
-
active: "/grok/business" == e,
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
href: "/api",
|
|
69
|
-
label: "API",
|
|
70
|
-
active: e.startsWith("/api"),
|
|
71
|
-
children: [
|
|
72
|
-
{ href: "/api#capabilities", label: "Overview" },
|
|
73
|
-
{
|
|
74
|
-
href: "https://docs.x.ai/docs/models?cluster=us-east-1#detailed-pricing-for-all-grok-models",
|
|
75
|
-
label: "Pricing",
|
|
76
|
-
external: !0,
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
href: "https://console.x.ai",
|
|
80
|
-
label: "API Console Login",
|
|
81
|
-
external: !0,
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
href: "https://docs.x.ai",
|
|
85
|
-
label: "Documentation",
|
|
86
|
-
external: !0,
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
{ href: "/company", label: "Company", active: "/company" == e },
|
|
91
|
-
{ href: "/colossus", label: "Colossus", active: "/colossus" == e },
|
|
92
|
-
{
|
|
93
|
-
href: "/careers",
|
|
94
|
-
label: "Careers",
|
|
95
|
-
active: e.startsWith("/careers"),
|
|
96
|
-
},
|
|
97
|
-
{ href: "/news", label: "News", active: e.startsWith("/news") },
|
|
98
|
-
]
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Possible methodology: The tool can iterate over all the JS chunks, and find the list of objects with keys like `href` (string), `label` (string), `active` (boolean) and `children` (array of objects of the same type). Then, it can organize them in a report.
|
|
102
|
-
|
|
103
|
-
### Analyis of [1Password](https://1password.com)
|
|
104
|
-
It was found that the client-side paths were stored in mostly stored in a way like:
|
|
105
|
-
```js
|
|
106
|
-
let s = JSON.parse(
|
|
107
|
-
'["/state-of-enterprise-security-report/thank-you/",......"/webinars/1p-quarterly-security-spotlight-and-roadmap-review/thank-you/"]',
|
|
108
|
-
)
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Some similar pattern was also observed in [OpenAI](https://openai.com), however, the full analysis of OpenAI's client-side paths is not done at the time of writing this.
|
|
112
|
-
|
|
113
|
-
It was also found that some paths were stored directly as a list. For example:
|
|
114
|
-
```js
|
|
115
|
-
let n = ["/pricing/xam", "/pricing/password-manager"];
|
|
116
|
-
```
|