@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.
Files changed (39) hide show
  1. package/.github/workflows/build-and-prettify.yaml +22 -3
  2. package/.github/workflows/pr_checker.yml +22 -0
  3. package/.github/workflows/publish-js-recon.yml +1 -2
  4. package/CHANGELOG.md +31 -0
  5. package/Dockerfile +1 -0
  6. package/build/analyze/index.js +1 -1
  7. package/build/analyze/index.js.map +1 -1
  8. package/build/api_gateway/genReq.js +1 -1
  9. package/build/api_gateway/genReq.js.map +1 -1
  10. package/build/globalConfig.js +1 -1
  11. package/build/globalConfig.js.map +1 -1
  12. package/build/index.js +3 -3
  13. package/build/index.js.map +1 -1
  14. package/build/lazyLoad/index.js +1 -1
  15. package/build/lazyLoad/index.js.map +1 -1
  16. package/build/lazyLoad/next_js/next_SubsequentRequests.js +1 -1
  17. package/build/lazyLoad/next_js/next_SubsequentRequests.js.map +1 -1
  18. package/build/lazyLoad/techDetect/index.js +12 -5
  19. package/build/lazyLoad/techDetect/index.js.map +1 -1
  20. package/build/map/index.js +3 -3
  21. package/build/map/index.js.map +1 -1
  22. package/build/map/next_js/getWebpackConnections.js +1 -1
  23. package/build/map/next_js/getWebpackConnections.js.map +1 -1
  24. package/build/map/next_js/utils.js +261 -197
  25. package/build/map/next_js/utils.js.map +1 -1
  26. package/build/refactor/index.js +3 -3
  27. package/build/report/utility/dataTables/genDataTablesPage.js +112 -0
  28. package/build/report/utility/dataTables/genDataTablesPage.js.map +1 -0
  29. package/build/report/utility/genHtml.js +312 -7
  30. package/build/report/utility/genHtml.js.map +1 -1
  31. package/build/run/index.js +46 -26
  32. package/build/run/index.js.map +1 -1
  33. package/build/strings/index.js +2 -2
  34. package/build/strings/index.js.map +1 -1
  35. package/build/utility/makeReq.js +11 -0
  36. package/build/utility/makeReq.js.map +1 -1
  37. package/build/utility/openapiGenerator.js +15 -10
  38. package/build/utility/openapiGenerator.js.map +1 -1
  39. 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, "&lt;")
7
+ .replace(/>/g, "&gt;")
8
+ .replace(/"/g, "&quot;")
9
+ .replace(/'/g, "&#39;");
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
- const html = (analyzeMarkdown, mappedJsonMarkdown) => __awaiter(void 0, void 0, void 0, function* () {
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 markdownContent = pages[pageName] || '<h2>Page Not Found: ' + pageName + '</h2>';
135
- contentDiv.innerHTML = pageName === 'home' ? markdownContent : window.marked.parse(markdownContent);
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
- if (event.target.tagName === 'A') {
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 = event.target.hash.substring(1);
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 renderedHtml = yield html(analyzeMarkdown, mappedJsonMarkdown);
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;AAQ3D,MAAM,IAAI,GAAG,CAAO,eAAuB,EAAE,kBAA0B,EAAE,EAAE;IACvE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA8DL,IAAI,CAAC,SAAS,CAAC;QACb,IAAI,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC;QACzC,UAAU,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC;QAClD,KAAK,EAAE,gKAAgK,MAAM,CAAC,OAAO,yDAAyD,MAAM,CAAC,OAAO,IAAI;KACnQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA2EE,CAAC;AACT,CAAC,CAAA,CAAC;AAEF,MAAM,OAAO,GAAG,CAAO,gBAAwB,EAAE,EAAqB,EAAE,EAAE;IACtE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC,CAAC;IAEzD,IAAI,eAAe,GAAG,kCAAkC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC;IACvF,IAAI,kBAAkB,GAAG,eAAe,CAAC;IAEzC,eAAe,GAAG,MAAM,UAAU,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IACxD,kBAAkB,GAAG,MAAM,aAAa,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;IACvC,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAc,CAAC,CAAC,CAAC,CAAE,IAAe,CAAC,CAAC,CAAC,WAAW,CAAC;QACnF,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC;QACvF,OAAO,0BAA0B,QAAQ,KAAK,eAAe,eAAe,CAAC;IACjF,CAAC,CAAC;IAEF,MAAM,CAAC,UAAU,CAAC;QACd,QAAQ;QACR,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,KAAK;QACf,GAAG,EAAE,IAAI;KACZ,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;IACrE,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;IAEjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC,CAAC;AACvE,CAAC,CAAA,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"genHtml.js","sourceRoot":"","sources":["../../../src/report/utility/genHtml.ts"],"names":[],"mappings":"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"}
@@ -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
- return;
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(outputDir + "/" + targetHost, mappedFile, ["json"], globalsUtil.getTech(), false, false);
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
- return;
116
+ process.exit(11);
117
+ }
118
+ try {
119
+ new URL(cmd.url);
96
120
  }
97
- let urlTest = new URL(cmd.url);
98
- if (!urlTest) {
99
- console.log(chalk.red("[!] Invalid URL"));
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
- return;
138
+ process.exit(14);
130
139
  }
131
140
  fs.mkdirSync(toolOutputDir);
132
141
  for (const url of urls) {
133
- const thisTargetWorkingDir = toolOutputDir + "/" + new URL(url).host.replace(":", "_");
134
- fs.mkdirSync(thisTargetWorkingDir);
135
- const outputDir = thisTargetWorkingDir + "/output";
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
  }