diff --git a/src/components/Results/Hsts.tsx b/src/components/Results/Hsts.tsx new file mode 100644 index 0000000..3b8ec45 --- /dev/null +++ b/src/components/Results/Hsts.tsx @@ -0,0 +1,42 @@ + +import { Card } from 'components/Form/Card'; +import Row, { ExpandableRow, RowProps } from 'components/Form/Row'; + +const cardStyles = ''; + +const parseHeader = (headerString: string): RowProps[] => { + return headerString.split(';').map((part) => { + const trimmedPart = part.trim(); + const equalsIndex = trimmedPart.indexOf('='); + + if (equalsIndex >= 0) { + return { + lbl: trimmedPart.substring(0, equalsIndex).trim(), + val: trimmedPart.substring(equalsIndex + 1).trim(), + }; + } else { + return { lbl: trimmedPart, val: 'true' }; + } + }); +}; + +const HstsCard = (props: {data: any, title: string, actionButtons: any }): JSX.Element => { + const hstsResults = props.data; + const hstsHeaders = hstsResults?.hstsHeader ? parseHeader(hstsResults.hstsHeader) : []; + return ( + + {typeof hstsResults.compatible === 'boolean' && ( + + )} + {hstsHeaders.length > 0 && hstsHeaders.map((header: RowProps, index: number) => { + return ( + + ); + }) + } + {hstsResults.message && (

{hstsResults.message}

)} +
+ ); +} + +export default HstsCard; diff --git a/src/pages/Results.tsx b/src/pages/Results.tsx index 1c27219..e7184d2 100644 --- a/src/pages/Results.tsx +++ b/src/pages/Results.tsx @@ -33,6 +33,7 @@ import TraceRouteCard from 'components/Results/TraceRoute'; import CarbonFootprintCard from 'components/Results/CarbonFootprint'; import SiteFeaturesCard from 'components/Results/SiteFeatures'; import DnsSecCard from 'components/Results/DnsSec'; +import HstsCard from 'components/Results/Hsts'; import SelfScanMsg from 'components/misc/SelfScanMsg'; import ProgressBar, { LoadingJob, LoadingState, initialJobs } from 'components/misc/ProgressBar'; @@ -280,7 +281,7 @@ const Results = (): JSX.Element => { }); // Fetch and parse domain whois results - const [whoIsResults, updateWhoIsResults] = useMotherHook({ + const [whoIsResults, updateWhoIsResults] = useMotherHook({ jobId: 'whois', updateLoadingJobs, addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly }, @@ -339,6 +340,14 @@ const Results = (): JSX.Element => { fetchRequest: () => fetch(`/get-carbon?url=${address}`).then(res => parseJson(res)), }); + // Check if a site is on the HSTS preload list + const [hstsResults, updateHstsResults] = useMotherHook({ + jobId: 'hsts', + updateLoadingJobs, + addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly }, + fetchRequest: () => fetch(`/check-hsts?url=${address}`).then(res => parseJson(res)), + }); + // Get site features from BuiltWith const [siteFeaturesResults, updateSiteFeaturesResults] = useMotherHook({ jobId: 'features', @@ -404,6 +413,7 @@ const Results = (): JSX.Element => { { id: 'ports', title: 'Open Ports', result: portsResults, Component: OpenPortsCard, refresh: updatePortsResults }, { id: 'screenshot', title: 'Screenshot', result: lighthouseResults?.fullPageScreenshot?.screenshot, Component: ScreenshotCard, refresh: updateLighthouseResults }, { id: 'txt-records', title: 'TXT Records', result: txtRecordResults, Component: TxtRecordCard, refresh: updateTxtRecordResults }, + { id: 'hsts', title: 'HSTS Check', result: hstsResults, Component: HstsCard, refresh: updateHstsResults }, { id: 'features', title: 'Site Features', result: siteFeaturesResults, Component: SiteFeaturesCard, refresh: updateSiteFeaturesResults }, { id: 'carbon', title: 'Carbon Footprint', result: carbonResults, Component: CarbonFootprintCard, refresh: updateCarbonResults }, ];