@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.
- package/.dockerignore +22 -0
- package/.github/pull_request_template.md +11 -0
- package/.github/workflows/docker-test.yml +54 -0
- package/.github/workflows/image.yml +107 -0
- package/.github/workflows/publish.yml +18 -0
- package/.idea/modules.xml +8 -0
- package/.idea/purple-a11y.iml +9 -0
- package/.idea/vcs.xml +6 -0
- package/.prettierrc.json +12 -0
- package/.vscode/extensions.json +5 -0
- package/.vscode/settings.json +10 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/DETAILS.md +163 -0
- package/Dockerfile +60 -0
- package/INSTALLATION.md +146 -0
- package/INTEGRATION.md +785 -0
- package/LICENSE +22 -0
- package/README.md +587 -0
- package/SECURITY.md +5 -0
- package/__mocks__/mock-report.html +1431 -0
- package/__mocks__/mockFunctions.ts +32 -0
- package/__mocks__/mockIssues.ts +64 -0
- package/__mocks__/mock_all_issues/000000001.json +64 -0
- package/__mocks__/mock_all_issues/000000002.json +53 -0
- package/__mocks__/mock_all_issues/fake-file.txt +0 -0
- package/__tests__/logs.test.ts +25 -0
- package/__tests__/mergeAxeResults.test.ts +278 -0
- package/__tests__/utils.test.ts +118 -0
- package/a11y-scan-results.zip +0 -0
- package/eslint.config.js +53 -0
- package/exclusions.txt +2 -0
- package/gitlab-pipeline-template.yml +54 -0
- package/jest.config.js +1 -0
- package/package.json +96 -0
- package/scripts/copyFiles.js +44 -0
- package/scripts/install_oobee_dependencies.cmd +13 -0
- package/scripts/install_oobee_dependencies.command +101 -0
- package/scripts/install_oobee_dependencies.ps1 +110 -0
- package/scripts/oobee_shell.cmd +13 -0
- package/scripts/oobee_shell.command +11 -0
- package/scripts/oobee_shell.sh +55 -0
- package/scripts/oobee_shell_ps.ps1 +54 -0
- package/src/cli.ts +401 -0
- package/src/combine.ts +240 -0
- package/src/constants/__tests__/common.test.ts +44 -0
- package/src/constants/cliFunctions.ts +305 -0
- package/src/constants/common.ts +1840 -0
- package/src/constants/constants.ts +443 -0
- package/src/constants/errorMeta.json +319 -0
- package/src/constants/itemTypeDescription.ts +11 -0
- package/src/constants/oobeeAi.ts +141 -0
- package/src/constants/questions.ts +181 -0
- package/src/constants/sampleData.ts +187 -0
- package/src/crawlers/__tests__/commonCrawlerFunc.test.ts +51 -0
- package/src/crawlers/commonCrawlerFunc.ts +656 -0
- package/src/crawlers/crawlDomain.ts +877 -0
- package/src/crawlers/crawlIntelligentSitemap.ts +156 -0
- package/src/crawlers/crawlLocalFile.ts +193 -0
- package/src/crawlers/crawlSitemap.ts +356 -0
- package/src/crawlers/custom/extractAndGradeText.ts +57 -0
- package/src/crawlers/custom/flagUnlabelledClickableElements.ts +964 -0
- package/src/crawlers/custom/utils.ts +486 -0
- package/src/crawlers/customAxeFunctions.ts +82 -0
- package/src/crawlers/pdfScanFunc.ts +468 -0
- package/src/crawlers/runCustom.ts +117 -0
- package/src/index.ts +173 -0
- package/src/logs.ts +66 -0
- package/src/mergeAxeResults.ts +964 -0
- package/src/npmIndex.ts +284 -0
- package/src/screenshotFunc/htmlScreenshotFunc.ts +411 -0
- package/src/screenshotFunc/pdfScreenshotFunc.ts +762 -0
- package/src/static/ejs/partials/components/categorySelector.ejs +4 -0
- package/src/static/ejs/partials/components/categorySelectorDropdown.ejs +57 -0
- package/src/static/ejs/partials/components/pagesScannedModal.ejs +70 -0
- package/src/static/ejs/partials/components/reportSearch.ejs +47 -0
- package/src/static/ejs/partials/components/ruleOffcanvas.ejs +105 -0
- package/src/static/ejs/partials/components/scanAbout.ejs +263 -0
- package/src/static/ejs/partials/components/screenshotLightbox.ejs +13 -0
- package/src/static/ejs/partials/components/summaryScanAbout.ejs +141 -0
- package/src/static/ejs/partials/components/summaryScanResults.ejs +16 -0
- package/src/static/ejs/partials/components/summaryTable.ejs +20 -0
- package/src/static/ejs/partials/components/summaryWcagCompliance.ejs +94 -0
- package/src/static/ejs/partials/components/topFive.ejs +6 -0
- package/src/static/ejs/partials/components/wcagCompliance.ejs +70 -0
- package/src/static/ejs/partials/footer.ejs +21 -0
- package/src/static/ejs/partials/header.ejs +230 -0
- package/src/static/ejs/partials/main.ejs +40 -0
- package/src/static/ejs/partials/scripts/bootstrap.ejs +8 -0
- package/src/static/ejs/partials/scripts/categorySelectorDropdownScript.ejs +190 -0
- package/src/static/ejs/partials/scripts/categorySummary.ejs +141 -0
- package/src/static/ejs/partials/scripts/highlightjs.ejs +335 -0
- package/src/static/ejs/partials/scripts/popper.ejs +7 -0
- package/src/static/ejs/partials/scripts/reportSearch.ejs +248 -0
- package/src/static/ejs/partials/scripts/ruleOffcanvas.ejs +801 -0
- package/src/static/ejs/partials/scripts/screenshotLightbox.ejs +71 -0
- package/src/static/ejs/partials/scripts/summaryScanResults.ejs +14 -0
- package/src/static/ejs/partials/scripts/summaryTable.ejs +78 -0
- package/src/static/ejs/partials/scripts/utils.ejs +441 -0
- package/src/static/ejs/partials/styles/bootstrap.ejs +12375 -0
- package/src/static/ejs/partials/styles/highlightjs.ejs +54 -0
- package/src/static/ejs/partials/styles/styles.ejs +1843 -0
- package/src/static/ejs/partials/styles/summaryBootstrap.ejs +12458 -0
- package/src/static/ejs/partials/summaryHeader.ejs +70 -0
- package/src/static/ejs/partials/summaryMain.ejs +75 -0
- package/src/static/ejs/report.ejs +420 -0
- package/src/static/ejs/summary.ejs +47 -0
- package/src/static/mustache/.prettierrc +4 -0
- package/src/static/mustache/Attention Deficit.mustache +11 -0
- package/src/static/mustache/Blind.mustache +11 -0
- package/src/static/mustache/Cognitive.mustache +7 -0
- package/src/static/mustache/Colorblindness.mustache +20 -0
- package/src/static/mustache/Deaf.mustache +12 -0
- package/src/static/mustache/Deafblind.mustache +7 -0
- package/src/static/mustache/Dyslexia.mustache +14 -0
- package/src/static/mustache/Low Vision.mustache +7 -0
- package/src/static/mustache/Mobility.mustache +15 -0
- package/src/static/mustache/Sighted Keyboard Users.mustache +42 -0
- package/src/static/mustache/report.mustache +1709 -0
- package/src/types/print-message.d.ts +28 -0
- package/src/types/types.ts +46 -0
- package/src/types/xpath-to-css.d.ts +3 -0
- package/src/utils.ts +332 -0
- package/tsconfig.json +15 -0
package/src/index.ts
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
/* eslint-disable func-names */
|
3
|
+
/* eslint-disable no-param-reassign */
|
4
|
+
import printMessage from 'print-message';
|
5
|
+
import inquirer from 'inquirer';
|
6
|
+
import { EnqueueStrategy } from 'crawlee';
|
7
|
+
import {
|
8
|
+
getVersion,
|
9
|
+
cleanUp,
|
10
|
+
setHeadlessMode,
|
11
|
+
getUserDataTxt,
|
12
|
+
writeToUserDataTxt,
|
13
|
+
} from './utils.js';
|
14
|
+
import {
|
15
|
+
prepareData,
|
16
|
+
messageOptions,
|
17
|
+
getPlaywrightDeviceDetailsObject,
|
18
|
+
getBrowserToRun,
|
19
|
+
getScreenToScan,
|
20
|
+
getClonedProfilesWithRandomToken,
|
21
|
+
deleteClonedProfiles,
|
22
|
+
} from './constants/common.js';
|
23
|
+
import questions from './constants/questions.js';
|
24
|
+
import combineRun from './combine.js';
|
25
|
+
import { BrowserTypes, RuleFlags, ScannerTypes } from './constants/constants.js';
|
26
|
+
|
27
|
+
export type Answers = {
|
28
|
+
headless: boolean;
|
29
|
+
deviceChosen: string;
|
30
|
+
customDevice: string;
|
31
|
+
viewportWidth: number;
|
32
|
+
browserToRun: BrowserTypes;
|
33
|
+
scanner: ScannerTypes;
|
34
|
+
url: string;
|
35
|
+
clonedBrowserDataDir: string;
|
36
|
+
playwrightDeviceDetailsObject: object;
|
37
|
+
nameEmail: string;
|
38
|
+
fileTypes: string;
|
39
|
+
metadata: string;
|
40
|
+
maxpages: number;
|
41
|
+
strategy: string;
|
42
|
+
isLocalFileScan: boolean;
|
43
|
+
finalUrl: string;
|
44
|
+
customFlowLabel: string;
|
45
|
+
specifiedMaxConcurrency: number;
|
46
|
+
blacklistedPatternsFilename: string;
|
47
|
+
additional: string;
|
48
|
+
followRobots: boolean;
|
49
|
+
header: string;
|
50
|
+
safeMode: boolean;
|
51
|
+
exportDirectory: string;
|
52
|
+
zip: string;
|
53
|
+
ruleset: RuleFlags[];
|
54
|
+
};
|
55
|
+
|
56
|
+
export type Data = {
|
57
|
+
type: ScannerTypes;
|
58
|
+
url: string;
|
59
|
+
entryUrl: string;
|
60
|
+
isHeadless: boolean;
|
61
|
+
deviceChosen: string;
|
62
|
+
customDevice: string;
|
63
|
+
viewportWidth: number;
|
64
|
+
playwrightDeviceDetailsObject: object;
|
65
|
+
maxRequestsPerCrawl: number;
|
66
|
+
strategy: EnqueueStrategy;
|
67
|
+
isLocalFileScan: boolean;
|
68
|
+
browser: string;
|
69
|
+
nameEmail: string;
|
70
|
+
customFlowLabel: string;
|
71
|
+
specifiedMaxConcurrency: number;
|
72
|
+
randomToken: string;
|
73
|
+
fileTypes: string;
|
74
|
+
blacklistedPatternsFilename: string;
|
75
|
+
includeScreenshots: boolean;
|
76
|
+
metadata: string;
|
77
|
+
followRobots: boolean;
|
78
|
+
extraHTTPHeaders: Record<string, string>;
|
79
|
+
safeMode: boolean;
|
80
|
+
userDataDirectory?: string;
|
81
|
+
zip?: string;
|
82
|
+
ruleset: RuleFlags[];
|
83
|
+
};
|
84
|
+
|
85
|
+
const userData = getUserDataTxt();
|
86
|
+
|
87
|
+
const runScan = async (answers: Answers) => {
|
88
|
+
const screenToScan = getScreenToScan(
|
89
|
+
answers.deviceChosen,
|
90
|
+
answers.customDevice,
|
91
|
+
answers.viewportWidth,
|
92
|
+
);
|
93
|
+
answers.playwrightDeviceDetailsObject = getPlaywrightDeviceDetailsObject(
|
94
|
+
answers.deviceChosen,
|
95
|
+
answers.customDevice,
|
96
|
+
answers.viewportWidth,
|
97
|
+
);
|
98
|
+
const { browserToRun } = getBrowserToRun(BrowserTypes.CHROME);
|
99
|
+
deleteClonedProfiles(browserToRun);
|
100
|
+
answers.browserToRun = browserToRun;
|
101
|
+
|
102
|
+
if (!answers.nameEmail) {
|
103
|
+
answers.nameEmail = `${userData.name}:${userData.email}`;
|
104
|
+
}
|
105
|
+
|
106
|
+
answers.fileTypes = 'html-only';
|
107
|
+
answers.metadata = '{}';
|
108
|
+
|
109
|
+
const data: Data = await prepareData(answers);
|
110
|
+
data.userDataDirectory = getClonedProfilesWithRandomToken(data.browser, data.randomToken);
|
111
|
+
|
112
|
+
setHeadlessMode(data.browser, data.isHeadless);
|
113
|
+
printMessage(['Scanning website...'], messageOptions);
|
114
|
+
|
115
|
+
await combineRun(data, screenToScan);
|
116
|
+
|
117
|
+
// Delete cloned directory
|
118
|
+
deleteClonedProfiles(data.browser);
|
119
|
+
|
120
|
+
// Delete dataset and request queues
|
121
|
+
cleanUp(data.randomToken);
|
122
|
+
|
123
|
+
process.exit(0);
|
124
|
+
};
|
125
|
+
|
126
|
+
if (userData) {
|
127
|
+
printMessage(
|
128
|
+
[
|
129
|
+
`Oobee (ver ${getVersion()})`,
|
130
|
+
'We recommend using Chrome browser for the best experience.',
|
131
|
+
'',
|
132
|
+
`Welcome back ${userData.name}!`,
|
133
|
+
`(Refer to readme.txt on how to change your profile)`,
|
134
|
+
],
|
135
|
+
{
|
136
|
+
// Note that the color is based on kleur NPM package
|
137
|
+
border: true,
|
138
|
+
borderColor: 'magenta',
|
139
|
+
},
|
140
|
+
);
|
141
|
+
|
142
|
+
inquirer.prompt(questions).then(async answers => {
|
143
|
+
await runScan(answers);
|
144
|
+
});
|
145
|
+
} else {
|
146
|
+
printMessage(
|
147
|
+
[`Oobee (ver ${getVersion()})`, 'We recommend using Chrome browser for the best experience.'],
|
148
|
+
{
|
149
|
+
// Note that the color is based on kleur NPM package
|
150
|
+
border: true,
|
151
|
+
borderColor: 'magenta',
|
152
|
+
},
|
153
|
+
);
|
154
|
+
|
155
|
+
printMessage(
|
156
|
+
[
|
157
|
+
`To personalise your experience, we will be collecting your name, email address and app usage data.`,
|
158
|
+
`Your information fully complies with GovTech's Privacy Policy.`,
|
159
|
+
],
|
160
|
+
{
|
161
|
+
border: false,
|
162
|
+
},
|
163
|
+
);
|
164
|
+
|
165
|
+
inquirer.prompt(questions).then(async answers => {
|
166
|
+
const { name, email } = answers;
|
167
|
+
answers.nameEmail = `${name}:${email}`;
|
168
|
+
await writeToUserDataTxt('name', name);
|
169
|
+
await writeToUserDataTxt('email', email);
|
170
|
+
|
171
|
+
await runScan(answers);
|
172
|
+
});
|
173
|
+
}
|
package/src/logs.ts
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
/* eslint-disable no-console */
|
2
|
+
/* eslint-disable no-shadow */
|
3
|
+
import { createLogger, format, transports } from 'winston';
|
4
|
+
import { guiInfoStatusTypes } from './constants/constants.js';
|
5
|
+
import { urlWithoutAuth } from './constants/common.js';
|
6
|
+
|
7
|
+
const { combine, timestamp, printf } = format;
|
8
|
+
|
9
|
+
// Sample output
|
10
|
+
// {"timestamp":"2020-11-25 17:29:07","level":"error","message":"hello world"}
|
11
|
+
const logFormat = printf(({ timestamp, level, message }) => {
|
12
|
+
const log = {
|
13
|
+
timestamp: `${timestamp}`,
|
14
|
+
level: `${level}`,
|
15
|
+
message: `${message}`,
|
16
|
+
};
|
17
|
+
|
18
|
+
return JSON.stringify(log);
|
19
|
+
});
|
20
|
+
|
21
|
+
// transport: storage device for logs
|
22
|
+
// Enabled for console and storing into files; Files are overwritten each time
|
23
|
+
// All logs in combined.txt, error in errors.txt
|
24
|
+
|
25
|
+
const consoleLogger = createLogger({
|
26
|
+
format: combine(timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), logFormat),
|
27
|
+
transports: [new transports.Console()],
|
28
|
+
});
|
29
|
+
|
30
|
+
// No display in consoles, this will mostly be used within the interactive script to avoid disrupting the flow
|
31
|
+
// Also used in common functions to not link internal information
|
32
|
+
// if running from mass scanner, log out errors in console
|
33
|
+
const silentLogger = createLogger({
|
34
|
+
format: combine(timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), logFormat),
|
35
|
+
transports: [
|
36
|
+
process.env.OOBEE_VERBOSE
|
37
|
+
? new transports.Console({ handleExceptions: true })
|
38
|
+
: new transports.File({ filename: 'errors.txt', level: 'warn', handleExceptions: true }),
|
39
|
+
].filter(Boolean),
|
40
|
+
});
|
41
|
+
|
42
|
+
// guiInfoLogger feeds the gui information via console log and is mainly used for scanning process
|
43
|
+
export const guiInfoLog = (status, data) => {
|
44
|
+
if (process.env.RUNNING_FROM_PH_GUI || process.env.OOBEE_VERBOSE) {
|
45
|
+
switch (status) {
|
46
|
+
case guiInfoStatusTypes.COMPLETED:
|
47
|
+
console.log('Scan completed');
|
48
|
+
break;
|
49
|
+
case guiInfoStatusTypes.SCANNED:
|
50
|
+
case guiInfoStatusTypes.SKIPPED:
|
51
|
+
case guiInfoStatusTypes.ERROR:
|
52
|
+
case guiInfoStatusTypes.DUPLICATE:
|
53
|
+
console.log(
|
54
|
+
`crawling::${data.numScanned || 0}::${status}::${
|
55
|
+
urlWithoutAuth(data.urlScanned) || 'no url provided'
|
56
|
+
}`,
|
57
|
+
);
|
58
|
+
break;
|
59
|
+
default:
|
60
|
+
console.log(`Status provided to gui info log not recognized: ${status}`);
|
61
|
+
break;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
};
|
65
|
+
|
66
|
+
export { logFormat, consoleLogger, silentLogger };
|