@govtechsg/oobee 0.10.20

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 (123) hide show
  1. package/.dockerignore +22 -0
  2. package/.github/pull_request_template.md +11 -0
  3. package/.github/workflows/docker-test.yml +54 -0
  4. package/.github/workflows/image.yml +107 -0
  5. package/.github/workflows/publish.yml +18 -0
  6. package/.idea/modules.xml +8 -0
  7. package/.idea/purple-a11y.iml +9 -0
  8. package/.idea/vcs.xml +6 -0
  9. package/.prettierrc.json +12 -0
  10. package/.vscode/extensions.json +5 -0
  11. package/.vscode/settings.json +10 -0
  12. package/CODE_OF_CONDUCT.md +128 -0
  13. package/DETAILS.md +163 -0
  14. package/Dockerfile +60 -0
  15. package/INSTALLATION.md +146 -0
  16. package/INTEGRATION.md +785 -0
  17. package/LICENSE +22 -0
  18. package/README.md +587 -0
  19. package/SECURITY.md +5 -0
  20. package/__mocks__/mock-report.html +1431 -0
  21. package/__mocks__/mockFunctions.ts +32 -0
  22. package/__mocks__/mockIssues.ts +64 -0
  23. package/__mocks__/mock_all_issues/000000001.json +64 -0
  24. package/__mocks__/mock_all_issues/000000002.json +53 -0
  25. package/__mocks__/mock_all_issues/fake-file.txt +0 -0
  26. package/__tests__/logs.test.ts +25 -0
  27. package/__tests__/mergeAxeResults.test.ts +278 -0
  28. package/__tests__/utils.test.ts +118 -0
  29. package/a11y-scan-results.zip +0 -0
  30. package/eslint.config.js +53 -0
  31. package/exclusions.txt +2 -0
  32. package/gitlab-pipeline-template.yml +54 -0
  33. package/jest.config.js +1 -0
  34. package/package.json +96 -0
  35. package/scripts/copyFiles.js +44 -0
  36. package/scripts/install_oobee_dependencies.cmd +13 -0
  37. package/scripts/install_oobee_dependencies.command +101 -0
  38. package/scripts/install_oobee_dependencies.ps1 +110 -0
  39. package/scripts/oobee_shell.cmd +13 -0
  40. package/scripts/oobee_shell.command +11 -0
  41. package/scripts/oobee_shell.sh +55 -0
  42. package/scripts/oobee_shell_ps.ps1 +54 -0
  43. package/src/cli.ts +401 -0
  44. package/src/combine.ts +240 -0
  45. package/src/constants/__tests__/common.test.ts +44 -0
  46. package/src/constants/cliFunctions.ts +305 -0
  47. package/src/constants/common.ts +1840 -0
  48. package/src/constants/constants.ts +443 -0
  49. package/src/constants/errorMeta.json +319 -0
  50. package/src/constants/itemTypeDescription.ts +11 -0
  51. package/src/constants/oobeeAi.ts +141 -0
  52. package/src/constants/questions.ts +181 -0
  53. package/src/constants/sampleData.ts +187 -0
  54. package/src/crawlers/__tests__/commonCrawlerFunc.test.ts +51 -0
  55. package/src/crawlers/commonCrawlerFunc.ts +656 -0
  56. package/src/crawlers/crawlDomain.ts +877 -0
  57. package/src/crawlers/crawlIntelligentSitemap.ts +156 -0
  58. package/src/crawlers/crawlLocalFile.ts +193 -0
  59. package/src/crawlers/crawlSitemap.ts +356 -0
  60. package/src/crawlers/custom/extractAndGradeText.ts +57 -0
  61. package/src/crawlers/custom/flagUnlabelledClickableElements.ts +964 -0
  62. package/src/crawlers/custom/utils.ts +486 -0
  63. package/src/crawlers/customAxeFunctions.ts +82 -0
  64. package/src/crawlers/pdfScanFunc.ts +468 -0
  65. package/src/crawlers/runCustom.ts +117 -0
  66. package/src/index.ts +173 -0
  67. package/src/logs.ts +66 -0
  68. package/src/mergeAxeResults.ts +964 -0
  69. package/src/npmIndex.ts +284 -0
  70. package/src/screenshotFunc/htmlScreenshotFunc.ts +411 -0
  71. package/src/screenshotFunc/pdfScreenshotFunc.ts +762 -0
  72. package/src/static/ejs/partials/components/categorySelector.ejs +4 -0
  73. package/src/static/ejs/partials/components/categorySelectorDropdown.ejs +57 -0
  74. package/src/static/ejs/partials/components/pagesScannedModal.ejs +70 -0
  75. package/src/static/ejs/partials/components/reportSearch.ejs +47 -0
  76. package/src/static/ejs/partials/components/ruleOffcanvas.ejs +105 -0
  77. package/src/static/ejs/partials/components/scanAbout.ejs +263 -0
  78. package/src/static/ejs/partials/components/screenshotLightbox.ejs +13 -0
  79. package/src/static/ejs/partials/components/summaryScanAbout.ejs +141 -0
  80. package/src/static/ejs/partials/components/summaryScanResults.ejs +16 -0
  81. package/src/static/ejs/partials/components/summaryTable.ejs +20 -0
  82. package/src/static/ejs/partials/components/summaryWcagCompliance.ejs +94 -0
  83. package/src/static/ejs/partials/components/topFive.ejs +6 -0
  84. package/src/static/ejs/partials/components/wcagCompliance.ejs +70 -0
  85. package/src/static/ejs/partials/footer.ejs +21 -0
  86. package/src/static/ejs/partials/header.ejs +230 -0
  87. package/src/static/ejs/partials/main.ejs +40 -0
  88. package/src/static/ejs/partials/scripts/bootstrap.ejs +8 -0
  89. package/src/static/ejs/partials/scripts/categorySelectorDropdownScript.ejs +190 -0
  90. package/src/static/ejs/partials/scripts/categorySummary.ejs +141 -0
  91. package/src/static/ejs/partials/scripts/highlightjs.ejs +335 -0
  92. package/src/static/ejs/partials/scripts/popper.ejs +7 -0
  93. package/src/static/ejs/partials/scripts/reportSearch.ejs +248 -0
  94. package/src/static/ejs/partials/scripts/ruleOffcanvas.ejs +801 -0
  95. package/src/static/ejs/partials/scripts/screenshotLightbox.ejs +71 -0
  96. package/src/static/ejs/partials/scripts/summaryScanResults.ejs +14 -0
  97. package/src/static/ejs/partials/scripts/summaryTable.ejs +78 -0
  98. package/src/static/ejs/partials/scripts/utils.ejs +441 -0
  99. package/src/static/ejs/partials/styles/bootstrap.ejs +12375 -0
  100. package/src/static/ejs/partials/styles/highlightjs.ejs +54 -0
  101. package/src/static/ejs/partials/styles/styles.ejs +1843 -0
  102. package/src/static/ejs/partials/styles/summaryBootstrap.ejs +12458 -0
  103. package/src/static/ejs/partials/summaryHeader.ejs +70 -0
  104. package/src/static/ejs/partials/summaryMain.ejs +75 -0
  105. package/src/static/ejs/report.ejs +420 -0
  106. package/src/static/ejs/summary.ejs +47 -0
  107. package/src/static/mustache/.prettierrc +4 -0
  108. package/src/static/mustache/Attention Deficit.mustache +11 -0
  109. package/src/static/mustache/Blind.mustache +11 -0
  110. package/src/static/mustache/Cognitive.mustache +7 -0
  111. package/src/static/mustache/Colorblindness.mustache +20 -0
  112. package/src/static/mustache/Deaf.mustache +12 -0
  113. package/src/static/mustache/Deafblind.mustache +7 -0
  114. package/src/static/mustache/Dyslexia.mustache +14 -0
  115. package/src/static/mustache/Low Vision.mustache +7 -0
  116. package/src/static/mustache/Mobility.mustache +15 -0
  117. package/src/static/mustache/Sighted Keyboard Users.mustache +42 -0
  118. package/src/static/mustache/report.mustache +1709 -0
  119. package/src/types/print-message.d.ts +28 -0
  120. package/src/types/types.ts +46 -0
  121. package/src/types/xpath-to-css.d.ts +3 -0
  122. package/src/utils.ts +332 -0
  123. package/tsconfig.json +15 -0
