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 },
];