mirror of
https://github.com/Lissy93/web-check.git
synced 2025-02-17 02:40:46 +01:00
Builds result cards for TLS results
This commit is contained in:
parent
49cfad2dbe
commit
823873bce2
68
src/components/Results/TlsCipherSuites.tsx
Normal file
68
src/components/Results/TlsCipherSuites.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import colors from 'styles/colors';
|
||||||
|
import { Card } from 'components/Form/Card';
|
||||||
|
import Button from 'components/Form/Button';
|
||||||
|
import Row, { ExpandableRow } from 'components/Form/Row';
|
||||||
|
|
||||||
|
const makeCipherSuites = (results: any) => {
|
||||||
|
if (!results || !results.connection_info || (results.connection_info.ciphersuite || [])?.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return results.connection_info.ciphersuite.map((ciphersuite: any) => {
|
||||||
|
return {
|
||||||
|
title: ciphersuite.cipher,
|
||||||
|
fields: [
|
||||||
|
{ lbl: 'Code', val: ciphersuite.code },
|
||||||
|
{ lbl: 'Protocols', val: ciphersuite.protocols.join(', ') },
|
||||||
|
{ lbl: 'Pubkey', val: ciphersuite.pubkey },
|
||||||
|
{ lbl: 'Sigalg', val: ciphersuite.sigalg },
|
||||||
|
{ lbl: 'Ticket Hint', val: ciphersuite.ticket_hint },
|
||||||
|
{ lbl: 'OCSP Stapling', val: ciphersuite.ocsp_stapling ? '✅ Enabled' : '❌ Disabled' },
|
||||||
|
{ lbl: 'PFS', val: ciphersuite.pfs },
|
||||||
|
ciphersuite.curves ? { lbl: 'Curves', val: ciphersuite.curves.join(', ') } : {},
|
||||||
|
]};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const TlsCard = (props: {data: any, title: string, actionButtons: any }): JSX.Element => {
|
||||||
|
|
||||||
|
const [cipherSuites, setCipherSuites] = useState(makeCipherSuites(props.data));
|
||||||
|
const [loadState, setLoadState] = useState<undefined | 'loading' | 'success' | 'error'>(undefined);
|
||||||
|
|
||||||
|
const updateData = (id: number) => {
|
||||||
|
setCipherSuites([]);
|
||||||
|
setLoadState('loading');
|
||||||
|
const fetchUrl = `https://tls-observatory.services.mozilla.com/api/v1/results?id=${id}`;
|
||||||
|
fetch(fetchUrl)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
setCipherSuites(makeCipherSuites(data));
|
||||||
|
setLoadState('success');
|
||||||
|
}).catch((error) => {
|
||||||
|
setLoadState('error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const scanId = props.data?.id;
|
||||||
|
return (
|
||||||
|
<Card heading={props.title} actionButtons={props.actionButtons}>
|
||||||
|
{ cipherSuites.length && cipherSuites.map((cipherSuite: any, index: number) => {
|
||||||
|
return (
|
||||||
|
<ExpandableRow lbl={cipherSuite.title} val="" rowList={cipherSuite.fields} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{ !cipherSuites.length && (
|
||||||
|
<div>
|
||||||
|
<p>No cipher suites found.<br />
|
||||||
|
This sometimes happens when the report didn't finish generating in-time, you can try re-requesting it.
|
||||||
|
</p>
|
||||||
|
<Button loadState={loadState} onClick={() => updateData(scanId)}>Refetch Report</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TlsCard;
|
87
src/components/Results/TlsClientSupport.tsx
Normal file
87
src/components/Results/TlsClientSupport.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import colors from 'styles/colors';
|
||||||
|
import { Card } from 'components/Form/Card';
|
||||||
|
import Button from 'components/Form/Button';
|
||||||
|
import Row, { ExpandableRow } from 'components/Form/Row';
|
||||||
|
|
||||||
|
|
||||||
|
// "name": "Android",
|
||||||
|
// "curve": "secp256r1",
|
||||||
|
// "version": "6.0",
|
||||||
|
// "platform": "",
|
||||||
|
// "protocol": "TLSv1.2",
|
||||||
|
// "curve_code": 23,
|
||||||
|
// "ciphersuite": "ECDHE-RSA-CHACHA20-POLY1305-OLD",
|
||||||
|
// "is_supported": true,
|
||||||
|
// "protocol_code": 771,
|
||||||
|
// "ciphersuite_code": 52243
|
||||||
|
|
||||||
|
const makeClientSupport = (results: any) => {
|
||||||
|
if (!results?.analysis) return [];
|
||||||
|
const target = results.target;
|
||||||
|
const sslLabsClientSupport = (
|
||||||
|
results.analysis.find((a: any) => a.analyzer === 'sslLabsClientSupport')
|
||||||
|
).result;
|
||||||
|
|
||||||
|
return sslLabsClientSupport.map((sup: any) => {
|
||||||
|
return {
|
||||||
|
title: `${sup.name} ${sup.platform ? `(on ${sup.platform})`: sup.version}`,
|
||||||
|
value: sup.is_supported ? '✅' : '❌',
|
||||||
|
fields: sup.is_supported ? [
|
||||||
|
sup.curve ? { lbl: 'Curve', val: sup.curve } : {},
|
||||||
|
{ lbl: 'Protocol', val: sup.protocol },
|
||||||
|
{ lbl: 'Cipher Suite', val: sup.ciphersuite },
|
||||||
|
{ lbl: 'Protocol Code', val: sup.protocol_code },
|
||||||
|
{ lbl: 'Cipher Suite Code', val: sup.ciphersuite_code },
|
||||||
|
{ lbl: 'Curve Code', val: sup.curve_code },
|
||||||
|
] : [
|
||||||
|
{ lbl: '', val: '',
|
||||||
|
plaintext: `The host ${target} does not support ${sup.name} `
|
||||||
|
+`${sup.version ? `version ${sup.version} `: ''} `
|
||||||
|
+ `${sup.platform ? `on ${sup.platform} `: ''}`}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const TlsCard = (props: {data: any, title: string, actionButtons: any }): JSX.Element => {
|
||||||
|
|
||||||
|
const [clientSupport, setClientSupport] = useState(makeClientSupport(props.data));
|
||||||
|
const [loadState, setLoadState] = useState<undefined | 'loading' | 'success' | 'error'>(undefined);
|
||||||
|
|
||||||
|
const updateData = (id: number) => {
|
||||||
|
setClientSupport([]);
|
||||||
|
setLoadState('loading');
|
||||||
|
const fetchUrl = `https://tls-observatory.services.mozilla.com/api/v1/results?id=${id}`;
|
||||||
|
fetch(fetchUrl)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
setClientSupport(makeClientSupport(data));
|
||||||
|
setLoadState('success');
|
||||||
|
}).catch(() => {
|
||||||
|
setLoadState('error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const scanId = props.data?.id;
|
||||||
|
return (
|
||||||
|
<Card heading={props.title} actionButtons={props.actionButtons}>
|
||||||
|
{clientSupport.map((support: any) => {
|
||||||
|
return (<ExpandableRow lbl={support.title} val={support.value || '?'} rowList={support.fields} />)
|
||||||
|
})}
|
||||||
|
{ !clientSupport.length && (
|
||||||
|
<div>
|
||||||
|
<p>No entries available to analyze.<br />
|
||||||
|
This sometimes happens when the report didn't finish generating in-time, you can try re-requesting it.
|
||||||
|
</p>
|
||||||
|
<Button loadState={loadState} onClick={() => updateData(scanId)}>Refetch Report</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TlsCard;
|
120
src/components/Results/TlsIssueAnalysis.tsx
Normal file
120
src/components/Results/TlsIssueAnalysis.tsx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import colors from 'styles/colors';
|
||||||
|
import { Card } from 'components/Form/Card';
|
||||||
|
import Button from 'components/Form/Button';
|
||||||
|
import Row, { ExpandableRow } from 'components/Form/Row';
|
||||||
|
|
||||||
|
const Expandable = styled.details`
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
summary::marker {
|
||||||
|
color: ${colors.primary};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const makeExpandableData = (results: any) => {
|
||||||
|
if (!results || !results.analysis || results.analysis.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return results.analysis.map((analysis: any) => {
|
||||||
|
const fields = Object.keys(analysis.result).map((label) => {
|
||||||
|
const lbl = isNaN(parseInt(label, 10)) ? label : '';
|
||||||
|
const val = analysis.result[label] || 'None';
|
||||||
|
if (typeof val !== 'object') {
|
||||||
|
return { lbl, val };
|
||||||
|
}
|
||||||
|
return { lbl, val: '', plaintext: JSON.stringify(analysis.result[label])};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
title: analysis.analyzer,
|
||||||
|
value: analysis.success ? '✅' : '❌',
|
||||||
|
fields,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeResults = (results: any) => {
|
||||||
|
const rows: { lbl: string; val?: any; plaintext?: string; list?: string[] }[] = [];
|
||||||
|
if (!results || !results.analysis || results.analysis.length === 0) {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
const caaWorker = results.analysis.find((a: any) => a.analyzer === 'caaWorker');
|
||||||
|
if (caaWorker.result.host) rows.push({ lbl: 'Host', val: caaWorker.result.host });
|
||||||
|
if (typeof caaWorker.result.has_caa === 'boolean') rows.push({ lbl: 'CA Authorization', val: caaWorker.result.has_caa });
|
||||||
|
if (caaWorker.result.issue) rows.push({ lbl: 'CAAs allowed to Issue Certs', plaintext: caaWorker.result.issue.join('\n') });
|
||||||
|
|
||||||
|
const mozillaGradingWorker = (results.analysis.find((a: any) => a.analyzer === 'mozillaGradingWorker')).result;
|
||||||
|
if (mozillaGradingWorker.grade) rows.push({ lbl: 'Mozilla Grading', val: mozillaGradingWorker.grade });
|
||||||
|
if (mozillaGradingWorker.gradeTrust) rows.push({ lbl: 'Mozilla Trust', val: mozillaGradingWorker.gradeTrust });
|
||||||
|
|
||||||
|
const symantecDistrust = (results.analysis.find((a: any) => a.analyzer === 'symantecDistrust')).result;
|
||||||
|
if (typeof symantecDistrust.isDistrusted === 'boolean') rows.push({ lbl: 'No distrusted symantec SSL?', val: !symantecDistrust.isDistrusted });
|
||||||
|
if (symantecDistrust.reasons) rows.push({ lbl: 'Symantec Distrust', plaintext: symantecDistrust.reasons.join('\n') });
|
||||||
|
|
||||||
|
const top1m = (results.analysis.find((a: any) => a.analyzer === 'top1m')).result;
|
||||||
|
if (top1m.certificate.rank) rows.push({ lbl: 'Certificate Rank', val: top1m.certificate.rank.toLocaleString() });
|
||||||
|
|
||||||
|
const mozillaEvaluationWorker = (results.analysis.find((a: any) => a.analyzer === 'mozillaEvaluationWorker')).result;
|
||||||
|
if (mozillaEvaluationWorker.level) rows.push({ lbl: 'Mozilla Evaluation Level', val: mozillaEvaluationWorker.level });
|
||||||
|
if (mozillaEvaluationWorker.failures) {
|
||||||
|
const { bad, old, intermediate, modern } = mozillaEvaluationWorker.failures;
|
||||||
|
if (bad) rows.push({ lbl: `Critical Security Issues (${bad.length})`, list: bad });
|
||||||
|
if (old) rows.push({ lbl: `Compatibility Config Issues (${old.length})`, list: old });
|
||||||
|
if (intermediate) rows.push({ lbl: `Intermediate Issues (${intermediate.length})`, list: intermediate });
|
||||||
|
if (modern) rows.push({ lbl: `Modern Issues (${modern.length})`, list: modern });
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TlsCard = (props: {data: any, title: string, actionButtons: any }): JSX.Element => {
|
||||||
|
|
||||||
|
const [tlsRowData, setTlsRowWata] = useState(makeExpandableData(props.data));
|
||||||
|
const [tlsResults, setTlsResults] = useState(makeResults(props.data));
|
||||||
|
const [loadState, setLoadState] = useState<undefined | 'loading' | 'success' | 'error'>(undefined);
|
||||||
|
|
||||||
|
const updateData = (id: number) => {
|
||||||
|
setTlsRowWata([]);
|
||||||
|
setLoadState('loading');
|
||||||
|
const fetchUrl = `https://tls-observatory.services.mozilla.com/api/v1/results?id=${id}`;
|
||||||
|
fetch(fetchUrl)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
setTlsRowWata(makeExpandableData(data));
|
||||||
|
setTlsResults(makeResults(data));
|
||||||
|
setLoadState('success');
|
||||||
|
}).catch(() => {
|
||||||
|
setLoadState('error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const scanId = props.data?.id;
|
||||||
|
return (
|
||||||
|
<Card heading={props.title} actionButtons={props.actionButtons}>
|
||||||
|
{ tlsResults.length > 0 && tlsResults.map((row: any, index: number) => {
|
||||||
|
return (
|
||||||
|
<Row lbl={row.lbl} val={row.val} plaintext={row.plaintext} listResults={row.list} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<Expandable>
|
||||||
|
<summary>Full Analysis Results</summary>
|
||||||
|
{ tlsRowData.length > 0 && tlsRowData.map((cipherSuite: any, index: number) => {
|
||||||
|
return (
|
||||||
|
<ExpandableRow lbl={cipherSuite.title} val={cipherSuite.value || '?'} rowList={cipherSuite.fields} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Expandable>
|
||||||
|
{ !tlsRowData.length && (
|
||||||
|
<div>
|
||||||
|
<p>No entries available to analyze.<br />
|
||||||
|
This sometimes happens when the report didn't finish generating in-time, you can try re-requesting it.
|
||||||
|
</p>
|
||||||
|
<Button loadState={loadState} onClick={() => updateData(scanId)}>Refetch Report</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TlsCard;
|
Loading…
Reference in New Issue
Block a user