@govtechsg/oobee 0.10.84 → 0.10.86
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/image.yml +3 -2
- package/.github/workflows/publish.yml +10 -0
- package/DETAILS.md +29 -0
- package/dist/cli.js +7 -6
- package/dist/combine.js +1 -1
- package/dist/constants/common.js +15 -4
- package/dist/constants/constants.js +604 -1
- package/dist/crawlers/commonCrawlerFunc.js +3 -2
- package/dist/crawlers/crawlSitemap.js +98 -80
- package/dist/crawlers/custom/utils.js +218 -71
- package/dist/crawlers/guards/urlGuard.js +8 -15
- package/dist/crawlers/runCustom.js +24 -15
- package/dist/generateOobeeClientScanner.js +570 -0
- package/dist/mergeAxeResults.js +49 -29
- package/dist/npmIndex.js +10 -2
- package/dist/proxyService.js +18 -3
- package/dist/services/s3Uploader.js +21 -10
- package/dist/static/ejs/partials/scripts/header/aboutScanModal/ScanConfiguration.ejs +2 -2
- package/dist/static/ejs/partials/scripts/ruleModal/constants.ejs +1 -761
- package/dist/static/ejs/summary.ejs +10 -5
- package/oobee-client-scanner.js +34992 -0
- package/package.json +3 -3
- package/src/cli.ts +20 -15
- package/src/combine.ts +3 -1
- package/src/constants/common.ts +22 -10
- package/src/constants/constants.ts +602 -1
- package/src/crawlers/commonCrawlerFunc.ts +4 -3
- package/src/crawlers/crawlSitemap.ts +116 -98
- package/src/crawlers/custom/utils.ts +244 -84
- package/src/crawlers/guards/urlGuard.ts +24 -31
- package/src/crawlers/runCustom.ts +38 -15
- package/src/generateOobeeClientScanner.ts +591 -0
- package/src/mergeAxeResults.ts +48 -29
- package/src/npmIndex.ts +12 -2
- package/src/proxyService.ts +25 -4
- package/src/services/s3Uploader.ts +23 -11
- package/src/static/ejs/partials/scripts/header/aboutScanModal/ScanConfiguration.ejs +2 -2
- package/src/static/ejs/partials/scripts/ruleModal/constants.ejs +1 -761
- package/src/static/ejs/summary.ejs +10 -5
- package/testStaticJSScanner.html +534 -0
|
@@ -1,12 +1,44 @@
|
|
|
1
|
-
/* eslint-disable no-shadow */
|
|
2
1
|
/* eslint-disable no-alert */
|
|
3
2
|
/* eslint-disable no-param-reassign */
|
|
4
3
|
/* eslint-env browser */
|
|
5
4
|
import path from 'path';
|
|
5
|
+
import { getDomain } from 'tldts';
|
|
6
6
|
import { runAxeScript } from '../commonCrawlerFunc.js';
|
|
7
7
|
import { consoleLogger, guiInfoLog } from '../../logs.js';
|
|
8
8
|
import { guiInfoStatusTypes } from '../../constants/constants.js';
|
|
9
9
|
import { isSkippedUrl, validateCustomFlowLabel } from '../../constants/common.js';
|
|
10
|
+
const sameRegistrableDomain = (hostA, hostB) => {
|
|
11
|
+
const domainA = getDomain(hostA);
|
|
12
|
+
const domainB = getDomain(hostB);
|
|
13
|
+
if (!domainA || !domainB)
|
|
14
|
+
return hostA === hostB;
|
|
15
|
+
return domainA === domainB;
|
|
16
|
+
};
|
|
17
|
+
const parseBoolEnv = (val, defaultVal) => {
|
|
18
|
+
if (val == null)
|
|
19
|
+
return defaultVal;
|
|
20
|
+
const v = String(val).trim().toLowerCase();
|
|
21
|
+
if (['1', 'true', 'yes', 'y', 'on'].includes(v))
|
|
22
|
+
return true;
|
|
23
|
+
if (['0', 'false', 'no', 'n', 'off'].includes(v))
|
|
24
|
+
return false;
|
|
25
|
+
return defaultVal;
|
|
26
|
+
};
|
|
27
|
+
const RESTRICT_OVERLAY_TO_ENTRY_DOMAIN = parseBoolEnv(process.env.RESTRICT_OVERLAY_TO_ENTRY_DOMAIN, false);
|
|
28
|
+
const isOverlayAllowed = (currentUrl, entryUrl) => {
|
|
29
|
+
try {
|
|
30
|
+
const cur = new URL(currentUrl);
|
|
31
|
+
if (cur.protocol !== 'http:' && cur.protocol !== 'https:')
|
|
32
|
+
return false;
|
|
33
|
+
if (!RESTRICT_OVERLAY_TO_ENTRY_DOMAIN)
|
|
34
|
+
return true;
|
|
35
|
+
const base = new URL(entryUrl);
|
|
36
|
+
return sameRegistrableDomain(cur.hostname, base.hostname);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
10
42
|
//! For Cypress Test
|
|
11
43
|
// env to check if Cypress test is running
|
|
12
44
|
const isCypressTest = process.env.IS_CYPRESS_TEST === 'true';
|
|
@@ -30,16 +62,19 @@ export const screenshotFullPage = async (page, screenshotsDir, screenshotIdx) =>
|
|
|
30
62
|
await page.evaluate(() => {
|
|
31
63
|
window.scrollTo(0, document.body.scrollHeight);
|
|
32
64
|
});
|
|
33
|
-
const isLoadMoreContent = async () =>
|
|
34
|
-
|
|
65
|
+
const isLoadMoreContent = async () => {
|
|
66
|
+
await new Promise(resolve => setTimeout(resolve, 2500));
|
|
67
|
+
if (page.isClosed())
|
|
68
|
+
return false;
|
|
69
|
+
try {
|
|
35
70
|
await page.waitForLoadState('domcontentloaded');
|
|
36
|
-
const newHeight = await page.evaluate(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
}
|
|
71
|
+
const newHeight = await page.evaluate(() => document.body.scrollHeight);
|
|
72
|
+
return newHeight > prevHeight;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
43
78
|
const result = await isLoadMoreContent();
|
|
44
79
|
return result;
|
|
45
80
|
};
|
|
@@ -157,7 +192,7 @@ export const MENU_POSITION = {
|
|
|
157
192
|
export const updateMenu = async (page, urlsCrawled) => {
|
|
158
193
|
log(`Overlay menu: updating: ${page.url()}`);
|
|
159
194
|
await page.evaluate(vars => {
|
|
160
|
-
const shadowHost = document.querySelector('#
|
|
195
|
+
const shadowHost = document.querySelector('#oobeeShadowHost');
|
|
161
196
|
if (shadowHost) {
|
|
162
197
|
const p = shadowHost.shadowRoot.querySelector('#oobee-p-pages-scanned');
|
|
163
198
|
if (p) {
|
|
@@ -200,7 +235,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
200
235
|
</svg>
|
|
201
236
|
`;
|
|
202
237
|
minBtn.innerHTML = MINBTN_SVG;
|
|
203
|
-
let currentPos =
|
|
238
|
+
let currentPos = vars.menuPos || 'RIGHT';
|
|
204
239
|
const isCollapsed = () => panel.classList.contains('collapsed');
|
|
205
240
|
const setPosClass = (pos) => {
|
|
206
241
|
panel.classList.remove('pos-left', 'pos-right');
|
|
@@ -217,7 +252,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
217
252
|
setDraggableSidebarMenu();
|
|
218
253
|
};
|
|
219
254
|
const toggleCollapsed = (force) => {
|
|
220
|
-
const willCollapse =
|
|
255
|
+
const willCollapse = typeof force === 'boolean' ? force : !isCollapsed();
|
|
221
256
|
if (willCollapse) {
|
|
222
257
|
panel.classList.add('collapsed');
|
|
223
258
|
localStorage.setItem('oobee:overlay-collapsed', '1');
|
|
@@ -261,22 +296,60 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
261
296
|
const h2 = document.createElement('h2');
|
|
262
297
|
h2.id = 'oobeeHPagesScanned';
|
|
263
298
|
h2.className = 'oobee-section-title';
|
|
264
|
-
h2.textContent =
|
|
299
|
+
h2.textContent = `Pages Scanned (${vars.urlsCrawled.scanned.length || 0})`;
|
|
300
|
+
const scanIcon = document.createElement('span');
|
|
301
|
+
scanIcon.className = 'oobee-btn-icon';
|
|
302
|
+
const SCAN_SVG = `
|
|
303
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
|
304
|
+
<g clip-path="url(#clip0_1421_431)">
|
|
305
|
+
<path d="M12.5763 11.5472L12.2958 11.2857L12.1037 11.1005C12.776 10.3183 12.9194 9.56432 12.9194 8.45969C12.9194 5.99657 10.9228 4 8.45969 4C5.99657 4 4 5.99657 4 8.45969C4 10.9228 5.99657 12.9194 8.45969 12.9194C9.56432 12.9194 10.3183 12.776 11.1005 12.1037L11.2857 12.2958L11.5472 12.5763L14.9777 16L16 14.9777L12.5763 11.5472ZM8.45969 11.5472C6.75129 11.5472 5.37221 10.1681 5.37221 8.45969C5.37221 6.75129 6.75129 5.37221 8.45969 5.37221C10.1681 5.37221 11.5472 6.75129 11.5472 8.45969C11.5472 10.1681 10.1681 11.5472 8.45969 11.5472Z" fill="white"/>
|
|
306
|
+
<path d="M18.5 0H19.5C19.7761 0 20 0.223858 20 0.5V5H18.5V0Z" fill="white"/>
|
|
307
|
+
<path d="M19.5 2.18552e-08L19.5 1.5L15 1.5L15 -2.18556e-07L19.5 2.18552e-08Z" fill="white"/>
|
|
308
|
+
<path d="M1.5 0H0.5C0.223858 0 0 0.223858 0 0.5V5H1.5V0Z" fill="white"/>
|
|
309
|
+
<path d="M0.5 2.18552e-08L0.5 1.5L5 1.5L5 -2.18556e-07L0.5 2.18552e-08Z" fill="white"/>
|
|
310
|
+
<path d="M1.5 20H0.5C0.223858 20 0 19.7761 0 19.5V15H1.5V20Z" fill="white"/>
|
|
311
|
+
<path d="M0.5 20L0.5 18.5L5 18.5L5 20L0.5 20Z" fill="white"/>
|
|
312
|
+
<path d="M18.5 20H19.5C19.7761 20 20 19.7761 20 19.5V15H18.5V20Z" fill="white"/>
|
|
313
|
+
<path d="M19.5 20L19.5 18.5L15 18.5L15 20L19.5 20Z" fill="white"/>
|
|
314
|
+
</g>
|
|
315
|
+
<defs>
|
|
316
|
+
<clipPath id="clip0_1421_431">
|
|
317
|
+
<rect width="20" height="20" fill="white"/>
|
|
318
|
+
</clipPath>
|
|
319
|
+
</defs>
|
|
320
|
+
</svg>
|
|
321
|
+
`;
|
|
322
|
+
scanIcon.innerHTML = SCAN_SVG;
|
|
265
323
|
const scanBtn = document.createElement('button');
|
|
266
324
|
scanBtn.id = 'oobeeBtnScan';
|
|
267
325
|
scanBtn.className = 'oobee-btn oobee-btn-primary';
|
|
268
|
-
scanBtn.innerText = 'Scan this page';
|
|
269
326
|
scanBtn.disabled = inProgress;
|
|
327
|
+
scanBtn.appendChild(scanIcon);
|
|
328
|
+
const scanText = document.createElement('span');
|
|
329
|
+
scanText.className = 'oobee-btn-text';
|
|
330
|
+
scanText.innerText = 'Scan page';
|
|
331
|
+
scanBtn.appendChild(scanText);
|
|
270
332
|
scanBtn.addEventListener('click', async () => customWindow.handleOnScanClick?.());
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
333
|
+
const endScanIcon = document.createElement('span');
|
|
334
|
+
endScanIcon.className = 'oobee-btn-icon';
|
|
335
|
+
const ENDSCAN_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
|
336
|
+
<path d="M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM13.59 5L10 8.59L6.41 5L5 6.41L8.59 10L5 13.59L6.41 15L10 11.41L13.59 15L15 13.59L11.41 10L15 6.41L13.59 5Z" fill="#9021A6"/>
|
|
337
|
+
</svg>
|
|
338
|
+
`;
|
|
339
|
+
endScanIcon.innerHTML = ENDSCAN_SVG;
|
|
340
|
+
const endScanBtn = document.createElement('button');
|
|
341
|
+
endScanBtn.id = 'oobeeBtnEndScan';
|
|
342
|
+
endScanBtn.className = 'oobee-btn oobee-btn-secondary';
|
|
343
|
+
endScanBtn.appendChild(endScanIcon);
|
|
344
|
+
const endScanText = document.createElement('span');
|
|
345
|
+
endScanText.className = 'oobee-btn-text';
|
|
346
|
+
endScanText.innerText = 'End scan';
|
|
347
|
+
endScanBtn.appendChild(endScanText);
|
|
348
|
+
endScanBtn.addEventListener('click', async () => customWindow.handleOnStopClick?.());
|
|
276
349
|
const btnGroup = document.createElement('div');
|
|
277
350
|
btnGroup.className = 'oobee-actions';
|
|
278
351
|
btnGroup.appendChild(scanBtn);
|
|
279
|
-
btnGroup.appendChild(
|
|
352
|
+
btnGroup.appendChild(endScanBtn);
|
|
280
353
|
const listWrap = document.createElement('div');
|
|
281
354
|
listWrap.id = 'oobeeList';
|
|
282
355
|
listWrap.className = 'oobee-list';
|
|
@@ -292,12 +365,12 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
292
365
|
}
|
|
293
366
|
const ol = document.createElement('ol');
|
|
294
367
|
ol.className = 'oobee-ol';
|
|
295
|
-
scanned.forEach(
|
|
368
|
+
scanned.forEach(item => {
|
|
296
369
|
const li = document.createElement('li');
|
|
297
370
|
li.className = 'oobee-li';
|
|
298
371
|
const title = document.createElement('div');
|
|
299
372
|
title.className = 'oobee-item-title';
|
|
300
|
-
title.textContent =
|
|
373
|
+
title.textContent = item.pageTitle && item.pageTitle.trim() ? item.pageTitle : item.url;
|
|
301
374
|
const url = document.createElement('div');
|
|
302
375
|
url.className = 'oobee-item-url';
|
|
303
376
|
url.textContent = item.url;
|
|
@@ -340,7 +413,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
340
413
|
border-right: 1px solid rgba(0,0,0,.08)
|
|
341
414
|
}
|
|
342
415
|
.oobee-panel.collapsed {
|
|
343
|
-
width:
|
|
416
|
+
width: 58px;
|
|
344
417
|
overflow: hidden
|
|
345
418
|
}
|
|
346
419
|
|
|
@@ -417,6 +490,12 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
417
490
|
padding: 1rem;
|
|
418
491
|
}
|
|
419
492
|
|
|
493
|
+
.oobee-panel.collapsed .oobee-actions {
|
|
494
|
+
display: flex;
|
|
495
|
+
justify-content: center;
|
|
496
|
+
padding: 1rem 0.7rem;
|
|
497
|
+
}
|
|
498
|
+
|
|
420
499
|
/* Base button */
|
|
421
500
|
.oobee-btn {
|
|
422
501
|
width: 100%;
|
|
@@ -427,6 +506,10 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
427
506
|
line-height: 1.2;
|
|
428
507
|
font-weight: 400;
|
|
429
508
|
cursor: pointer;
|
|
509
|
+
display: flex;
|
|
510
|
+
align-items: center;
|
|
511
|
+
justify-content: center;
|
|
512
|
+
gap: 10px;
|
|
430
513
|
transition: {
|
|
431
514
|
box-shadow .12s ease,
|
|
432
515
|
transform .02s ease,
|
|
@@ -440,6 +523,19 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
440
523
|
cursor:not-allowed
|
|
441
524
|
}
|
|
442
525
|
|
|
526
|
+
.oobee-panel.collapsed .oobee-btn {
|
|
527
|
+
width: 44px !important;
|
|
528
|
+
height: 44px !important;
|
|
529
|
+
min-width: 44px !important;
|
|
530
|
+
min-height: 44px !important;
|
|
531
|
+
max-width: 44px !important;
|
|
532
|
+
max-height: 44px !important;
|
|
533
|
+
border-radius: 50% !important;
|
|
534
|
+
padding: 0 !important;
|
|
535
|
+
justify-content: center;
|
|
536
|
+
gap: 0;
|
|
537
|
+
}
|
|
538
|
+
|
|
443
539
|
/* Primary (filled) */
|
|
444
540
|
.oobee-btn-primary {
|
|
445
541
|
background: #9021a6;
|
|
@@ -495,6 +591,25 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
495
591
|
display: none;
|
|
496
592
|
}
|
|
497
593
|
|
|
594
|
+
.oobee-btn-icon {
|
|
595
|
+
display: inline-flex;
|
|
596
|
+
align-items: center;
|
|
597
|
+
justify-content: center;
|
|
598
|
+
width: 20px;
|
|
599
|
+
height: 20px;
|
|
600
|
+
vertical-align: middle;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.oobee-btn-text {
|
|
604
|
+
display: inline;
|
|
605
|
+
white-space: nowrap;
|
|
606
|
+
vertical-align: middle;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.oobee-panel.collapsed .oobee-btn-text {
|
|
610
|
+
display: none;
|
|
611
|
+
}
|
|
612
|
+
|
|
498
613
|
#oobeeStopOverlay[hidden] {
|
|
499
614
|
display:none !important;
|
|
500
615
|
}
|
|
@@ -512,7 +627,10 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
512
627
|
}
|
|
513
628
|
|
|
514
629
|
.oobee-panel.collapsed .oobee-section-title {
|
|
515
|
-
|
|
630
|
+
font-size: 14px;
|
|
631
|
+
display: flex;
|
|
632
|
+
justify-content: center;
|
|
633
|
+
text-align: center;
|
|
516
634
|
}
|
|
517
635
|
|
|
518
636
|
.oobee-ol {
|
|
@@ -596,8 +714,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
596
714
|
if (!icon)
|
|
597
715
|
return;
|
|
598
716
|
const closed = isCollapsed();
|
|
599
|
-
const arrowPointsRight = (currentPos === 'RIGHT' && !closed) ||
|
|
600
|
-
(currentPos === 'LEFT' && closed);
|
|
717
|
+
const arrowPointsRight = (currentPos === 'RIGHT' && !closed) || (currentPos === 'LEFT' && closed);
|
|
601
718
|
icon.classList.toggle('is-left', !arrowPointsRight);
|
|
602
719
|
minBtn.setAttribute('aria-label', closed ? 'Expand panel' : 'Collapse panel');
|
|
603
720
|
}
|
|
@@ -652,7 +769,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
652
769
|
borderRadius: '16px',
|
|
653
770
|
overflow: 'hidden',
|
|
654
771
|
boxShadow: '0 10px 40px rgba(0,0,0,.35)',
|
|
655
|
-
fontFamily: 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif'
|
|
772
|
+
fontFamily: 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif',
|
|
656
773
|
});
|
|
657
774
|
const dialogSheet = new CSSStyleSheet();
|
|
658
775
|
dialogSheet.replaceSync(`
|
|
@@ -689,12 +806,17 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
689
806
|
display: 'flex',
|
|
690
807
|
alignItems: 'center',
|
|
691
808
|
justifyContent: 'space-between',
|
|
692
|
-
gap: '8px'
|
|
809
|
+
gap: '8px',
|
|
693
810
|
});
|
|
694
811
|
const title = document.createElement('h2');
|
|
695
812
|
title.id = 'oobee-stop-title';
|
|
696
813
|
title.textContent = 'Are you sure you want to stop this scan?';
|
|
697
|
-
Object.assign(title.style, {
|
|
814
|
+
Object.assign(title.style, {
|
|
815
|
+
margin: '0',
|
|
816
|
+
fontSize: '22px',
|
|
817
|
+
fontWeight: '700',
|
|
818
|
+
lineHeight: '1.25',
|
|
819
|
+
});
|
|
698
820
|
const closeX = document.createElement('button');
|
|
699
821
|
closeX.type = 'button';
|
|
700
822
|
closeX.setAttribute('aria-label', 'Close');
|
|
@@ -711,13 +833,13 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
711
833
|
height: '36px',
|
|
712
834
|
borderRadius: '12px',
|
|
713
835
|
display: 'grid',
|
|
714
|
-
placeItems: 'center'
|
|
836
|
+
placeItems: 'center',
|
|
715
837
|
});
|
|
716
838
|
head.appendChild(title);
|
|
717
839
|
head.appendChild(closeX);
|
|
718
840
|
const bodyWrap = document.createElement('div');
|
|
719
841
|
Object.assign(bodyWrap.style, {
|
|
720
|
-
padding: '12px 20px 20px 20px'
|
|
842
|
+
padding: '12px 20px 20px 20px',
|
|
721
843
|
});
|
|
722
844
|
const form = document.createElement('form');
|
|
723
845
|
form.noValidate = true;
|
|
@@ -725,7 +847,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
725
847
|
Object.assign(form.style, {
|
|
726
848
|
display: 'grid',
|
|
727
849
|
gridTemplateColumns: '1fr',
|
|
728
|
-
rowGap: '12px'
|
|
850
|
+
rowGap: '12px',
|
|
729
851
|
});
|
|
730
852
|
const label = document.createElement('label');
|
|
731
853
|
label.setAttribute('for', 'oobee-stop-input');
|
|
@@ -741,7 +863,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
741
863
|
padding: '12px 14px',
|
|
742
864
|
fontSize: '14px',
|
|
743
865
|
outline: 'none',
|
|
744
|
-
boxSizing: 'border-box'
|
|
866
|
+
boxSizing: 'border-box',
|
|
745
867
|
});
|
|
746
868
|
input.addEventListener('focus', () => {
|
|
747
869
|
input.style.borderColor = '#7b4dff';
|
|
@@ -765,7 +887,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
765
887
|
fontWeight: '600',
|
|
766
888
|
color: '#fff',
|
|
767
889
|
background: '#9021A6',
|
|
768
|
-
cursor: 'pointer'
|
|
890
|
+
cursor: 'pointer',
|
|
769
891
|
});
|
|
770
892
|
const cancel = document.createElement('button');
|
|
771
893
|
cancel.type = 'button';
|
|
@@ -777,7 +899,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
777
899
|
fontSize: '14px',
|
|
778
900
|
justifySelf: 'center',
|
|
779
901
|
cursor: 'pointer',
|
|
780
|
-
padding: '6px'
|
|
902
|
+
padding: '6px',
|
|
781
903
|
});
|
|
782
904
|
actions.appendChild(primary);
|
|
783
905
|
actions.appendChild(cancel);
|
|
@@ -792,10 +914,13 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
792
914
|
stopDialog.appendChild(bodyWrap);
|
|
793
915
|
shadowRoot.appendChild(stopDialog);
|
|
794
916
|
let stopResolver = null;
|
|
795
|
-
const hideStop = () => {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
917
|
+
const hideStop = () => {
|
|
918
|
+
try {
|
|
919
|
+
stopDialog.close();
|
|
920
|
+
}
|
|
921
|
+
catch { }
|
|
922
|
+
stopResolver = null;
|
|
923
|
+
};
|
|
799
924
|
const showStop = () => {
|
|
800
925
|
if (!shouldHideInput)
|
|
801
926
|
input.value = '';
|
|
@@ -813,7 +938,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
813
938
|
});
|
|
814
939
|
}
|
|
815
940
|
};
|
|
816
|
-
form.addEventListener('submit',
|
|
941
|
+
form.addEventListener('submit', e => {
|
|
817
942
|
e.preventDefault();
|
|
818
943
|
const v = (input.value || '').trim();
|
|
819
944
|
if (stopResolver)
|
|
@@ -830,13 +955,13 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
830
955
|
stopResolver({ confirmed: false, label: '' });
|
|
831
956
|
hideStop();
|
|
832
957
|
});
|
|
833
|
-
stopDialog.addEventListener('cancel',
|
|
958
|
+
stopDialog.addEventListener('cancel', e => {
|
|
834
959
|
e.preventDefault();
|
|
835
960
|
if (stopResolver)
|
|
836
961
|
stopResolver({ confirmed: false, label: '' });
|
|
837
962
|
hideStop();
|
|
838
963
|
});
|
|
839
|
-
customWindow.oobeeShowStopModal = () => new Promise(
|
|
964
|
+
customWindow.oobeeShowStopModal = () => new Promise(resolve => {
|
|
840
965
|
stopResolver = resolve;
|
|
841
966
|
showStop();
|
|
842
967
|
});
|
|
@@ -861,7 +986,7 @@ export const addOverlayMenu = async (page, urlsCrawled, menuPos, opts = {
|
|
|
861
986
|
log('Overlay menu: successfully added');
|
|
862
987
|
})
|
|
863
988
|
.catch(error => {
|
|
864
|
-
error('Overlay menu: failed to add', error);
|
|
989
|
+
consoleLogger.error('Overlay menu: failed to add', error);
|
|
865
990
|
});
|
|
866
991
|
};
|
|
867
992
|
export const removeOverlayMenu = async (page) => {
|
|
@@ -884,7 +1009,14 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
|
|
|
884
1009
|
let menuPos = MENU_POSITION.right;
|
|
885
1010
|
// eslint-disable-next-line no-underscore-dangle
|
|
886
1011
|
const pageId = page._guid;
|
|
887
|
-
page.on('dialog', () => {
|
|
1012
|
+
page.on('dialog', async (dialog) => {
|
|
1013
|
+
try {
|
|
1014
|
+
await dialog.dismiss();
|
|
1015
|
+
}
|
|
1016
|
+
catch {
|
|
1017
|
+
// dialog may already be closed
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
888
1020
|
const pageClosePromise = new Promise(resolve => {
|
|
889
1021
|
page.on('close', () => {
|
|
890
1022
|
log(`Page: close detected: ${page.url()}`);
|
|
@@ -910,11 +1042,19 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
|
|
|
910
1042
|
await processPage(page, processPageParams);
|
|
911
1043
|
log('Scan: success');
|
|
912
1044
|
pagesDict[pageId].isScanning = false;
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1045
|
+
if (page.isClosed())
|
|
1046
|
+
return;
|
|
1047
|
+
const allowed = isOverlayAllowed(page.url(), processPageParams.entryUrl);
|
|
1048
|
+
if (allowed) {
|
|
1049
|
+
await addOverlayMenu(page, processPageParams.urlsCrawled, menuPos, {
|
|
1050
|
+
inProgress: false,
|
|
1051
|
+
collapsed: !!pagesDict[pageId]?.collapsed,
|
|
1052
|
+
hideStopInput: !!processPageParams.customFlowLabel,
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
else {
|
|
1056
|
+
await removeOverlayMenu(page);
|
|
1057
|
+
}
|
|
918
1058
|
}
|
|
919
1059
|
catch (error) {
|
|
920
1060
|
log(`Scan failed ${error}`);
|
|
@@ -944,10 +1084,10 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
|
|
|
944
1084
|
});
|
|
945
1085
|
if (!inputValue?.confirmed) {
|
|
946
1086
|
await page.evaluate(() => {
|
|
947
|
-
const
|
|
948
|
-
if (
|
|
949
|
-
|
|
950
|
-
|
|
1087
|
+
const endScanBtn = document.getElementById('oobeeBtnEndScan');
|
|
1088
|
+
if (endScanBtn) {
|
|
1089
|
+
endScanBtn.disabled = false;
|
|
1090
|
+
endScanBtn.textContent = 'Stop';
|
|
951
1091
|
}
|
|
952
1092
|
});
|
|
953
1093
|
return;
|
|
@@ -976,7 +1116,14 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
|
|
|
976
1116
|
}
|
|
977
1117
|
};
|
|
978
1118
|
page.on('domcontentloaded', async () => {
|
|
1119
|
+
if (page.isClosed())
|
|
1120
|
+
return;
|
|
979
1121
|
try {
|
|
1122
|
+
const allowed = isOverlayAllowed(page.url(), processPageParams.entryUrl);
|
|
1123
|
+
if (!allowed) {
|
|
1124
|
+
await removeOverlayMenu(page);
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
980
1127
|
const existingOverlay = await page.evaluate(() => {
|
|
981
1128
|
return document.querySelector('#oobeeShadowHost');
|
|
982
1129
|
});
|
|
@@ -989,11 +1136,6 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
|
|
|
989
1136
|
hideStopInput: !!processPageParams.customFlowLabel,
|
|
990
1137
|
});
|
|
991
1138
|
}
|
|
992
|
-
setTimeout(() => {
|
|
993
|
-
// Timeout here to slow things down a little
|
|
994
|
-
}, 1000);
|
|
995
|
-
//! For Cypress Test
|
|
996
|
-
// Auto-clicks 'Scan this page' button only once
|
|
997
1139
|
if (isCypressTest) {
|
|
998
1140
|
try {
|
|
999
1141
|
await handleOnScanClick();
|
|
@@ -1003,22 +1145,27 @@ export const initNewPage = async (page, pageClosePromises, processPageParams, pa
|
|
|
1003
1145
|
consoleLogger.info(`Error in calling handleOnScanClick, isCypressTest: ${isCypressTest}`);
|
|
1004
1146
|
}
|
|
1005
1147
|
}
|
|
1006
|
-
consoleLogger.info(`Overlay state: ${existingOverlay}`);
|
|
1007
1148
|
}
|
|
1008
1149
|
catch {
|
|
1009
1150
|
consoleLogger.info('Error in adding overlay menu to page');
|
|
1010
|
-
consoleLogger.info('Error in adding overlay menu to page');
|
|
1011
1151
|
}
|
|
1012
1152
|
});
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1153
|
+
try {
|
|
1154
|
+
if (page.isClosed())
|
|
1155
|
+
return page;
|
|
1156
|
+
await page.exposeFunction('handleOnScanClick', handleOnScanClick);
|
|
1157
|
+
await page.exposeFunction('handleOnStopClick', handleOnStopClick);
|
|
1158
|
+
// Define the updateMenuPos function
|
|
1159
|
+
const updateMenuPos = newPos => {
|
|
1160
|
+
const prevPos = menuPos;
|
|
1161
|
+
if (prevPos !== newPos) {
|
|
1162
|
+
menuPos = newPos;
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
await page.exposeFunction('updateMenuPos', updateMenuPos);
|
|
1166
|
+
}
|
|
1167
|
+
catch (e) {
|
|
1168
|
+
log(`Error exposing functions on page: ${e}`);
|
|
1169
|
+
}
|
|
1023
1170
|
return page;
|
|
1024
1171
|
};
|
|
@@ -2,15 +2,16 @@ const ALLOWED_PROTOCOLS = new Set(['http:', 'https:']);
|
|
|
2
2
|
export function addUrlGuardScript(context, opts = {}) {
|
|
3
3
|
const { fallbackUrl } = opts;
|
|
4
4
|
const lastAllowedUrlByPage = new WeakMap();
|
|
5
|
-
const attachGuardsToPage =
|
|
5
|
+
const attachGuardsToPage = page => {
|
|
6
6
|
if (!lastAllowedUrlByPage.has(page) && fallbackUrl) {
|
|
7
7
|
lastAllowedUrlByPage.set(page, String(fallbackUrl));
|
|
8
8
|
}
|
|
9
|
-
page
|
|
10
|
-
|
|
9
|
+
page
|
|
10
|
+
.addInitScript(() => {
|
|
11
|
+
const isAllowedProtocol = value => {
|
|
11
12
|
try {
|
|
12
13
|
const s = value instanceof URL ? value.toString() : String(value);
|
|
13
|
-
const protocol = new URL(s, window.location.href)
|
|
14
|
+
const { protocol } = new URL(s, window.location.href);
|
|
14
15
|
return protocol === 'http:' || protocol === 'https:';
|
|
15
16
|
}
|
|
16
17
|
catch {
|
|
@@ -24,17 +25,9 @@ export function addUrlGuardScript(context, opts = {}) {
|
|
|
24
25
|
return null;
|
|
25
26
|
return openOriginal.call(this, targetUrl, ...args);
|
|
26
27
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
assignOriginal(nextUrl); };
|
|
31
|
-
win.location.replace = (nextUrl) => { if (isAllowedProtocol(nextUrl))
|
|
32
|
-
replaceOriginal(nextUrl); };
|
|
33
|
-
Object.defineProperty(win.location, 'href', {
|
|
34
|
-
get() { return String(win.location.toString()); },
|
|
35
|
-
set(nextUrl) { if (isAllowedProtocol(nextUrl))
|
|
36
|
-
assignOriginal(nextUrl); },
|
|
37
|
-
});
|
|
28
|
+
})
|
|
29
|
+
.catch(() => {
|
|
30
|
+
// page may have closed before addInitScript completed; safe to ignore
|
|
38
31
|
});
|
|
39
32
|
const restoreToSafeUrl = async (page, attemptedUrl) => {
|
|
40
33
|
try {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/* eslint-env browser */
|
|
2
|
-
import { chromium } from 'playwright';
|
|
3
2
|
import { createCrawleeSubFolders } from './commonCrawlerFunc.js';
|
|
4
3
|
import { cleanUpAndExit, register, registerSoftClose } from '../utils.js';
|
|
5
4
|
import constants, { getIntermediateScreenshotsPath, guiInfoStatusTypes, } from '../constants/constants.js';
|
|
6
5
|
import { initNewPage, log } from './custom/utils.js';
|
|
7
6
|
import { guiInfoLog } from '../logs.js';
|
|
8
7
|
import { addUrlGuardScript } from './guards/urlGuard.js';
|
|
9
|
-
import { getPlaywrightLaunchOptions } from '../constants/common.js';
|
|
8
|
+
import { getBrowserToRun, getPlaywrightLaunchOptions, initModifiedUserAgent, } from '../constants/common.js';
|
|
10
9
|
// Export of classes
|
|
11
10
|
export class ProcessPageParams {
|
|
12
11
|
constructor(scannedIdx, blacklistedPatterns, includeScreenshots, dataset, intermediateScreenshotsPath, urlsCrawled, randomToken) {
|
|
@@ -19,7 +18,7 @@ export class ProcessPageParams {
|
|
|
19
18
|
this.randomToken = randomToken;
|
|
20
19
|
}
|
|
21
20
|
}
|
|
22
|
-
const runCustom = async (url, randomToken, viewportSettings, blacklistedPatterns, includeScreenshots, initialCustomFlowLabel) => {
|
|
21
|
+
const runCustom = async (url, randomToken, browserToRun, userDataDirectory, viewportSettings, blacklistedPatterns, includeScreenshots, initialCustomFlowLabel) => {
|
|
23
22
|
// checks and delete datasets path if it already exists
|
|
24
23
|
process.env.CRAWLEE_STORAGE_DIR = randomToken;
|
|
25
24
|
const urlsCrawled = { ...constants.urlsCrawledObj };
|
|
@@ -27,48 +26,58 @@ const runCustom = async (url, randomToken, viewportSettings, blacklistedPatterns
|
|
|
27
26
|
const intermediateScreenshotsPath = getIntermediateScreenshotsPath(randomToken);
|
|
28
27
|
const processPageParams = new ProcessPageParams(0, // scannedIdx
|
|
29
28
|
blacklistedPatterns, includeScreenshots, dataset, intermediateScreenshotsPath, urlsCrawled, randomToken);
|
|
29
|
+
processPageParams.entryUrl = url;
|
|
30
30
|
if (initialCustomFlowLabel && initialCustomFlowLabel.trim()) {
|
|
31
31
|
processPageParams.customFlowLabel = initialCustomFlowLabel.trim();
|
|
32
32
|
}
|
|
33
33
|
const pagesDict = {};
|
|
34
34
|
const pageClosePromises = [];
|
|
35
35
|
try {
|
|
36
|
+
const { browserToRun: resolvedBrowserToRun } = getBrowserToRun(randomToken, browserToRun, false);
|
|
36
37
|
const deviceConfig = viewportSettings.playwrightDeviceDetailsObject;
|
|
37
38
|
const hasCustomViewport = !!deviceConfig;
|
|
38
|
-
const
|
|
39
|
+
const rawDevice = (deviceConfig || {});
|
|
40
|
+
const { userAgent: deviceUserAgent, ...contextDeviceOptions } = rawDevice;
|
|
41
|
+
await initModifiedUserAgent(resolvedBrowserToRun, viewportSettings.playwrightDeviceDetailsObject);
|
|
42
|
+
const baseLaunchOptions = getPlaywrightLaunchOptions(resolvedBrowserToRun);
|
|
39
43
|
// Merge base args with custom flow specific args
|
|
40
44
|
const baseArgs = baseLaunchOptions.args || [];
|
|
41
45
|
const customArgs = hasCustomViewport ? ['--window-size=1920,1040'] : ['--start-maximized'];
|
|
42
|
-
const mergedArgs = [
|
|
43
|
-
|
|
46
|
+
const mergedArgs = [
|
|
47
|
+
...baseArgs.filter(a => !a.startsWith('--window-size') && a !== '--start-maximized'),
|
|
48
|
+
...customArgs,
|
|
49
|
+
];
|
|
50
|
+
const context = await constants.launcher.launchPersistentContext(userDataDirectory, {
|
|
44
51
|
...baseLaunchOptions,
|
|
45
52
|
args: mergedArgs,
|
|
46
53
|
headless: false,
|
|
47
|
-
channel: 'chrome',
|
|
48
|
-
});
|
|
49
|
-
const context = await browser.newContext({
|
|
50
54
|
ignoreHTTPSErrors: true,
|
|
51
55
|
serviceWorkers: 'block',
|
|
52
56
|
viewport: null,
|
|
53
|
-
...(hasCustomViewport ?
|
|
57
|
+
...(hasCustomViewport ? contextDeviceOptions : {}),
|
|
58
|
+
userAgent: process.env.OOBEE_USER_AGENT || deviceUserAgent,
|
|
54
59
|
});
|
|
55
60
|
register(context);
|
|
56
61
|
processPageParams.stopAll = async () => {
|
|
57
62
|
try {
|
|
58
63
|
await context.close().catch(() => { });
|
|
59
|
-
await browser.close().catch(() => { });
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
64
|
}
|
|
65
|
+
catch { }
|
|
63
66
|
};
|
|
64
67
|
// For handling closing playwright browser and continue generate artifacts etc
|
|
65
68
|
registerSoftClose(processPageParams.stopAll);
|
|
66
69
|
addUrlGuardScript(context, { fallbackUrl: url });
|
|
70
|
+
const page = context.pages().find(existingPage => !existingPage.isClosed()) || (await context.newPage());
|
|
71
|
+
await initNewPage(page, pageClosePromises, processPageParams, pagesDict);
|
|
67
72
|
// Detection of new page
|
|
68
73
|
context.on('page', async (newPage) => {
|
|
69
|
-
|
|
74
|
+
try {
|
|
75
|
+
await initNewPage(newPage, pageClosePromises, processPageParams, pagesDict);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
log(`Error initializing new page: ${e}`);
|
|
79
|
+
}
|
|
70
80
|
});
|
|
71
|
-
const page = await context.newPage();
|
|
72
81
|
await page.goto(url, { timeout: 0 });
|
|
73
82
|
// to execute and wait for all pages to close
|
|
74
83
|
// idea is for promise to be pending until page.on('close') detected
|