diff --git a/package-lock.json b/package-lock.json index 5627f66a..b4036992 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "purplea11ydesktop", - "version": "0.9.40", + "version": "0.9.41", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "purplea11ydesktop", - "version": "0.9.40", + "version": "0.9.41", "dependencies": { "axios": "^1.6.0", "fs-extra": "^11.1.1", diff --git a/package.json b/package.json index 20244242..977c5e43 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "purplea11ydesktop", "productName": "Purple A11y", - "version": "0.9.40", + "version": "0.9.41", "private": true, "dependencies": { "axios": "^1.6.0", diff --git a/public/electron/scanManager.js b/public/electron/scanManager.js index 6b5fa53f..d51d9a76 100644 --- a/public/electron/scanManager.js +++ b/public/electron/scanManager.js @@ -58,6 +58,7 @@ const getScanOptions = (details) => { falsePositive, includeScreenshots, includeSubdomains, + followRobots, metadata, } = details; const options = ["-c", scanType, "-u", url, "-k", `${name}:${email}`, "-i", fileTypes]; @@ -108,6 +109,10 @@ const getScanOptions = (details) => { options.push("-f", "true"); } + if (followRobots) { + options.push("-r", "yes"); + } + if (metadata) { options.push("-q", metadata); } diff --git a/src/MainWindow/CustomFlow/index.jsx b/src/MainWindow/CustomFlow/index.jsx index af6b56a3..846e837f 100644 --- a/src/MainWindow/CustomFlow/index.jsx +++ b/src/MainWindow/CustomFlow/index.jsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { useLocation, useNavigate } from "react-router"; -import { cliErrorCodes, cliErrorTypes } from "../../common/constants"; +import { cliErrorCodes, cliErrorTypes, errorStates } from "../../common/constants"; import services from "../../services"; import './CustomFlow.scss'; import arrowLeft from "../../assets/arrow-left-purple.svg"; @@ -195,7 +195,7 @@ const CustomFlowPage = ({ completedScanId, setCompletedScanId }) => { return; } - navigate("/error", {state: { isCustomScan: true }}); + navigate("/error", {state: { errorState: errorStates.customScanError }}); return; } diff --git a/src/MainWindow/ErrorPage/ErrorPage.scss b/src/MainWindow/ErrorPage/ErrorPage.scss index 467e81f6..152d6baa 100644 --- a/src/MainWindow/ErrorPage/ErrorPage.scss +++ b/src/MainWindow/ErrorPage/ErrorPage.scss @@ -29,8 +29,9 @@ background-color: transparent; } - .actions { + .btn-container { display: flex; + align-items: center; justify-content: center; } } diff --git a/src/MainWindow/ErrorPage/index.jsx b/src/MainWindow/ErrorPage/index.jsx index fb62c27b..bbb4d834 100644 --- a/src/MainWindow/ErrorPage/index.jsx +++ b/src/MainWindow/ErrorPage/index.jsx @@ -5,21 +5,15 @@ import returnIcon from "../../assets/return-purple.svg"; import { useNavigate, useLocation } from "react-router"; import { ReactComponent as ExclamationCircleIcon } from "../../assets/exclamation-circle.svg"; import { useState, useEffect } from "react"; +import { errorStates } from "../../common/constants"; const ErrorPage = () => { const { state } = useLocation(); const navigate = useNavigate(); - const [isCustomScan, setIsCustomScan] = useState(false); - const [isBrowserError, setIsBrowserError] = useState(false); + const [ errorState, setErrorState ] = useState(''); useEffect(() => { - if (state?.isCustomScan) { - setIsCustomScan(state.isCustomScan); - } - - if (state?.isBrowserError) { - setIsBrowserError(state.isBrowserError); - } + if (state?.errorState) setErrorState(state.errorState); }, []); const replayCustomFlow = async () => { @@ -28,7 +22,7 @@ const ErrorPage = () => { }; const handleBackToHome = () => { - if (isCustomScan) { + if (errorState === errorStates.customScanError) { window.services.cleanUpCustomFlowScripts(); window.localStorage.removeItem("latestCustomFlowGeneratedScript"); window.localStorage.removeItem("latestCustomFlowScanDetails"); @@ -37,35 +31,47 @@ const ErrorPage = () => { return; } + const errorMessageToDisplay = () => { + switch (errorState) { + case errorStates.browserError: + return ( + <> +

Unable to use browser to scan

+

Please close either Google Chrome or Microsoft Edge browser.

+ + ) + case errorStates.noPagesScannedError: + return (<>

No pages were scanned.

) + default: + return (<>

Something went wrong! Please try again.

) + } + + } + return (
} /> - {isBrowserError - ? <> -

Unable to use browser to scan

-

Please close either Google Chrome or Microsoft Edge browser.

- - :

Something went wrong! Please try again.

- } -
- {isCustomScan - ? ( - <> - - - - ) - : } + {errorMessageToDisplay()} +
+ {errorState === errorStates.customScanError + ? ( + <> + + + + ) + : + }
); diff --git a/src/MainWindow/HomePage/AdvancedScanOptions.jsx b/src/MainWindow/HomePage/AdvancedScanOptions.jsx index 581e6862..73e12c36 100644 --- a/src/MainWindow/HomePage/AdvancedScanOptions.jsx +++ b/src/MainWindow/HomePage/AdvancedScanOptions.jsx @@ -226,42 +226,58 @@ const AdvancedScanOptions = ({
{advancedOptions.scanType !== scanTypeOptions[3] && ( -
- handleMaxConcurrencyOnFocus()} - onBlur={() => setShowMaxConcurrencyTooltip(false)} - onMouseEnter={() => handleMaxConcurrencyOnMouseEnter()} - onMouseLeave={() => setIsMaxConcurrencyMouseEvent(false)} - onChange={handleSetAdvancedOption( - "maxConcurrency", - (e) => e.target.checked - )} - /> - - -
- - +
+ setShowMaxConcurrencyTooltip(true)} - onMouseLeave={() => setShowMaxConcurrencyTooltip(false)} - alt="tooltip icon for slow scan mode" + checked={advancedOptions.maxConcurrency} + onFocus={() => handleMaxConcurrencyOnFocus()} + onBlur={() => setShowMaxConcurrencyTooltip(false)} + onMouseEnter={() => handleMaxConcurrencyOnMouseEnter()} + onMouseLeave={() => setIsMaxConcurrencyMouseEvent(false)} + onChange={handleSetAdvancedOption( + "maxConcurrency", + (e) => e.target.checked + )} /> + +
+ + setShowMaxConcurrencyTooltip(true)} + onMouseLeave={() => setShowMaxConcurrencyTooltip(false)} + alt="tooltip icon for slow scan mode" + /> +
-
+
+ e.target.checked + )} + /> + +
+ )}
diff --git a/src/MainWindow/HomePage/HomePage.scss b/src/MainWindow/HomePage/HomePage.scss index ded00aa7..621a98ef 100644 --- a/src/MainWindow/HomePage/HomePage.scss +++ b/src/MainWindow/HomePage/HomePage.scss @@ -164,7 +164,8 @@ #false-positive-toggle-group, #screenshots-toggle-group, - #subdomain-toggle-group { + #subdomain-toggle-group, + #max-concurrency-toggle-group { margin-bottom: 1rem; } diff --git a/src/MainWindow/HomePage/index.jsx b/src/MainWindow/HomePage/index.jsx index bc4860e6..fba128a1 100644 --- a/src/MainWindow/HomePage/index.jsx +++ b/src/MainWindow/HomePage/index.jsx @@ -8,7 +8,7 @@ import labModeOn from "../../assets/lab-icon-on.svg"; import InitScanForm from "./InitScanForm"; import "./HomePage.scss"; import services from "../../services"; -import { cliErrorCodes, cliErrorTypes, versionComparator } from "../../common/constants"; +import { cliErrorCodes, cliErrorTypes, errorStates, versionComparator } from "../../common/constants"; import Modal from "../../common/components/Modal"; import { BasicAuthForm, BasicAuthFormFooter } from "./BasicAuthForm"; import EditUserDetailsModal from "./EditUserDetailsModal"; @@ -195,7 +195,7 @@ const HomePage = ({ isProxy, appVersionInfo, setCompletedScanId }) => { } else { /* When no pages were scanned (e.g. out of domain upon redirects when valid URL was entered), redirects user to error page to going to result page with empty result */ - navigate("/error"); + navigate("/error", { state: { errorState: errorStates.noPagesScannedError }}); return; } } @@ -223,7 +223,7 @@ const HomePage = ({ isProxy, appVersionInfo, setCompletedScanId }) => { errorMessageToShow = "Invalid sitemap."; break; case cliErrorTypes.browserError: - navigate('/error', { state: { isBrowserError: true }}); + navigate('/error', { state: { errorState: errorStates.browserError }}); return; case cliErrorTypes.systemError: default: @@ -250,6 +250,7 @@ const HomePage = ({ isProxy, appVersionInfo, setCompletedScanId }) => { const scanDetails = JSON.parse(window.localStorage.getItem("scanDetails")); const splitUrl = scanDetails.scanUrl.split("://"); scanDetails.scanUrl = `${splitUrl[0]}://${username}:${password}@${splitUrl[1]}`; + setScanButtonIsClicked(true); startScan(scanDetails); setShowBasicAuthModal(false); return; diff --git a/src/common/constants.js b/src/common/constants.js index b8014998..f49b5db3 100644 --- a/src/common/constants.js +++ b/src/common/constants.js @@ -88,7 +88,8 @@ export const getDefaultAdvancedOptions = (isProxy) => { maxConcurrency: false, falsePositive: false, includeScreenshots: true, - includeSubdomains: true + includeSubdomains: true, + followRobots: false, } }; @@ -104,6 +105,12 @@ export const cliErrorTypes = { browserError: 17, }; +export const errorStates = { + browserError: 'browserError', + customScanError: 'customScanError', + noPagesScannedError: 'noPagesScannedError' +} + export const userDataFormDetails = { // production form // formUrl: "https://form.gov.sg/6453387735eb0c00128becdc", diff --git a/src/services.js b/src/services.js index 6aeb8a51..f7870017 100644 --- a/src/services.js +++ b/src/services.js @@ -68,6 +68,7 @@ const startScan = async (scanDetails) => { falsePositive, includeScreenshots, includeSubdomains, + followRobots, scanMetadata, } = scanDetails; @@ -85,6 +86,7 @@ const startScan = async (scanDetails) => { fileTypes: fileTypes[selectedFileTypes], includeScreenshots, includeSubdomains, + followRobots, metadata: JSON.stringify(scanMetadata), };