@govtechsg/oobee 0.10.69 → 0.10.72
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/DETAILS.md +0 -1
- package/README.md +13 -1
- package/S3_UPLOAD_README.md +172 -0
- package/dev/runGenerateJustHtmlReport.ts +25 -0
- package/package.json +4 -2
- package/src/combine.ts +71 -14
- package/src/constants/common.ts +89 -91
- package/src/constants/constants.ts +535 -60
- package/src/crawlers/crawlDomain.ts +313 -305
- package/src/crawlers/crawlIntelligentSitemap.ts +24 -18
- package/src/crawlers/crawlLocalFile.ts +31 -25
- package/src/crawlers/crawlSitemap.ts +264 -253
- package/src/crawlers/custom/utils.ts +809 -119
- package/src/crawlers/guards/urlGuard.ts +77 -0
- package/src/crawlers/runCustom.ts +32 -4
- package/src/generateHtmlReport.ts +224 -0
- package/src/mergeAxeResults.ts +94 -44
- package/src/runGenerateJustHtmlReport.ts +20 -0
- package/src/services/s3Uploader.ts +184 -0
- package/src/static/ejs/partials/components/allIssues/AllIssues.ejs +9 -0
- package/src/static/ejs/partials/components/allIssues/CategoryBadges.ejs +82 -0
- package/src/static/ejs/partials/components/allIssues/FilterBar.ejs +33 -0
- package/src/static/ejs/partials/components/allIssues/IssuesTable.ejs +41 -0
- package/src/static/ejs/partials/components/header/SiteInfo.ejs +119 -0
- package/src/static/ejs/partials/components/header/aboutScanModal/AboutScanModal.ejs +15 -0
- package/src/static/ejs/partials/components/header/aboutScanModal/ScanConfiguration.ejs +44 -0
- package/src/static/ejs/partials/components/header/aboutScanModal/ScanDetails.ejs +142 -0
- package/src/static/ejs/partials/components/prioritiseIssues/IssueDetailCard.ejs +36 -0
- package/src/static/ejs/partials/components/prioritiseIssues/PrioritiseIssues.ejs +47 -0
- package/src/static/ejs/partials/components/ruleModal/ruleOffcanvas.ejs +196 -0
- package/src/static/ejs/partials/components/scannedPagesSegmentedTabs.ejs +48 -0
- package/src/static/ejs/partials/components/shared/InfoAlert.ejs +3 -0
- package/src/static/ejs/partials/components/{topFive.ejs → topTen.ejs} +2 -2
- package/src/static/ejs/partials/components/wcagCompliance/FailedCriteria.ejs +47 -0
- package/src/static/ejs/partials/components/wcagCompliance/WcagCompliance.ejs +16 -0
- package/src/static/ejs/partials/components/wcagCompliance/WcagGaugeBar.ejs +16 -0
- package/src/static/ejs/partials/components/wcagCoverageDetails.ejs +18 -0
- package/src/static/ejs/partials/footer.ejs +1 -1
- package/src/static/ejs/partials/header.ejs +7 -223
- package/src/static/ejs/partials/main.ejs +12 -23
- package/src/static/ejs/partials/scripts/allIssues/AllIssues.ejs +376 -0
- package/src/static/ejs/partials/scripts/categorySummary.ejs +1 -1
- package/src/static/ejs/partials/scripts/header/SiteInfo.ejs +44 -0
- package/src/static/ejs/partials/scripts/header/aboutScanModal/AboutScanModal.ejs +51 -0
- package/src/static/ejs/partials/scripts/header/aboutScanModal/ScanConfiguration.ejs +127 -0
- package/src/static/ejs/partials/scripts/header/aboutScanModal/ScanDetails.ejs +60 -0
- package/src/static/ejs/partials/scripts/prioritiseIssues/IssueDetailCard.ejs +137 -0
- package/src/static/ejs/partials/scripts/prioritiseIssues/PrioritiseIssues.ejs +214 -0
- package/src/static/ejs/partials/scripts/prioritiseIssues/wcagSvgMap.ejs +861 -0
- package/src/static/ejs/partials/scripts/ruleModal/constants.ejs +949 -0
- package/src/static/ejs/partials/scripts/ruleModal/itemCardRenderer.ejs +352 -0
- package/src/static/ejs/partials/scripts/ruleModal/pageAccordionBuilder.ejs +468 -0
- package/src/static/ejs/partials/scripts/ruleModal/ruleOffcanvas.ejs +306 -0
- package/src/static/ejs/partials/scripts/ruleModal/utilities.ejs +483 -0
- package/src/static/ejs/partials/scripts/scannedPagesSegmentedTabs.ejs +35 -0
- package/src/static/ejs/partials/scripts/screenshotLightbox.ejs +61 -57
- package/src/static/ejs/partials/scripts/topTen.ejs +61 -0
- package/src/static/ejs/partials/scripts/utils.ejs +15 -0
- package/src/static/ejs/partials/scripts/wcagCompliance/FailedCriteria.ejs +103 -0
- package/src/static/ejs/partials/scripts/wcagCompliance/WcagGaugeBar.ejs +47 -0
- package/src/static/ejs/partials/scripts/wcagCompliance.ejs +15 -0
- package/src/static/ejs/partials/scripts/wcagCoverageDetails.ejs +75 -0
- package/src/static/ejs/partials/styles/allIssues/AllIssues.ejs +384 -0
- package/src/static/ejs/partials/styles/bootstrap.ejs +17 -1
- package/src/static/ejs/partials/styles/header/SiteInfo.ejs +121 -0
- package/src/static/ejs/partials/styles/header/aboutScanModal/AboutScanModal.ejs +82 -0
- package/src/static/ejs/partials/styles/header/aboutScanModal/ScanConfiguration.ejs +50 -0
- package/src/static/ejs/partials/styles/header/aboutScanModal/ScanDetails.ejs +149 -0
- package/src/static/ejs/partials/styles/header.ejs +7 -0
- package/src/static/ejs/partials/styles/prioritiseIssues/IssueDetailCard.ejs +141 -0
- package/src/static/ejs/partials/styles/prioritiseIssues/PrioritiseIssues.ejs +204 -0
- package/src/static/ejs/partials/styles/ruleModal/ruleOffcanvas.ejs +456 -0
- package/src/static/ejs/partials/styles/scannedPagesSegmentedTabs.ejs +46 -0
- package/src/static/ejs/partials/styles/shared/InfoAlert.ejs +12 -0
- package/src/static/ejs/partials/styles/styles.ejs +198 -470
- package/src/static/ejs/partials/styles/topTenCard.ejs +44 -0
- package/src/static/ejs/partials/styles/wcagCompliance/FailedCriteria.ejs +59 -0
- package/src/static/ejs/partials/styles/wcagCompliance/WcagGaugeBar.ejs +62 -0
- package/src/static/ejs/partials/styles/wcagCompliance.ejs +36 -0
- package/src/static/ejs/partials/styles/wcagCoverageDetails.ejs +33 -0
- package/src/static/ejs/report.ejs +42 -259
- package/src/static/ejs/summary.ejs +1 -1
- package/src/utils.ts +30 -0
- package/src/static/ejs/partials/components/categorySelector.ejs +0 -4
- package/src/static/ejs/partials/components/categorySelectorDropdown.ejs +0 -57
- package/src/static/ejs/partials/components/pagesScannedModal.ejs +0 -70
- package/src/static/ejs/partials/components/reportSearch.ejs +0 -47
- package/src/static/ejs/partials/components/ruleOffcanvas.ejs +0 -105
- package/src/static/ejs/partials/components/scanAbout.ejs +0 -328
- package/src/static/ejs/partials/components/wcagCompliance.ejs +0 -52
- package/src/static/ejs/partials/scripts/categorySelectorDropdownScript.ejs +0 -190
- package/src/static/ejs/partials/scripts/reportSearch.ejs +0 -287
- package/src/static/ejs/partials/scripts/ruleOffcanvas.ejs +0 -804
- package/src/static/ejs/partials/scripts/scanAboutScript.ejs +0 -38
package/DETAILS.md
CHANGED
|
@@ -168,7 +168,6 @@ Note: Level AAA are disabled by default. Please specify `enable-wcag-aaa` in ru
|
|
|
168
168
|
| Issue ID | Issue Description | Severity |
|
|
169
169
|
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
|
|
170
170
|
| accesskeys | Ensures every accesskey attribute value is unique | Good to Fix |
|
|
171
|
-
| aria-allowed-role | Ensures role attribute has an appropriate value for the element | Good to Fix |
|
|
172
171
|
| aria-dialog-name | Ensures every ARIA dialog and alertdialog node has an accessible name | Good to Fix |
|
|
173
172
|
| aria-text | Ensures role="text" is used on elements with no focusable descendants | Good to Fix |
|
|
174
173
|
| aria-treeitem-name | Ensures every ARIA treeitem node has an accessible name | Good to Fix |
|
package/README.md
CHANGED
|
@@ -571,7 +571,7 @@ The following URL and file validation error codes are provided to troubleshoot t
|
|
|
571
571
|
| 11 | invalidUrl | Invalid URL. Please check and try again. | • Ensure the URL starts with `http://` or `https://`.<br>• Check for typos in the URL. |
|
|
572
572
|
| 12 | cannotBeResolved | URL cannot be accessed. Please verify whether the website exists. | • Confirm the domain name is correct.<br>• Check DNS resolution with `ping` or `nslookup`.<br>• Ensure the site is publicly accessible (not behind VPN/firewall). |
|
|
573
573
|
| 14 | systemError | Something went wrong when verifying the URL. Please try again in a few minutes. If this issue persists, please contact the Oobee team. | • Retry after a few minutes.<br>• Check internet connection.<br>• If persistent, report as a system issue. |
|
|
574
|
-
| 15 | notASitemap | Invalid sitemap URL format. Please enter a valid sitemap URL ending with .XML e.g. https://www.example.com/sitemap.xml. | • Ensure the URL points to a valid XML sitemap.<br>• View [Examples of sitemaps sitemaps.org - Protocol](https://www.sitemaps.org/protocol.html)<br>• Test the URL in a browser to confirm it returns XML. |
|
|
574
|
+
| 15 | notASitemap | Invalid sitemap URL format. Please enter a valid sitemap URL ending with .XML or .TXT e.g. https://www.example.com/sitemap.xml. | • Ensure the URL points to a valid XML sitemap.<br>• View [Examples of sitemaps sitemaps.org - Protocol](https://www.sitemaps.org/protocol.html)<br>• Test the URL in a browser to confirm it returns XML. |
|
|
575
575
|
| 16 | unauthorised | Login required. Please enter your credentials and try again. | • Check if the site requires username/password.<br>• Provide credentials in Oobee if supported. |
|
|
576
576
|
| 17 | browserError | Incompatible browser. Please ensure you are using Chrome or Edge browser. | • Install the latest version of Chrome or Edge.|
|
|
577
577
|
| 18 | sslProtocolError | SSL certificate error. Please check the SSL configuration of your website and try again. | • Verify SSL certificate validity (not expired, issued by trusted CA).<br>• Check for mismatched TLS versions or cipher issues.<br>• Use an SSL checker tool (e.g., Qualys SSL Labs). |
|
|
@@ -666,3 +666,15 @@ We recommend looking at our **Technology Stack** to understand the usage of each
|
|
|
666
666
|
Oobee uses third-party open-source tools that may be downloaded over the Internet during the installation process of Oobee. Users should be aware of the libraries used by examining `package.json`.
|
|
667
667
|
|
|
668
668
|
Oobee may send information to the website or URL where the user chooses to initiate a Oobee scan. Limited user information such as e-mail address, name, and basic analytics is collected for the purpose of knowing our usage patterns better.
|
|
669
|
+
|
|
670
|
+
## Development and Testing Helpers
|
|
671
|
+
|
|
672
|
+
Useful helpers for Oobee developers
|
|
673
|
+
### Generate new HTML
|
|
674
|
+
When working on EJS (HTML, CSS, JS), use the following helper script to test your changes so that you do not need to run a full scan to get the new report in results directory.
|
|
675
|
+
|
|
676
|
+
It uses the existing report *.json files for the embedded HTML dataset.
|
|
677
|
+
|
|
678
|
+
```
|
|
679
|
+
npx tsx dev/runGenerateJustHtmlReport.ts results/<report directory>
|
|
680
|
+
```
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# S3 Upload Integration
|
|
2
|
+
|
|
3
|
+
This document explains how to use the S3 upload feature in the Oobee engine.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Oobee engine can now automatically upload scan results to Amazon S3 after completing a scan. This enables integration with cloud-based workflows and event-driven architectures:
|
|
8
|
+
|
|
9
|
+
1. Oobee runs and generates scan results locally
|
|
10
|
+
2. Results are automatically uploaded to S3 with metadata
|
|
11
|
+
3. S3 events can trigger downstream processing (e.g., serverless functions)
|
|
12
|
+
4. External systems can process results and update databases, send notifications, etc.
|
|
13
|
+
|
|
14
|
+
## Environment Variables
|
|
15
|
+
|
|
16
|
+
To enable S3 upload, set the following environment variables before running Oobee:
|
|
17
|
+
|
|
18
|
+
### Required Variables
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# AWS Configuration
|
|
22
|
+
export AWS_REGION="ap-southeast-1" # AWS region
|
|
23
|
+
export AWS_ACCESS_KEY_ID="your-access-key" # AWS credentials
|
|
24
|
+
export AWS_SECRET_ACCESS_KEY="your-secret-key" # AWS credentials
|
|
25
|
+
export S3_BUCKET_NAME="oobee-scan-results" # S3 bucket name
|
|
26
|
+
|
|
27
|
+
# Scan Metadata (provided by your orchestration system)
|
|
28
|
+
export OOBEE_SCAN_ID="uuid-scan-id" # Unique scan identifier
|
|
29
|
+
export OOBEE_USER_ID="uuid-user-id" # User identifier
|
|
30
|
+
export OOBEE_USER_EMAIL="user@example.com" # User email
|
|
31
|
+
|
|
32
|
+
# Optional Variables
|
|
33
|
+
export CLOUDFRONT_BASE_URL="https://cdn.example.com" # CloudFront distribution URL
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### S3 Upload Path
|
|
37
|
+
|
|
38
|
+
Files will be uploaded to: `s3://{bucket}/users/{userId}/scans/{scanId}/`
|
|
39
|
+
|
|
40
|
+
Example: `s3://oobee-scan-results/users/550e8400-e29b-41d4-a716-446655440000/scans/650e8400-e29b-41d4-a716-446655440001/`
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### 1. Automated Workflow (Containerized/Orchestrated)
|
|
45
|
+
|
|
46
|
+
When running Oobee in a containerized or orchestrated environment, pass the required environment variables to the container:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Docker example
|
|
50
|
+
docker run -e OOBEE_SCAN_ID="650e8400-e29b-41d4-a716-446655440001" \
|
|
51
|
+
-e OOBEE_USER_ID="550e8400-e29b-41d4-a716-446655440000" \
|
|
52
|
+
-e OOBEE_USER_EMAIL="user@example.com" \
|
|
53
|
+
-e S3_BUCKET_NAME="oobee-scan-results" \
|
|
54
|
+
-e AWS_REGION="ap-southeast-1" \
|
|
55
|
+
oobee-engine:latest
|
|
56
|
+
|
|
57
|
+
# Kubernetes example
|
|
58
|
+
env:
|
|
59
|
+
- name: OOBEE_SCAN_ID
|
|
60
|
+
value: "650e8400-e29b-41d4-a716-446655440001"
|
|
61
|
+
- name: OOBEE_USER_ID
|
|
62
|
+
value: "550e8400-e29b-41d4-a716-446655440000"
|
|
63
|
+
- name: OOBEE_USER_EMAIL
|
|
64
|
+
value: "user@example.com"
|
|
65
|
+
- name: S3_BUCKET_NAME
|
|
66
|
+
value: "oobee-scan-results"
|
|
67
|
+
- name: AWS_REGION
|
|
68
|
+
value: "ap-southeast-1"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 2. From CLI (Manual Testing)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Set environment variables
|
|
75
|
+
export AWS_REGION="ap-southeast-1"
|
|
76
|
+
export AWS_ACCESS_KEY_ID="your-key"
|
|
77
|
+
export AWS_SECRET_ACCESS_KEY="your-secret"
|
|
78
|
+
### 3. Without S3 Upload (Default Behavior)
|
|
79
|
+
|
|
80
|
+
If environment variables are not set, Oobee works as before:
|
|
81
|
+
- Results are saved locally only
|
|
82
|
+
- No S3 upload occurs
|
|
83
|
+
- No errors (logs informational message)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Run normally without S3 variables
|
|
87
|
+
npm run cli -- -c website -u https://example.com
|
|
88
|
+
```
|
|
89
|
+
If environment variables are not set, Oobee will work as before:
|
|
90
|
+
- Results are saved locally
|
|
91
|
+
- No S3 upload occurs
|
|
92
|
+
- No errors or warnings (except info log)
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Just run normally without S3 variables
|
|
96
|
+
npm run cli -- -c website -u https://example.com
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Implementation Details
|
|
100
|
+
|
|
101
|
+
### Files Uploaded
|
|
102
|
+
|
|
103
|
+
The S3 uploader uploads all files from the results directory, but only tracks these file types in the response:
|
|
104
|
+
- `.html` - HTML reports
|
|
105
|
+
- `.csv` - CSV data files
|
|
106
|
+
- `.pdf` - PDF reports
|
|
107
|
+
- `.zip` - Zipped scan results
|
|
108
|
+
|
|
109
|
+
All files are uploaded with S3 metadata containing:
|
|
110
|
+
- `scanid` - Scan ID
|
|
111
|
+
- `userid` - User ID
|
|
112
|
+
- `useremail` - User email
|
|
113
|
+
|
|
114
|
+
### S3 Metadata
|
|
115
|
+
|
|
116
|
+
Each uploaded file includes metadata that the S3 Lambda processor uses:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
{
|
|
120
|
+
"scanid": "650e8400-e29b-41d4-a716-446655440001",
|
|
121
|
+
"userid": "550e8400-e29b-41d4-a716-446655440000",
|
|
122
|
+
"useremail": "user@example.com",
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Error Handling
|
|
127
|
+
|
|
128
|
+
- If S3 upload fails, the scan continues normally
|
|
129
|
+
- Error is logged but doesn't fail the scan
|
|
130
|
+
- Results are still available locally
|
|
131
|
+
- User can retry upload manually if needed
|
|
132
|
+
|
|
133
|
+
### Key Functions
|
|
134
|
+
|
|
135
|
+
#### `isS3UploadEnabled()`
|
|
136
|
+
Checks if all required environment variables are set.
|
|
137
|
+
|
|
138
|
+
#### `getS3MetadataFromEnv()`
|
|
139
|
+
Extracts scan metadata from environment variables.
|
|
140
|
+
|
|
141
|
+
#### `getS3UploadPrefix()`
|
|
142
|
+
Generates the S3 path: `users/{userId}/scans/{scanId}`
|
|
143
|
+
|
|
144
|
+
#### `uploadFolderToS3(localPath, s3Prefix, metadata)`
|
|
145
|
+
Uploads entire results folder to S3 with metadata.
|
|
146
|
+
|
|
147
|
+
## Testing
|
|
148
|
+
|
|
149
|
+
### Local Testing
|
|
150
|
+
|
|
151
|
+
1. Create a test S3 bucket:
|
|
152
|
+
```bash
|
|
153
|
+
aws s3 mb s3://oobee-test-scan-results
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
2. Set test environment variables:
|
|
157
|
+
```bash
|
|
158
|
+
export S3_BUCKET_NAME="oobee-test-scan-results"
|
|
159
|
+
export OOBEE_SCAN_ID="test-$(date +%s)"
|
|
160
|
+
export OOBEE_USER_ID="test-user-123"
|
|
161
|
+
export OOBEE_USER_EMAIL="test@example.com"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
3. Run a test scan:
|
|
165
|
+
```bash
|
|
166
|
+
npm run cli -- -c website -u https://example.com -p 5
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
4. Verify upload:
|
|
170
|
+
```bash
|
|
171
|
+
aws s3 ls s3://oobee-test-scan-results/users/test-user-123/scans/
|
|
172
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
For developemnt and testing purposes only. Run the report generator
|
|
3
|
+
on an existing results directory.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { generateHtmlReport } from '../src/generateHtmlReport.js';
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
const dirArg = process.argv[2];
|
|
11
|
+
|
|
12
|
+
if (!dirArg) {
|
|
13
|
+
console.error('Usage: npx tsx src/dev-generate-from-existing.ts <results-dir>');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const resultDir = path.resolve(process.cwd(), dirArg);
|
|
18
|
+
const out = await generateHtmlReport(resultDir);
|
|
19
|
+
console.log('\nOpen:', out);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
main().catch((e) => {
|
|
23
|
+
console.error(e);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@govtechsg/oobee",
|
|
3
3
|
"main": "dist/npmIndex.js",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.72",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Government Technology Agency <info@tech.gov.sg>",
|
|
7
7
|
"dependencies": {
|
|
8
|
+
"@aws-sdk/client-s3": "^3.948.0",
|
|
8
9
|
"@json2csv/node": "^7.0.3",
|
|
9
10
|
"@napi-rs/canvas": "^0.1.53",
|
|
10
11
|
"@sentry/node": "^9.13.0",
|
|
12
|
+
"@types/aws-sdk": "^0.0.42",
|
|
11
13
|
"axe-core": "^4.10.3",
|
|
12
14
|
"axios": "^1.8.2",
|
|
13
15
|
"base64-stream": "^1.0.0",
|
|
@@ -16,7 +18,7 @@
|
|
|
16
18
|
"ejs": "^3.1.9",
|
|
17
19
|
"file-type": "^19.5.0",
|
|
18
20
|
"fs-extra": "^11.2.0",
|
|
19
|
-
"glob": "^
|
|
21
|
+
"glob": "^11.1.0",
|
|
20
22
|
"https": "^1.0.0",
|
|
21
23
|
"inquirer": "^9.2.12",
|
|
22
24
|
"jsdom": "^21.1.2",
|
package/src/combine.ts
CHANGED
|
@@ -5,13 +5,19 @@ import crawlDomain from './crawlers/crawlDomain.js';
|
|
|
5
5
|
import crawlLocalFile from './crawlers/crawlLocalFile.js';
|
|
6
6
|
import crawlIntelligentSitemap from './crawlers/crawlIntelligentSitemap.js';
|
|
7
7
|
import generateArtifacts from './mergeAxeResults.js';
|
|
8
|
-
import { getHost, createAndUpdateResultsFolders, cleanUpAndExit } from './utils.js';
|
|
8
|
+
import { getHost, createAndUpdateResultsFolders, cleanUpAndExit, getStoragePath } from './utils.js';
|
|
9
9
|
import { ScannerTypes, UrlsCrawled } from './constants/constants.js';
|
|
10
10
|
import { getBlackListedPatterns, submitForm } from './constants/common.js';
|
|
11
11
|
import { consoleLogger, silentLogger } from './logs.js';
|
|
12
12
|
import runCustom from './crawlers/runCustom.js';
|
|
13
13
|
import { alertMessageOptions } from './constants/cliFunctions.js';
|
|
14
14
|
import { Data } from './index.js';
|
|
15
|
+
import {
|
|
16
|
+
isS3UploadEnabled,
|
|
17
|
+
getS3MetadataFromEnv,
|
|
18
|
+
getS3UploadPrefix,
|
|
19
|
+
uploadFolderToS3,
|
|
20
|
+
} from './services/s3Uploader.js';
|
|
15
21
|
|
|
16
22
|
// Class exports
|
|
17
23
|
export class ViewportSettingsClass {
|
|
@@ -61,18 +67,18 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
61
67
|
zip,
|
|
62
68
|
ruleset, // Enable custom checks, Enable WCAG AAA: if checked, = 'enable-wcag-aaa')
|
|
63
69
|
generateJsonFiles,
|
|
64
|
-
scanDuration
|
|
70
|
+
scanDuration,
|
|
65
71
|
} = envDetails;
|
|
66
72
|
|
|
67
73
|
process.env.CRAWLEE_LOG_LEVEL = 'ERROR';
|
|
68
74
|
process.env.CRAWLEE_STORAGE_DIR = randomToken;
|
|
69
75
|
|
|
70
76
|
if (process.env.CRAWLEE_SYSTEM_INFO_V2 === undefined) {
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
// Set the environment variable to enable system info v2
|
|
78
|
+
// Resolves issue with when wmic is not installed on Windows
|
|
73
79
|
process.env.CRAWLEE_SYSTEM_INFO_V2 = '1';
|
|
74
80
|
}
|
|
75
|
-
|
|
81
|
+
|
|
76
82
|
const host = type === ScannerTypes.SITEMAP || type === ScannerTypes.LOCALFILE ? '' : getHost(url);
|
|
77
83
|
|
|
78
84
|
let blacklistedPatterns: string[] | null = null;
|
|
@@ -121,19 +127,26 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
121
127
|
);
|
|
122
128
|
|
|
123
129
|
let urlsCrawledObj: UrlsCrawled;
|
|
130
|
+
let uiCustomFlowLabel: string | undefined;
|
|
131
|
+
let durationExceeded = false;
|
|
132
|
+
|
|
124
133
|
switch (type) {
|
|
125
134
|
case ScannerTypes.CUSTOM:
|
|
126
|
-
|
|
135
|
+
const res = await runCustom(
|
|
127
136
|
url,
|
|
128
137
|
randomToken,
|
|
129
138
|
viewportSettings,
|
|
130
139
|
blacklistedPatterns,
|
|
131
140
|
includeScreenshots,
|
|
141
|
+
customFlowLabel && customFlowLabel !== 'None' ? customFlowLabel : '',
|
|
132
142
|
);
|
|
143
|
+
|
|
144
|
+
urlsCrawledObj = res.urlsCrawled;
|
|
145
|
+
uiCustomFlowLabel = res.customFlowLabel;
|
|
133
146
|
break;
|
|
134
147
|
|
|
135
148
|
case ScannerTypes.SITEMAP:
|
|
136
|
-
|
|
149
|
+
const sitemapResult = await crawlSitemap({
|
|
137
150
|
sitemapUrl: url,
|
|
138
151
|
randomToken,
|
|
139
152
|
host,
|
|
@@ -148,10 +161,12 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
148
161
|
extraHTTPHeaders,
|
|
149
162
|
scanDuration,
|
|
150
163
|
});
|
|
164
|
+
urlsCrawledObj = sitemapResult.urlsCrawled;
|
|
165
|
+
durationExceeded = sitemapResult.durationExceeded;
|
|
151
166
|
break;
|
|
152
167
|
|
|
153
168
|
case ScannerTypes.LOCALFILE:
|
|
154
|
-
|
|
169
|
+
const localFileResult = await crawlLocalFile({
|
|
155
170
|
url,
|
|
156
171
|
randomToken,
|
|
157
172
|
host,
|
|
@@ -166,10 +181,18 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
166
181
|
extraHTTPHeaders,
|
|
167
182
|
scanDuration,
|
|
168
183
|
});
|
|
184
|
+
if (localFileResult) {
|
|
185
|
+
if ('urlsCrawled' in localFileResult) {
|
|
186
|
+
urlsCrawledObj = localFileResult.urlsCrawled;
|
|
187
|
+
durationExceeded = localFileResult.durationExceeded;
|
|
188
|
+
} else {
|
|
189
|
+
urlsCrawledObj = localFileResult;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
169
192
|
break;
|
|
170
193
|
|
|
171
194
|
case ScannerTypes.INTELLIGENT:
|
|
172
|
-
|
|
195
|
+
const intelligentResult = await crawlIntelligentSitemap(
|
|
173
196
|
url,
|
|
174
197
|
randomToken,
|
|
175
198
|
host,
|
|
@@ -185,12 +208,14 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
185
208
|
followRobots,
|
|
186
209
|
extraHTTPHeaders,
|
|
187
210
|
safeMode,
|
|
188
|
-
scanDuration
|
|
211
|
+
scanDuration,
|
|
189
212
|
);
|
|
213
|
+
urlsCrawledObj = intelligentResult.urlsCrawled;
|
|
214
|
+
durationExceeded = intelligentResult.durationExceeded;
|
|
190
215
|
break;
|
|
191
216
|
|
|
192
217
|
case ScannerTypes.WEBSITE:
|
|
193
|
-
|
|
218
|
+
const websiteResult = await crawlDomain({
|
|
194
219
|
url,
|
|
195
220
|
randomToken,
|
|
196
221
|
host,
|
|
@@ -209,6 +234,8 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
209
234
|
safeMode,
|
|
210
235
|
ruleset,
|
|
211
236
|
});
|
|
237
|
+
urlsCrawledObj = websiteResult.urlsCrawled;
|
|
238
|
+
durationExceeded = websiteResult.durationExceeded;
|
|
212
239
|
break;
|
|
213
240
|
|
|
214
241
|
default:
|
|
@@ -235,7 +262,9 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
235
262
|
deviceToScan,
|
|
236
263
|
urlsCrawledObj.scanned,
|
|
237
264
|
pagesNotScanned,
|
|
238
|
-
|
|
265
|
+
uiCustomFlowLabel && uiCustomFlowLabel.length > 0
|
|
266
|
+
? uiCustomFlowLabel
|
|
267
|
+
: customFlowLabel || 'None',
|
|
239
268
|
undefined,
|
|
240
269
|
scanDetails,
|
|
241
270
|
zip,
|
|
@@ -243,6 +272,36 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
243
272
|
);
|
|
244
273
|
const [name, email] = nameEmail.split(':');
|
|
245
274
|
|
|
275
|
+
// Upload results to S3 if environment variables are set
|
|
276
|
+
if (isS3UploadEnabled()) {
|
|
277
|
+
const siteName = (urlsCrawledObj.scanned[0]?.pageTitle ?? '')
|
|
278
|
+
.replace(/^\d+\s*:\s*/, '')
|
|
279
|
+
.trim();
|
|
280
|
+
const scanMetadata = getS3MetadataFromEnv(siteName, durationExceeded);
|
|
281
|
+
const s3Prefix = getS3UploadPrefix();
|
|
282
|
+
|
|
283
|
+
if (scanMetadata && s3Prefix) {
|
|
284
|
+
try {
|
|
285
|
+
const storagePath = getStoragePath(randomToken);
|
|
286
|
+
consoleLogger.info('Starting S3 upload...');
|
|
287
|
+
consoleLogger.info(`Upload path: ${s3Prefix}`);
|
|
288
|
+
|
|
289
|
+
const uploadedFiles = await uploadFolderToS3(storagePath, s3Prefix, scanMetadata);
|
|
290
|
+
|
|
291
|
+
consoleLogger.info(`Successfully uploaded ${uploadedFiles.length} files to S3`);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
294
|
+
consoleLogger.error(`Failed to upload results to S3: ${errorMessage}`);
|
|
295
|
+
// Don't fail the scan if S3 upload fails
|
|
296
|
+
consoleLogger.warn('Continuing without S3 upload...');
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
consoleLogger.warn('S3 upload enabled but metadata/prefix not available');
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
consoleLogger.info('S3 upload not enabled (missing environment variables)');
|
|
303
|
+
}
|
|
304
|
+
|
|
246
305
|
await submitForm(
|
|
247
306
|
browser,
|
|
248
307
|
userDataDirectory,
|
|
@@ -258,13 +317,11 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
|
258
317
|
metadata,
|
|
259
318
|
);
|
|
260
319
|
} else {
|
|
261
|
-
|
|
262
320
|
// No page were scanned because the URL loaded does not meet the crawler requirements
|
|
263
321
|
printMessage([`No pages were scanned.`], alertMessageOptions);
|
|
264
322
|
cleanUpAndExit(1, randomToken, true);
|
|
265
323
|
}
|
|
266
324
|
} else {
|
|
267
|
-
|
|
268
325
|
// No page were scanned because the URL loaded does not meet the crawler requirements
|
|
269
326
|
printMessage([`No pages were scanned.`], alertMessageOptions);
|
|
270
327
|
cleanUpAndExit(1, randomToken, true);
|