@@ -0,0 +1,443 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import fs from 'fs-extra';
4
+ import { globSync } from 'glob';
5
+ import which from 'which';
6
+ import os from 'os';
7
+ import { spawnSync, execSync } from 'child_process';
8
+ import { chromium } from 'playwright';
9
+ import { silentLogger } from '../logs.js';
10
+
11
+ const filename = fileURLToPath(import.meta.url);
12
+ const dirname = path.dirname(filename);
13
+
14
+ const maxRequestsPerCrawl = 100;
15
+
16
+ export const blackListedFileExtensions = [
17
+ 'css',
18
+ 'js',
19
+ 'txt',
20
+ 'mp3',
21
+ 'mp4',
22
+ 'jpg',
23
+ 'jpeg',
24
+ 'png',
25
+ 'svg',
26
+ 'gif',
27
+ 'woff',
28
+ 'zip',
29
+ 'webp',
30
+ 'json',
31
+ ];
32
+
33
+ export const getIntermediateScreenshotsPath = (datasetsPath: string): string =>
34
+ `${datasetsPath}/screenshots`;
35
+ export const destinationPath = (storagePath: string): string => `${storagePath}/screenshots`;
36
+
37
+ /** Get the path to Default Profile in the Chrome Data Directory
38
+ * as per https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md
39
+ * @returns path to Default Profile in the Chrome Data Directory
40
+ */
41
+ export const getDefaultChromeDataDir = (): string => {
42
+ try {
43
+ let defaultChromeDataDir = null;
44
+ if (os.platform() === 'win32') {
45
+ defaultChromeDataDir = path.join(
46
+ os.homedir(),
47
+ 'AppData',
48
+ 'Local',
49
+ 'Google',
50
+ 'Chrome',
51
+ 'User Data',
52
+ );
53
+ } else if (os.platform() === 'darwin') {
54
+ defaultChromeDataDir = path.join(
55
+ os.homedir(),
56
+ 'Library',
57
+ 'Application Support',
58
+ 'Google',
59
+ 'Chrome',
60
+ );
61
+ }
62
+
63
+ if (defaultChromeDataDir && fs.existsSync(defaultChromeDataDir)) {
64
+ return defaultChromeDataDir;
65
+ }
66
+ return null;
67
+ } catch (error) {
68
+ console.error(`Error in getDefaultChromeDataDir(): ${error}`);
69
+ }
70
+ };
71
+
72
+ /**
73
+ * Get the path to Default Profile in the Edge Data Directory
74
+ * @returns path to Default Profile in the Edge Data Directory
75
+ */
76
+ export const getDefaultEdgeDataDir = (): string => {
77
+ try {
78
+ let defaultEdgeDataDir = null;
79
+ if (os.platform() === 'win32') {
80
+ defaultEdgeDataDir = path.join(
81
+ os.homedir(),
82
+ 'AppData',
83
+ 'Local',
84
+ 'Microsoft',
85
+ 'Edge',
86
+ 'User Data',
87
+ );
88
+ } else if (os.platform() === 'darwin') {
89
+ defaultEdgeDataDir = path.join(
90
+ os.homedir(),
91
+ 'Library',
92
+ 'Application Support',
93
+ 'Microsoft Edge',
94
+ );
95
+ }
96
+
97
+ if (defaultEdgeDataDir && fs.existsSync(defaultEdgeDataDir)) {
98
+ return defaultEdgeDataDir;
99
+ }
100
+ return null;
101
+ } catch (error) {
102
+ console.error(`Error in getDefaultEdgeDataDir(): ${error}`);
103
+ }
104
+ };
105
+
106
+ export const getDefaultChromiumDataDir = () => {
107
+ try {
108
+ let defaultChromiumDataDir = null;
109
+
110
+ if (os.platform() === 'win32') {
111
+ defaultChromiumDataDir = path.join(os.homedir(), 'AppData', 'Local', 'Chromium', 'User Data');
112
+ } else if (os.platform() === 'darwin') {
113
+ defaultChromiumDataDir = path.join(
114
+ os.homedir(),
115
+ 'Library',
116
+ 'Application Support',
117
+ 'Chromium',
118
+ );
119
+ } else {
120
+ defaultChromiumDataDir = path.join(process.cwd(), 'Chromium Support');
121
+
122
+ try {
123
+ fs.mkdirSync(defaultChromiumDataDir, { recursive: true }); // Use { recursive: true } to create parent directories if they don't exist
124
+ } catch {
125
+ defaultChromiumDataDir = '/tmp';
126
+ }
127
+
128
+ silentLogger.warn(`Using Chromium support directory at ${defaultChromiumDataDir}`);
129
+ }
130
+
131
+ if (defaultChromiumDataDir && fs.existsSync(defaultChromiumDataDir)) {
132
+ return defaultChromiumDataDir;
133
+ }
134
+ return null;
135
+ } catch (error) {
136
+ silentLogger.error(`Error in getDefaultChromiumDataDir(): ${error}`);
137
+ }
138
+ };
139
+
140
+ export const removeQuarantineFlag = function (searchPath: string) {
141
+ if (os.platform() === 'darwin') {
142
+ const execPaths = globSync(searchPath, { absolute: true, nodir: true });
143
+ if (execPaths.length > 0) {
144
+ execPaths.forEach(filePath => spawnSync('xattr', ['-d', 'com.apple.quarantine', filePath]));
145
+ }
146
+ }
147
+ };
148
+
149
+ export const getExecutablePath = function (dir: string, file: string): string {
150
+ let execPaths = globSync(`${dir}/${file}`, { absolute: true, nodir: true });
151
+
152
+ if (execPaths.length === 0) {
153
+ const execInPATH = which.sync(file, { nothrow: true });
154
+
155
+ if (execInPATH) {
156
+ return fs.realpathSync(execInPATH);
157
+ }
158
+ const splitPath =
159
+ os.platform() === 'win32' ? process.env.PATH.split(';') : process.env.PATH.split(':');
160
+
161
+ for (const path in splitPath) {
162
+ execPaths = globSync(`${path}/${file}`, { absolute: true, nodir: true });
163
+ if (execPaths.length !== 0) return fs.realpathSync(execPaths[0]);
164
+ }
165
+ return null;
166
+ }
167
+ removeQuarantineFlag(execPaths[0]);
168
+ return execPaths[0];
169
+ };
170
+
171
+ /**
172
+ * Matches the pattern user:password@domain.com
173
+ */
174
+ export const basicAuthRegex = /^.*\/\/.*:.*@.*$/i;
175
+
176
+ // for crawlers
177
+ export const axeScript = path.join(dirname, '../../node_modules/axe-core/axe.min.js');
178
+ export class UrlsCrawled {
179
+ toScan: string[] = [];
180
+ scanned: { url: string; actualUrl: string; pageTitle: string }[] = [];
181
+ invalid: string[] = [];
182
+ scannedRedirects: { fromUrl: string; toUrl: string }[] = [];
183
+ notScannedRedirects: { fromUrl: string; toUrl: string }[] = [];
184
+ outOfDomain: string[] = [];
185
+ blacklisted: string[] = [];
186
+ error: { url: string }[] = [];
187
+ exceededRequests: string[] = [];
188
+ forbidden: string[] = [];
189
+ userExcluded: string[] = [];
190
+ everything: string[] = [];
191
+
192
+ constructor(urlsCrawled?: Partial<UrlsCrawled>) {
193
+ if (urlsCrawled) {
194
+ Object.assign(this, urlsCrawled);
195
+ }
196
+ }
197
+ }
198
+
199
+ const urlsCrawledObj = new UrlsCrawled();
200
+
201
+ /* eslint-disable no-unused-vars */
202
+ export enum ScannerTypes {
203
+ SITEMAP = 'Sitemap',
204
+ WEBSITE = 'Website',
205
+ CUSTOM = 'Custom',
206
+ INTELLIGENT = 'Intelligent',
207
+ LOCALFILE = 'LocalFile',
208
+ }
209
+ /* eslint-enable no-unused-vars */
210
+
211
+ export const guiInfoStatusTypes = {
212
+ SCANNED: 'scanned',
213
+ SKIPPED: 'skipped',
214
+ COMPLETED: 'completed',
215
+ ERROR: 'error',
216
+ DUPLICATE: 'duplicate',
217
+ };
218
+
219
+ let launchOptionsArgs = [];
220
+
221
+ // Check if running in docker container
222
+ if (fs.existsSync('/.dockerenv')) {
223
+ launchOptionsArgs = ['--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage'];
224
+ }
225
+
226
+ export const getProxy = (): { type: string; url: string } | null => {
227
+ if (os.platform() === 'win32') {
228
+ let internetSettings: string[];
229
+ try {
230
+ internetSettings = execSync(
231
+ 'Get-ItemProperty -Path "Registry::HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"',
232
+ { shell: 'powershell.exe' },
233
+ )
234
+ .toString()
235
+ .split('\n');
236
+ } catch (e) {
237
+ console.log(e.toString());
238
+ silentLogger.error(e.toString());
239
+ }
240
+
241
+ const getSettingValue = (settingName: string) =>
242
+ internetSettings
243
+ .find(s => s.startsWith(settingName))
244
+ // split only once at with ':' as the delimiter
245
+ ?.split(/:(.*)/s)[1]
246
+ ?.trim();
247
+
248
+ if (getSettingValue('AutoConfigURL')) {
249
+ return { type: 'autoConfig', url: getSettingValue('AutoConfigURL') };
250
+ }
251
+ if (getSettingValue('ProxyEnable') === '1') {
252
+ return { type: 'manualProxy', url: getSettingValue('ProxyServer') };
253
+ }
254
+ return null;
255
+ }
256
+ // develop for mac
257
+ return null;
258
+ };
259
+
260
+ export const proxy = getProxy();
261
+
262
+ if (proxy && proxy.type === 'autoConfig') {
263
+ launchOptionsArgs.push(`--proxy-pac-url=${proxy.url}`);
264
+ } else if (proxy && proxy.type === 'manualProxy') {
265
+ launchOptionsArgs.push(`--proxy-server=${proxy.url}`);
266
+ }
267
+
268
+ export const impactOrder = {
269
+ minor: 0,
270
+ moderate: 1,
271
+ serious: 2,
272
+ critical: 3,
273
+ };
274
+
275
+ export const formDataFields = {
276
+ formUrl: `https://docs.google.com/forms/d/e/1FAIpQLSem5C8fyNs5TiU5Vv2Y63-SH7CHN86f-LEPxeN_1u_ldUbgUA/formResponse`, // prod
277
+ entryUrlField: 'entry.1562345227',
278
+ redirectUrlField: 'entry.473072563',
279
+ scanTypeField: 'entry.1148680657',
280
+ emailField: 'entry.52161304',
281
+ nameField: 'entry.1787318910',
282
+ resultsField: 'entry.904051439',
283
+ numberOfPagesScannedField: 'entry.238043773',
284
+ additionalPageDataField: 'entry.2090887881',
285
+ metadataField: 'entry.1027769131',
286
+ };
287
+
288
+ export const sitemapPaths = [
289
+ '/sitemap.xml',
290
+ '/sitemap/sitemap.xml',
291
+ '/sitemap-index.xml',
292
+ '/sitemap_index.xml',
293
+ '/sitemapindex.xml',
294
+ '/sitemap/index.xml',
295
+ '/sitemap1.xml',
296
+ '/sitemap/',
297
+ '/post-sitemap',
298
+ '/page-sitemap',
299
+ '/sitemap.txt',
300
+ '/sitemap.php',
301
+ '/sitemap.xml.bz2',
302
+ '/sitemap.xml.xz',
303
+ '/sitemap_index.xml.bz2',
304
+ '/sitemap_index.xml.xz',
305
+ ];
306
+
307
+ const wcagLinks = {
308
+ 'WCAG 1.1.1': 'https://www.w3.org/TR/WCAG21/#non-text-content',
309
+ 'WCAG 1.2.2': 'https://www.w3.org/TR/WCAG21/#captions-prerecorded',
310
+ 'WCAG 1.3.1': 'https://www.w3.org/TR/WCAG21/#info-and-relationships',
311
+ // 'WCAG 1.3.4': 'https://www.w3.org/TR/WCAG21/#orientation', - TODO: review for veraPDF
312
+ 'WCAG 1.3.5': 'https://www.w3.org/TR/WCAG21/#use-of-color',
313
+ 'WCAG 1.4.1': 'https://www.w3.org/TR/WCAG21/#use-of-color',
314
+ 'WCAG 1.4.2': 'https://www.w3.org/TR/WCAG21/#audio-control',
315
+ 'WCAG 1.4.3': 'https://www.w3.org/TR/WCAG21/#contrast-minimum',
316
+ 'WCAG 1.4.4': 'https://www.w3.org/TR/WCAG21/#resize-text',
317
+ 'WCAG 1.4.6': 'https://www.w3.org/TR/WCAG21/#contrast-enhanced',
318
+ // 'WCAG 1.4.10': 'https://www.w3.org/TR/WCAG21/#reflow', - TODO: review for veraPDF
319
+ 'WCAG 1.4.12': 'https://www.w3.org/TR/WCAG21/#text-spacing',
320
+ 'WCAG 2.1.1': 'https://www.w3.org/TR/WCAG21/#pause-stop-hide',
321
+ 'WCAG 2.2.1': 'https://www.w3.org/TR/WCAG21/#timing-adjustable',
322
+ 'WCAG 2.2.2': 'https://www.w3.org/TR/WCAG21/#pause-stop-hide',
323
+ 'WCAG 2.2.4': 'https://www.w3.org/TR/WCAG21/#interruptions',
324
+ 'WCAG 2.4.1': 'https://www.w3.org/TR/WCAG21/#bypass-blocks',
325
+ 'WCAG 2.4.2': 'https://www.w3.org/TR/WCAG21/#page-titled',
326
+ 'WCAG 2.4.3': 'https://www.w3.org/TR/WCAG21/#focus-order',
327
+ 'WCAG 2.4.4': 'https://www.w3.org/TR/WCAG21/#link-purpose-in-context',
328
+ 'WCAG 2.4.9': 'https://www.w3.org/TR/WCAG21/#link-purpose-link-only',
329
+ 'WCAG 2.5.8': 'https://www.w3.org/TR/WCAG22/#target-size-minimum',
330
+ 'WCAG 3.1.1': 'https://www.w3.org/TR/WCAG21/#language-of-page',
331
+ 'WCAG 3.1.2': 'https://www.w3.org/TR/WCAG21/#labels-or-instructions',
332
+ 'WCAG 3.2.5': 'https://www.w3.org/TR/WCAG21/#change-on-request',
333
+ 'WCAG 4.1.2': 'https://www.w3.org/TR/WCAG21/#name-role-value',
334
+ };
335
+
336
+ const urlCheckStatuses = {
337
+ success: { code: 0 },
338
+ invalidUrl: { code: 11, message: 'Invalid URL or URL is not using http or https.' },
339
+ cannotBeResolved: {
340
+ code: 12,
341
+ message:
342
+ 'Provided URL cannot be accessed. Please verify your internet connectivity and the correctness of the domain.',
343
+ },
344
+ errorStatusReceived: {
345
+ // unused for now
346
+ code: 13,
347
+ message: 'Provided URL cannot be accessed. Server responded with code ', // append it with the response code received,
348
+ },
349
+ systemError: {
350
+ code: 14,
351
+ message: 'Something went wrong when verifying the URL. Please try again later.',
352
+ },
353
+ notASitemap: { code: 15, message: 'Provided URL is not a sitemap.' },
354
+ unauthorised: { code: 16, message: 'Provided URL needs basic authorisation.' },
355
+ browserError: {
356
+ code: 17,
357
+ message:
358
+ 'No browser available to run scans. Please ensure you have Chrome or Edge (for Windows only) installed.',
359
+ },
360
+ axiosTimeout: { code: 18, message: 'Axios timeout exceeded. Falling back on browser checks.' },
361
+ notALocalFile: { code: 19, message: 'Provided filepath is not a local html or sitemap file.' },
362
+ };
363
+
364
+ /* eslint-disable no-unused-vars */
365
+ export enum BrowserTypes {
366
+ CHROMIUM = 'chromium',
367
+ CHROME = 'chrome',
368
+ EDGE = 'msedge',
369
+ }
370
+ /* eslint-enable no-unused-vars */
371
+
372
+ const xmlSitemapTypes = {
373
+ xml: 0,
374
+ xmlIndex: 1,
375
+ rss: 2,
376
+ atom: 3,
377
+ unknown: 4,
378
+ };
379
+
380
+ const forbiddenCharactersInDirPath = ['<', '>', ':', '"', '\\', '/', '|', '?', '*'];
381
+
382
+ const reserveFileNameKeywords = [
383
+ 'CON',
384
+ 'PRN',
385
+ 'AUX',
386
+ 'NUL',
387
+ 'COM1',
388
+ 'COM2',
389
+ 'COM3',
390
+ 'COM4',
391
+ 'COM5',
392
+ 'COM6',
393
+ 'COM7',
394
+ 'COM8',
395
+ 'COM9',
396
+ 'LPT1',
397
+ 'LPT2',
398
+ 'LPT3',
399
+ 'LPT4',
400
+ 'LPT5',
401
+ 'LPT6',
402
+ 'LPT7',
403
+ 'LPT8',
404
+ 'LPT9',
405
+ ];
406
+
407
+ export default {
408
+ cliZipFileName: 'oobee-scan-results.zip',
409
+ exportDirectory: `${process.cwd()}`,
410
+ maxRequestsPerCrawl,
411
+ maxConcurrency: 25,
412
+ urlsCrawledObj,
413
+ impactOrder,
414
+ launchOptionsArgs,
415
+ xmlSitemapTypes,
416
+ urlCheckStatuses,
417
+ launcher: chromium,
418
+ pdfScanResultFileName: 'pdf-scan-results.json',
419
+ forbiddenCharactersInDirPath,
420
+ reserveFileNameKeywords,
421
+ wcagLinks,
422
+ robotsTxtUrls: null,
423
+ };
424
+
425
+ export const rootPath = dirname;
426
+ export const wcagWebPage = 'https://www.w3.org/TR/WCAG21/';
427
+ const latestAxeVersion = '4.9';
428
+ export const axeVersion = latestAxeVersion;
429
+ export const axeWebPage = `https://dequeuniversity.com/rules/axe/${latestAxeVersion}/`;
430
+
431
+ export const saflyIconSelector = `#__safly_icon`;
432
+ export const cssQuerySelectors = [
433
+ ':not(a):is([role="link"]',
434
+ 'button[onclick])',
435
+ 'a:not([href])',
436
+ '[role="button"]:not(a[href])', // Add this line to select elements with role="button" where it is not <a> with href
437
+ ];
438
+
439
+ export enum RuleFlags {
440
+ DEFAULT = 'default',
441
+ DISABLE_OOBEE = 'disable-oobee',
442
+ ENABLE_WCAG_AAA = 'enable-wcag-aaa',
443
+ }