@shriyanss/js-recon 1.2.1-alpha.2 → 1.2.1-beta.2
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/.github/workflows/build-and-prettify.yaml +22 -3
- package/.github/workflows/pr_checker.yml +22 -0
- package/.github/workflows/publish-js-recon.yml +1 -2
- package/CHANGELOG.md +31 -0
- package/Dockerfile +1 -0
- package/build/analyze/index.js +1 -1
- package/build/analyze/index.js.map +1 -1
- package/build/api_gateway/genReq.js +1 -1
- package/build/api_gateway/genReq.js.map +1 -1
- package/build/globalConfig.js +1 -1
- package/build/globalConfig.js.map +1 -1
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/lazyLoad/index.js +1 -1
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/next_js/next_SubsequentRequests.js +1 -1
- package/build/lazyLoad/next_js/next_SubsequentRequests.js.map +1 -1
- package/build/lazyLoad/techDetect/index.js +12 -5
- package/build/lazyLoad/techDetect/index.js.map +1 -1
- package/build/map/index.js +3 -3
- package/build/map/index.js.map +1 -1
- package/build/map/next_js/getWebpackConnections.js +1 -1
- package/build/map/next_js/getWebpackConnections.js.map +1 -1
- package/build/map/next_js/utils.js +261 -197
- package/build/map/next_js/utils.js.map +1 -1
- package/build/refactor/index.js +3 -3
- package/build/report/utility/dataTables/genDataTablesPage.js +112 -0
- package/build/report/utility/dataTables/genDataTablesPage.js.map +1 -0
- package/build/report/utility/genHtml.js +312 -7
- package/build/report/utility/genHtml.js.map +1 -1
- package/build/run/index.js +46 -26
- package/build/run/index.js.map +1 -1
- package/build/strings/index.js +2 -2
- package/build/strings/index.js.map +1 -1
- package/build/utility/makeReq.js +11 -0
- package/build/utility/makeReq.js.map +1 -1
- package/build/utility/openapiGenerator.js +15 -10
- package/build/utility/openapiGenerator.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import hljs from "highlight.js";
|
|
2
|
+
const escapeHtml = (value) => {
|
|
3
|
+
const str = value === null || value === undefined ? "" : String(value);
|
|
4
|
+
return str
|
|
5
|
+
.replace(/&/g, "&")
|
|
6
|
+
.replace(/</g, "<")
|
|
7
|
+
.replace(/>/g, ">")
|
|
8
|
+
.replace(/"/g, """)
|
|
9
|
+
.replace(/'/g, "'");
|
|
10
|
+
};
|
|
11
|
+
const booleanIcon = (b) => (b ? "✅" : "❌");
|
|
12
|
+
// Map severity to a sortable rank: info < low < medium < high
|
|
13
|
+
const severityRank = (sev) => {
|
|
14
|
+
const s = (sev || "").toString().toLowerCase().trim();
|
|
15
|
+
switch (s) {
|
|
16
|
+
case "info":
|
|
17
|
+
return 0;
|
|
18
|
+
case "low":
|
|
19
|
+
return 1;
|
|
20
|
+
case "medium":
|
|
21
|
+
return 2;
|
|
22
|
+
case "high":
|
|
23
|
+
return 3;
|
|
24
|
+
default:
|
|
25
|
+
return 99; // unknown values sort last
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
// Render JS code with highlight.js inside a pre/code block
|
|
29
|
+
const renderJsCode = (code) => {
|
|
30
|
+
const src = code !== null && code !== void 0 ? code : "";
|
|
31
|
+
try {
|
|
32
|
+
const highlighted = hljs.highlight(src, { language: "javascript", ignoreIllegals: true }).value;
|
|
33
|
+
return `<pre class="code-cell"><code class="hljs language-javascript">${highlighted}</code></pre>`;
|
|
34
|
+
}
|
|
35
|
+
catch (_a) {
|
|
36
|
+
// Fallback to escaped plain text
|
|
37
|
+
return `<pre class="code-cell">${escapeHtml(src)}</pre>`;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const genDataTablesPage = (db) => {
|
|
41
|
+
const findings = db.prepare(`SELECT * FROM analysis_findings`).all();
|
|
42
|
+
const mapped = db.prepare(`SELECT * FROM mapped`).all();
|
|
43
|
+
const findingsRows = findings
|
|
44
|
+
.map((f) => `
|
|
45
|
+
<tr>
|
|
46
|
+
<td>${escapeHtml(f.ruleId)}</td>
|
|
47
|
+
<td>${escapeHtml(f.ruleName)}</td>
|
|
48
|
+
<td>${escapeHtml(f.ruleType)}</td>
|
|
49
|
+
<td>${escapeHtml(f.ruleDescription)}</td>
|
|
50
|
+
<td>${escapeHtml(f.ruleAuthor)}</td>
|
|
51
|
+
<td>${escapeHtml(f.ruleTech)}</td>
|
|
52
|
+
<td data-order="${severityRank(f.severity)}">${escapeHtml(f.severity)}</td>
|
|
53
|
+
<td>${escapeHtml(f.message)}</td>
|
|
54
|
+
<td>${renderJsCode(f.findingLocation)}</td>
|
|
55
|
+
</tr>`)
|
|
56
|
+
.join("\n");
|
|
57
|
+
const mappedRows = mapped
|
|
58
|
+
.map((m) => `
|
|
59
|
+
<tr>
|
|
60
|
+
<td>${escapeHtml(m.id)}</td>
|
|
61
|
+
<td>${escapeHtml(m.description)}</td>
|
|
62
|
+
<td>${booleanIcon(m.containsFetch)}</td>
|
|
63
|
+
<td>${booleanIcon(m.isAxiosClient)}</td>
|
|
64
|
+
<td>${escapeHtml(m.exports)}</td>
|
|
65
|
+
<td>${escapeHtml(m.imports)}</td>
|
|
66
|
+
<td>${escapeHtml(m.file)}</td>
|
|
67
|
+
</tr>`)
|
|
68
|
+
.join("\n");
|
|
69
|
+
const html = `
|
|
70
|
+
<section>
|
|
71
|
+
<h2>Analyze Findings (Sortable)</h2>
|
|
72
|
+
<table id="findings-table" class="display data-table" style="width:100%">
|
|
73
|
+
<thead>
|
|
74
|
+
<tr>
|
|
75
|
+
<th>Rule ID</th>
|
|
76
|
+
<th>Name</th>
|
|
77
|
+
<th>Type</th>
|
|
78
|
+
<th>Description</th>
|
|
79
|
+
<th>Author</th>
|
|
80
|
+
<th>Tech</th>
|
|
81
|
+
<th>Severity</th>
|
|
82
|
+
<th>Message</th>
|
|
83
|
+
<th>Location</th>
|
|
84
|
+
</tr>
|
|
85
|
+
</thead>
|
|
86
|
+
<tbody>
|
|
87
|
+
${findingsRows}
|
|
88
|
+
</tbody>
|
|
89
|
+
</table>
|
|
90
|
+
|
|
91
|
+
<h2 style="margin-top: 2rem;">Mapped Data (Sortable)</h2>
|
|
92
|
+
<table id="mapped-table" class="display data-table" style="width:100%">
|
|
93
|
+
<thead>
|
|
94
|
+
<tr>
|
|
95
|
+
<th>ID</th>
|
|
96
|
+
<th>Description</th>
|
|
97
|
+
<th>Contains Fetch</th>
|
|
98
|
+
<th>Axios Client</th>
|
|
99
|
+
<th>Exports</th>
|
|
100
|
+
<th>Imports</th>
|
|
101
|
+
<th>File</th>
|
|
102
|
+
</tr>
|
|
103
|
+
</thead>
|
|
104
|
+
<tbody>
|
|
105
|
+
${mappedRows}
|
|
106
|
+
</tbody>
|
|
107
|
+
</table>
|
|
108
|
+
</section>`;
|
|
109
|
+
return html;
|
|
110
|
+
};
|
|
111
|
+
export default genDataTablesPage;
|
|
112
|
+
//# sourceMappingURL=genDataTablesPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"genDataTablesPage.js","sourceRoot":"","sources":["../../../../src/report/utility/dataTables/genDataTablesPage.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,13 +14,46 @@ import hljs from "highlight.js";
|
|
|
14
14
|
import addAnalyze from "./markdownGen/addAnalyze.js";
|
|
15
15
|
import CONFIG from "../../globalConfig.js";
|
|
16
16
|
import addMappedJson from "./markdownGen/addMappedJson.js";
|
|
17
|
-
|
|
17
|
+
import genDataTablesPage from "./dataTables/genDataTablesPage.js";
|
|
18
|
+
import { createRequire } from "module";
|
|
19
|
+
// Attempt to read local DataTables assets from node_modules (for offline/self-contained reports)
|
|
20
|
+
const getLocalDataTablesAssets = () => {
|
|
21
|
+
try {
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const dtJsPath = require.resolve("datatables.net/js/dataTables.min.js");
|
|
24
|
+
const dtCssPath = require.resolve("datatables.net-dt/css/dataTables.dataTables.min.css");
|
|
25
|
+
const js = fs.readFileSync(dtJsPath, "utf8");
|
|
26
|
+
const css = fs.readFileSync(dtCssPath, "utf8");
|
|
27
|
+
return { js, css };
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
console.warn("[DataTables] Local assets not found; falling back to CDN", (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
31
|
+
return { js: null, css: null };
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
// Attempt to read local jQuery asset
|
|
35
|
+
const getLocalJqueryAsset = () => {
|
|
36
|
+
try {
|
|
37
|
+
const require = createRequire(import.meta.url);
|
|
38
|
+
const jqPath = require.resolve("jquery/dist/jquery.min.js");
|
|
39
|
+
const js = fs.readFileSync(jqPath, "utf8");
|
|
40
|
+
return js;
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.warn("[DataTables] Local jQuery not found; falling back to CDN", (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const html = (analyzeMarkdown, mappedJsonMarkdown, dataTablesHtml, dtAssets, jqueryJs) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
48
|
return `<!DOCTYPE html>
|
|
19
49
|
<html>
|
|
20
50
|
<head>
|
|
21
51
|
<meta charset="UTF-8">
|
|
22
52
|
<title>JS Recon Report</title>
|
|
23
53
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/styles/github-dark.css">
|
|
54
|
+
${dtAssets.css
|
|
55
|
+
? `<style id="dt-inline-css">${dtAssets.css}</style>`
|
|
56
|
+
: `<link rel="stylesheet" href="https://cdn.datatables.net/2.0.8/css/dataTables.dataTables.min.css">`}
|
|
24
57
|
<style>
|
|
25
58
|
h2, h3, h4 {
|
|
26
59
|
cursor: pointer;
|
|
@@ -62,6 +95,32 @@ const html = (analyzeMarkdown, mappedJsonMarkdown) => __awaiter(void 0, void 0,
|
|
|
62
95
|
display: flex;
|
|
63
96
|
gap: 15px;
|
|
64
97
|
}
|
|
98
|
+
.data-table { font-size: 0.9rem; }
|
|
99
|
+
pre.code-cell { white-space: pre-wrap; word-break: break-word; max-height: 200px; overflow: auto; }
|
|
100
|
+
thead tr.filter-row th { padding: 4px 6px; }
|
|
101
|
+
thead tr.filter-row th input {
|
|
102
|
+
width: 100%;
|
|
103
|
+
padding: 4px 6px;
|
|
104
|
+
border: 1px solid #ccc;
|
|
105
|
+
border-radius: 4px;
|
|
106
|
+
font-size: 0.85rem;
|
|
107
|
+
box-sizing: border-box;
|
|
108
|
+
}
|
|
109
|
+
/* Column resize styles */
|
|
110
|
+
table.display.data-table { table-layout: fixed; }
|
|
111
|
+
table.display.data-table thead th { position: relative; }
|
|
112
|
+
.th-resizer {
|
|
113
|
+
position: absolute;
|
|
114
|
+
right: 0;
|
|
115
|
+
top: 0;
|
|
116
|
+
width: 6px;
|
|
117
|
+
height: 100%;
|
|
118
|
+
cursor: col-resize;
|
|
119
|
+
user-select: none;
|
|
120
|
+
opacity: 0;
|
|
121
|
+
transition: opacity 0.15s ease-in-out;
|
|
122
|
+
}
|
|
123
|
+
table.display.data-table thead th:hover .th-resizer { opacity: 1; }
|
|
65
124
|
</style>
|
|
66
125
|
</head>
|
|
67
126
|
<body>
|
|
@@ -72,6 +131,7 @@ const html = (analyzeMarkdown, mappedJsonMarkdown) => __awaiter(void 0, void 0,
|
|
|
72
131
|
<ul class="navbar-links" id="navbar-links">
|
|
73
132
|
<li><a href="#home">Home</a></li>
|
|
74
133
|
<li><a href="#mappedJson">Mapped JSON</a></li>
|
|
134
|
+
<li><a href="#dataTables">Data Tables</a></li>
|
|
75
135
|
<li><a href="#about">About</a></li>
|
|
76
136
|
</ul>
|
|
77
137
|
</nav>
|
|
@@ -80,16 +140,114 @@ const html = (analyzeMarkdown, mappedJsonMarkdown) => __awaiter(void 0, void 0,
|
|
|
80
140
|
${JSON.stringify({
|
|
81
141
|
home: yield marked.parse(analyzeMarkdown),
|
|
82
142
|
mappedJson: yield marked.parse(mappedJsonMarkdown),
|
|
143
|
+
dataTables: dataTablesHtml,
|
|
83
144
|
about: `# About\n\n The documentation for this tool is available at [JS Recon Docs](https://js-recon.io/).\n\n## Version\n\nThis report is generated with JS Recon [v${CONFIG.version}](https://github.com/shriyanss/js-recon/releases/tag/v${CONFIG.version}).`,
|
|
84
|
-
})}
|
|
145
|
+
}).replace(/</g, "\\u003c")}
|
|
85
146
|
</script>
|
|
86
147
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
148
|
+
${jqueryJs
|
|
149
|
+
? `<script id="jquery-inline-js">${jqueryJs}</script>`
|
|
150
|
+
: `<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>`}
|
|
151
|
+
${dtAssets.js
|
|
152
|
+
? `<script id="dt-inline-js">${dtAssets.js}</script>`
|
|
153
|
+
: `<script src="https://cdn.datatables.net/2.0.8/js/dataTables.min.js"></script>`}
|
|
87
154
|
<script>
|
|
88
155
|
document.addEventListener('DOMContentLoaded', () => {
|
|
89
156
|
const contentDiv = document.getElementById('content');
|
|
90
157
|
const navbarLinks = document.getElementById('navbar-links');
|
|
91
158
|
const pages = JSON.parse(document.getElementById('page-data').textContent);
|
|
92
159
|
|
|
160
|
+
const isDtAvailable = () => {
|
|
161
|
+
if (typeof window.DataTable !== 'undefined') return true; // ESM/global
|
|
162
|
+
const $ = window.jQuery || window.$;
|
|
163
|
+
return !!($ && $.fn && ($.fn.DataTable || $.fn.dataTable)); // UMD via jQuery plugin
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const loadScriptOnce = (src, id) => new Promise((resolve, reject) => {
|
|
167
|
+
if (document.getElementById(id)) return resolve(null);
|
|
168
|
+
const s = document.createElement('script');
|
|
169
|
+
s.src = src;
|
|
170
|
+
s.id = id;
|
|
171
|
+
s.onload = () => resolve(null);
|
|
172
|
+
s.onerror = (e) => reject(e);
|
|
173
|
+
document.head.appendChild(s);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const loadCssOnce = (href, id) => new Promise((resolve) => {
|
|
177
|
+
if (document.getElementById(id)) return resolve(null);
|
|
178
|
+
const l = document.createElement('link');
|
|
179
|
+
l.rel = 'stylesheet';
|
|
180
|
+
l.href = href;
|
|
181
|
+
l.id = id;
|
|
182
|
+
l.onload = () => resolve(null);
|
|
183
|
+
l.onerror = () => resolve(null);
|
|
184
|
+
document.head.appendChild(l);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const ensureJquery = async () => {
|
|
188
|
+
if (window.jQuery) return true;
|
|
189
|
+
const jqCdn = [
|
|
190
|
+
'https://code.jquery.com/jquery-3.7.1.min.js',
|
|
191
|
+
'https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js',
|
|
192
|
+
'https://unpkg.com/jquery@3.7.1/dist/jquery.min.js'
|
|
193
|
+
];
|
|
194
|
+
for (let i = 0; i < jqCdn.length; i++) {
|
|
195
|
+
const url = jqCdn[i];
|
|
196
|
+
try {
|
|
197
|
+
await loadScriptOnce(url, 'jq-cdn-' + i);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
console.error('[jQuery] Load failed', url, e);
|
|
200
|
+
}
|
|
201
|
+
if (window.jQuery) {
|
|
202
|
+
console.log('[jQuery] Loaded from', url);
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const tryLoadDataTables = async () => {
|
|
210
|
+
// Ensure jQuery is present for DataTables UMD build
|
|
211
|
+
if (!isDtAvailable() && !window.jQuery) {
|
|
212
|
+
const jqOk = await ensureJquery();
|
|
213
|
+
if (!jqOk) {
|
|
214
|
+
console.error('[DataTables] jQuery not available; cannot initialize DataTables.');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const sources = [
|
|
218
|
+
{
|
|
219
|
+
js: 'https://cdn.datatables.net/2.0.8/js/dataTables.min.js',
|
|
220
|
+
css: 'https://cdn.datatables.net/2.0.8/css/dataTables.dataTables.min.css',
|
|
221
|
+
id: 'dt-cdn'
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
js: 'https://cdn.jsdelivr.net/npm/datatables.net@2.0.8/js/dataTables.min.js',
|
|
225
|
+
css: 'https://cdn.jsdelivr.net/npm/datatables.net-dt@2.0.8/css/dataTables.dataTables.min.css',
|
|
226
|
+
id: 'dt-jsd'
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
js: 'https://unpkg.com/datatables.net@2.0.8/js/dataTables.min.js',
|
|
230
|
+
css: 'https://unpkg.com/datatables.net-dt@2.0.8/css/dataTables.dataTables.min.css',
|
|
231
|
+
id: 'dt-unp'
|
|
232
|
+
}
|
|
233
|
+
];
|
|
234
|
+
for (const src of sources) {
|
|
235
|
+
if (isDtAvailable()) return true;
|
|
236
|
+
console.warn('[DataTables] Attempting load from', src.js);
|
|
237
|
+
await loadCssOnce(src.css, src.id + '-css');
|
|
238
|
+
try {
|
|
239
|
+
await loadScriptOnce(src.js, src.id + '-js');
|
|
240
|
+
} catch (e) {
|
|
241
|
+
console.error('[DataTables] Load failed', src.js, e);
|
|
242
|
+
}
|
|
243
|
+
if (isDtAvailable()) {
|
|
244
|
+
console.log('[DataTables] Loaded from', src.js);
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
};
|
|
250
|
+
|
|
93
251
|
const updateVisibility = () => {
|
|
94
252
|
const headers = contentDiv.querySelectorAll('h2, h3, h4');
|
|
95
253
|
let parentCollapsedLevels = [];
|
|
@@ -116,6 +274,143 @@ const html = (analyzeMarkdown, mappedJsonMarkdown) => __awaiter(void 0, void 0,
|
|
|
116
274
|
});
|
|
117
275
|
};
|
|
118
276
|
|
|
277
|
+
const setColWidth = (tableEl, colIndex, widthPx) => {
|
|
278
|
+
const nth = colIndex + 1;
|
|
279
|
+
const w = Math.max(50, widthPx) + 'px';
|
|
280
|
+
const th = tableEl.querySelector('thead tr:first-child th:nth-child(' + nth + ')');
|
|
281
|
+
if (th) th.style.width = w;
|
|
282
|
+
const filterTh = tableEl.querySelector('thead tr.filter-row th:nth-child(' + nth + ')');
|
|
283
|
+
if (filterTh) filterTh.style.width = w;
|
|
284
|
+
const tds = tableEl.querySelectorAll('tbody tr td:nth-child(' + nth + ')');
|
|
285
|
+
tds.forEach(td => { td.style.width = w; });
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const addColumnResizers = (tableEl) => {
|
|
289
|
+
if (!tableEl || tableEl.dataset.resizers === '1') return;
|
|
290
|
+
const ths = tableEl.querySelectorAll('thead tr:first-child th');
|
|
291
|
+
ths.forEach((th, idx) => {
|
|
292
|
+
if (th.querySelector('.th-resizer')) return;
|
|
293
|
+
const handle = document.createElement('div');
|
|
294
|
+
handle.className = 'th-resizer';
|
|
295
|
+
th.appendChild(handle);
|
|
296
|
+
let startX = 0;
|
|
297
|
+
let startWidth = 0;
|
|
298
|
+
const onMouseMove = (e) => {
|
|
299
|
+
const dx = e.pageX - startX;
|
|
300
|
+
setColWidth(tableEl, idx, startWidth + dx);
|
|
301
|
+
};
|
|
302
|
+
const onMouseUp = () => {
|
|
303
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
304
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
305
|
+
document.body.style.cursor = '';
|
|
306
|
+
};
|
|
307
|
+
handle.addEventListener('mousedown', (e) => {
|
|
308
|
+
e.preventDefault();
|
|
309
|
+
startX = e.pageX;
|
|
310
|
+
startWidth = th.offsetWidth;
|
|
311
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
312
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
313
|
+
document.body.style.cursor = 'col-resize';
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
tableEl.dataset.resizers = '1';
|
|
317
|
+
// Ensure table layout
|
|
318
|
+
tableEl.style.tableLayout = 'fixed';
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const initializeDataTablesIfPresent = async () => {
|
|
322
|
+
try {
|
|
323
|
+
// Ensure DataTables (ESM or UMD) is present
|
|
324
|
+
if (!isDtAvailable()) {
|
|
325
|
+
const ok = await tryLoadDataTables();
|
|
326
|
+
if (!ok) {
|
|
327
|
+
console.error('[DataTables] Unable to load DataTables from any CDN. Controls disabled.');
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const tables = contentDiv.querySelectorAll('table.data-table');
|
|
332
|
+
console.log('[DataTables] Initializing', tables.length, 'table(s)');
|
|
333
|
+
tables.forEach((table) => {
|
|
334
|
+
if (table.dataset.dtInit === '1') return;
|
|
335
|
+
const options = {
|
|
336
|
+
paging: true,
|
|
337
|
+
searching: true,
|
|
338
|
+
ordering: true,
|
|
339
|
+
orderMulti: true,
|
|
340
|
+
pageLength: 25,
|
|
341
|
+
autoWidth: false,
|
|
342
|
+
// v1 fallback
|
|
343
|
+
dom: 'lfrtip',
|
|
344
|
+
// v2 layout API
|
|
345
|
+
layout: {
|
|
346
|
+
topStart: 'search',
|
|
347
|
+
topEnd: 'pageLength',
|
|
348
|
+
bottomStart: 'info',
|
|
349
|
+
bottomEnd: 'paging'
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// Per-table column options
|
|
354
|
+
if (table.id === 'findings-table') {
|
|
355
|
+
options.columnDefs = [{ targets: 8, orderable: false, searchable: false }];
|
|
356
|
+
} else if (table.id === 'mapped-table') {
|
|
357
|
+
options.columnDefs = [{ targets: [2, 3], searchable: false }];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let dt;
|
|
361
|
+
try {
|
|
362
|
+
if (typeof window.DataTable !== 'undefined') {
|
|
363
|
+
dt = new window.DataTable(table, options); // ESM/global style
|
|
364
|
+
} else {
|
|
365
|
+
const $ = window.jQuery || window.$;
|
|
366
|
+
if ($ && $.fn && $.fn.DataTable) {
|
|
367
|
+
dt = $(table).DataTable(options); // UMD via jQuery plugin
|
|
368
|
+
} else {
|
|
369
|
+
throw new Error('DataTables not available.');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} catch (e) {
|
|
373
|
+
console.error('[DataTables] Error instantiating on table#' + table.id, e);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Add per-column filters (second header row)
|
|
378
|
+
const thead = table.querySelector('thead');
|
|
379
|
+
if (thead && !thead.querySelector('tr.filter-row')) {
|
|
380
|
+
const headerCells = thead.querySelectorAll('tr:first-child th');
|
|
381
|
+
const filterRow = document.createElement('tr');
|
|
382
|
+
filterRow.className = 'filter-row';
|
|
383
|
+
headerCells.forEach((th, idx) => {
|
|
384
|
+
const thFilter = document.createElement('th');
|
|
385
|
+
// Skip filter for non-searchable columns if specified
|
|
386
|
+
const isSearchable = !options.columnDefs || !options.columnDefs.some(cd => {
|
|
387
|
+
if (Array.isArray(cd.targets)) return cd.targets.includes(idx) && cd.searchable === false;
|
|
388
|
+
return cd.targets === idx && cd.searchable === false;
|
|
389
|
+
});
|
|
390
|
+
if (isSearchable) {
|
|
391
|
+
const input = document.createElement('input');
|
|
392
|
+
input.type = 'text';
|
|
393
|
+
input.placeholder = 'Filter ' + (th.textContent || '').trim();
|
|
394
|
+
input.addEventListener('input', () => {
|
|
395
|
+
dt.column(idx).search(input.value).draw();
|
|
396
|
+
});
|
|
397
|
+
thFilter.appendChild(input);
|
|
398
|
+
}
|
|
399
|
+
filterRow.appendChild(thFilter);
|
|
400
|
+
});
|
|
401
|
+
thead.appendChild(filterRow);
|
|
402
|
+
}
|
|
403
|
+
table.dataset.dtInit = '1';
|
|
404
|
+
// Attach column resizers after DataTables and filters are in place
|
|
405
|
+
addColumnResizers(table);
|
|
406
|
+
// Let DataTables recalc
|
|
407
|
+
try { if (dt && dt.columns) dt.columns().adjust(); } catch (e) {}
|
|
408
|
+
});
|
|
409
|
+
} catch (e) {
|
|
410
|
+
console.error('DataTables init error', e);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
119
414
|
const initializeCollapsibleHeaders = () => {
|
|
120
415
|
const headers = contentDiv.querySelectorAll('h2, h3, h4');
|
|
121
416
|
headers.forEach((header) => {
|
|
@@ -131,9 +426,14 @@ const html = (analyzeMarkdown, mappedJsonMarkdown) => __awaiter(void 0, void 0,
|
|
|
131
426
|
};
|
|
132
427
|
|
|
133
428
|
const renderPage = (pageName) => {
|
|
134
|
-
const
|
|
135
|
-
|
|
429
|
+
const content = pages[pageName] || '<h2>Page Not Found: ' + pageName + '</h2>';
|
|
430
|
+
if (pageName === 'home' || pageName === 'dataTables' || pageName === 'mappedJson') {
|
|
431
|
+
contentDiv.innerHTML = content;
|
|
432
|
+
} else {
|
|
433
|
+
contentDiv.innerHTML = window.marked.parse(content);
|
|
434
|
+
}
|
|
136
435
|
initializeCollapsibleHeaders();
|
|
436
|
+
initializeDataTablesIfPresent();
|
|
137
437
|
};
|
|
138
438
|
|
|
139
439
|
const handleHashChange = () => {
|
|
@@ -142,9 +442,11 @@ const html = (analyzeMarkdown, mappedJsonMarkdown) => __awaiter(void 0, void 0,
|
|
|
142
442
|
};
|
|
143
443
|
|
|
144
444
|
navbarLinks.addEventListener('click', (event) => {
|
|
145
|
-
|
|
445
|
+
const t = event.target;
|
|
446
|
+
const a = t && (t.closest ? t.closest('a') : null);
|
|
447
|
+
if (a && a.hash) {
|
|
146
448
|
event.preventDefault();
|
|
147
|
-
const pageName =
|
|
449
|
+
const pageName = a.hash.substring(1);
|
|
148
450
|
window.location.hash = pageName;
|
|
149
451
|
}
|
|
150
452
|
});
|
|
@@ -164,6 +466,7 @@ const genHtml = (outputReportFile, db) => __awaiter(void 0, void 0, void 0, func
|
|
|
164
466
|
let mappedJsonMarkdown = analyzeMarkdown;
|
|
165
467
|
analyzeMarkdown = yield addAnalyze(analyzeMarkdown, db);
|
|
166
468
|
mappedJsonMarkdown = yield addMappedJson(mappedJsonMarkdown, db);
|
|
469
|
+
const dataTablesHtml = genDataTablesPage(db);
|
|
167
470
|
const renderer = new marked.Renderer();
|
|
168
471
|
renderer.code = ({ text, lang }) => {
|
|
169
472
|
const language = hljs.getLanguage(lang) ? lang : "plaintext";
|
|
@@ -176,7 +479,9 @@ const genHtml = (outputReportFile, db) => __awaiter(void 0, void 0, void 0, func
|
|
|
176
479
|
pedantic: false,
|
|
177
480
|
gfm: true,
|
|
178
481
|
});
|
|
179
|
-
const
|
|
482
|
+
const dtAssets = getLocalDataTablesAssets();
|
|
483
|
+
const jqueryJs = getLocalJqueryAsset();
|
|
484
|
+
const renderedHtml = yield html(analyzeMarkdown, mappedJsonMarkdown, dataTablesHtml, dtAssets, jqueryJs);
|
|
180
485
|
fs.writeFileSync(outputReportFile, renderedHtml);
|
|
181
486
|
console.log(chalk.green("[✓] HTML report generated successfully"));
|
|
182
487
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"genHtml.js","sourceRoot":"","sources":["../../../src/report/utility/genHtml.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,IAAI,MAAM,cAAc,CAAC;AAEhC,OAAO,UAAU,MAAM,6BAA6B,CAAC;AACrD,OAAO,MAAM,MAAM,uBAAuB,CAAC;AAC3C,OAAO,aAAa,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"genHtml.js","sourceRoot":"","sources":["../../../src/report/utility/genHtml.ts"],"names":[],"mappings":"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"}
|
package/build/run/index.js
CHANGED
|
@@ -17,7 +17,23 @@ import chalk from "chalk";
|
|
|
17
17
|
import CONFIG from "../globalConfig.js";
|
|
18
18
|
import analyze from "../analyze/index.js";
|
|
19
19
|
import report from "../report/index.js";
|
|
20
|
-
import { clearJsUrls, clearJsonUrls } from "../lazyLoad/globals.js";
|
|
20
|
+
import { clearJsUrls, clearJsonUrls, getJsUrls } from "../lazyLoad/globals.js";
|
|
21
|
+
import path from "path";
|
|
22
|
+
const getCdnDir = (host, outputDir) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
+
// get the JS URLs
|
|
24
|
+
let cdnDir;
|
|
25
|
+
for (const url of getJsUrls()) {
|
|
26
|
+
if (url.includes("_next/static/chunks")) {
|
|
27
|
+
// check if the host and url.host match
|
|
28
|
+
const urlHost = new URL(url).host.replace(":", "_");
|
|
29
|
+
if (urlHost !== host) {
|
|
30
|
+
cdnDir = path.join(outputDir, urlHost);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return cdnDir;
|
|
36
|
+
});
|
|
21
37
|
const processUrl = (url, outputDir, workingDir, cmd, isBatch) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
38
|
const targetHost = new URL(url).host.replace(":", "_");
|
|
23
39
|
console.log(chalk.bgGreenBright(`[+] Starting analysis for ${url}...`));
|
|
@@ -30,7 +46,7 @@ const processUrl = (url, outputDir, workingDir, cmd, isBatch) => __awaiter(void
|
|
|
30
46
|
console.log(chalk.bgGreen("[+] Lazyload complete."));
|
|
31
47
|
if (globalsUtil.getTech() === "") {
|
|
32
48
|
console.log(chalk.bgRed("[!] Technology not detected. Quitting."));
|
|
33
|
-
|
|
49
|
+
process.exit(10);
|
|
34
50
|
}
|
|
35
51
|
if (globalsUtil.getTech() !== "next") {
|
|
36
52
|
console.log(chalk.bgYellow(`[!] The tool only supports Next.JS ('next') fully. For ${globalsUtil.getTech()}, only downloading JS files is supported`));
|
|
@@ -45,6 +61,11 @@ const processUrl = (url, outputDir, workingDir, cmd, isBatch) => __awaiter(void
|
|
|
45
61
|
const analyzeFile = isBatch ? `${workingDir}/analyze.json` : "analyze.json";
|
|
46
62
|
const reportDbFile = isBatch ? `${workingDir}/js-recon.db` : "js-recon.db";
|
|
47
63
|
const reportFile = isBatch ? `${workingDir}/report` : "report";
|
|
64
|
+
// if the target is using a CDN, then just passing the outputDir/host won't work, and would throw an error.
|
|
65
|
+
// So, if the target was found to be using a CDN, scan the CDN directory rather than the outputDir/host
|
|
66
|
+
// One IMPORTANT thing: this is only meant for modules that rely on just the code (map)
|
|
67
|
+
const cdnDir = yield getCdnDir(url, outputDir);
|
|
68
|
+
const cdnOutputDir = cdnDir ? cdnDir : outputDir + "/" + targetHost;
|
|
48
69
|
console.log(chalk.bgCyan("[2/8] Running strings to extract endpoints..."));
|
|
49
70
|
yield strings(outputDir, stringsFile, true, extractedUrlsFile, false, false, false);
|
|
50
71
|
console.log(chalk.bgGreen("[+] Strings complete."));
|
|
@@ -59,7 +80,7 @@ const processUrl = (url, outputDir, workingDir, cmd, isBatch) => __awaiter(void
|
|
|
59
80
|
if (isBatch) {
|
|
60
81
|
globalsUtil.setOpenapiOutputFile(openapiFile);
|
|
61
82
|
}
|
|
62
|
-
yield map(
|
|
83
|
+
yield map(cdnOutputDir, mappedFile, ["json"], globalsUtil.getTech(), false, false);
|
|
63
84
|
console.log(chalk.bgGreen("[+] Map complete."));
|
|
64
85
|
console.log(chalk.bgCyan("[6/8] Running endpoints to extract endpoints..."));
|
|
65
86
|
if (fs.existsSync(`${outputDir}/${targetHost}/___subsequent_requests`)) {
|
|
@@ -92,12 +113,14 @@ export default (cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
92
113
|
if (fs.existsSync(cmd.output)) {
|
|
93
114
|
console.log(chalk.red(`[!] Output directory ${cmd.output} already exists. Please switch to other directory or it might conflict with this process.`));
|
|
94
115
|
console.log(chalk.yellow(`[i] For advanced users: use the individual modules separately. See docs at ${CONFIG.modulesDocs}`));
|
|
95
|
-
|
|
116
|
+
process.exit(11);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
new URL(cmd.url);
|
|
96
120
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return;
|
|
121
|
+
catch (e) {
|
|
122
|
+
console.log(chalk.red(`[!] Invalid URL: ${cmd.url}`));
|
|
123
|
+
process.exit(12);
|
|
101
124
|
}
|
|
102
125
|
yield processUrl(cmd.url, cmd.output, ".", cmd, false);
|
|
103
126
|
}
|
|
@@ -107,32 +130,29 @@ export default (cmd) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
107
130
|
.readFileSync(cmd.url, "utf-8")
|
|
108
131
|
.split("\n")
|
|
109
132
|
.filter((url) => url !== "");
|
|
110
|
-
// iterate through the URLs, and make sure they are valid URLs
|
|
111
|
-
let allPassed = true;
|
|
112
|
-
for (const url of urls) {
|
|
113
|
-
try {
|
|
114
|
-
let urlTest = new URL(url);
|
|
115
|
-
}
|
|
116
|
-
catch (e) {
|
|
117
|
-
console.log(chalk.red(`[!] Invalid URL: ${url}`));
|
|
118
|
-
allPassed = false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (!allPassed) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
133
|
// first of all, make a new directory for the tool output
|
|
125
134
|
const toolOutputDir = "js_recon_run_output";
|
|
126
135
|
if (fs.existsSync(toolOutputDir)) {
|
|
127
136
|
console.log(chalk.red(`[!] Output directory ${toolOutputDir} already exists. Please switch to other directory or it might conflict with this process.`));
|
|
128
137
|
console.log(chalk.yellow(`[i] For advanced users: use the individual modules separately. See docs at ${CONFIG.modulesDocs}`));
|
|
129
|
-
|
|
138
|
+
process.exit(14);
|
|
130
139
|
}
|
|
131
140
|
fs.mkdirSync(toolOutputDir);
|
|
132
141
|
for (const url of urls) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
// Validate URL only
|
|
143
|
+
let urlObj;
|
|
144
|
+
try {
|
|
145
|
+
urlObj = new URL(url);
|
|
146
|
+
}
|
|
147
|
+
catch (_a) {
|
|
148
|
+
console.log(chalk.bgRed(`[!] Invalid URL: ${url}`));
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const thisTargetWorkingDir = `${toolOutputDir}/${urlObj.host.replace(":", "_")}`;
|
|
152
|
+
if (!fs.existsSync(thisTargetWorkingDir)) {
|
|
153
|
+
fs.mkdirSync(thisTargetWorkingDir, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
const outputDir = `${thisTargetWorkingDir}/output`;
|
|
136
156
|
yield processUrl(url, outputDir, thisTargetWorkingDir, cmd, true);
|
|
137
157
|
}
|
|
138
158
|
}
|