From 73612bb5baa4f71b57431c174c6d62e2a2e07bde Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Mon, 24 Jul 2023 20:51:46 +0100 Subject: [PATCH] Adds support for fetching, parsing security.txt file (#7) --- src/components/Results/SecurityTxt.tsx | 67 ++++++++++++++++++++++++++ src/components/misc/ProgressBar.tsx | 1 + src/pages/Results.tsx | 11 +++++ src/utils/docs.ts | 22 ++++++++- 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/components/Results/SecurityTxt.tsx diff --git a/src/components/Results/SecurityTxt.tsx b/src/components/Results/SecurityTxt.tsx new file mode 100644 index 0000000..07dd871 --- /dev/null +++ b/src/components/Results/SecurityTxt.tsx @@ -0,0 +1,67 @@ + +import { Card } from 'components/Form/Card'; +import Row, { Details } from 'components/Form/Row'; +import colors from 'styles/colors'; + +const cardStyles = ` +small { + margin-top: 1rem; + opacity: 0.5; + display: block; + a { color: ${colors.primary}; } +} +summary { + padding: 0.5rem 0 0 0.5rem !important; + cursor: pointer; + font-weight: bold; +} +pre { + background: ${colors.background}; + padding: 0.5rem 0.25rem; + border-radius: 4px; + overflow: auto; +} +`; + +const getPagePath = (url: string): string => { + try { + return new URL(url).pathname; + } catch (error) { + return url; + } +} + +const SecurityTxtCard = (props: {data: any, title: string, actionButtons: any }): JSX.Element => { + const securityTxt = props.data; + return ( + + + { securityTxt.isPresent && ( + <> + + + {securityTxt.fields && Object.keys(securityTxt.fields).map((field: string, index: number) => { + if (securityTxt.fields[field].includes('http')) return ( + + {field} + {getPagePath(securityTxt.fields[field])} + + ); + return ( + + ); + })} +
+ View Full Policy +
{securityTxt.content}
+
+ + )} + {!securityTxt.isPresent && ( + Having a security.txt ensures security researchers know how and where to safely report vulnerabilities. + )} +
+ ); +} + +export default SecurityTxtCard; diff --git a/src/components/misc/ProgressBar.tsx b/src/components/misc/ProgressBar.tsx index bad6b87..d5bf0c7 100644 --- a/src/components/misc/ProgressBar.tsx +++ b/src/components/misc/ProgressBar.tsx @@ -201,6 +201,7 @@ const jobNames = [ 'txt-records', 'sitemap', 'hsts', + 'security-txt', // 'whois', 'features', 'carbon', diff --git a/src/pages/Results.tsx b/src/pages/Results.tsx index 9d997e0..ed2a1ee 100644 --- a/src/pages/Results.tsx +++ b/src/pages/Results.tsx @@ -39,8 +39,10 @@ import SitemapCard from 'components/Results/Sitemap'; import DomainLookup from 'components/Results/DomainLookup'; import DnsServerCard from 'components/Results/DnsServer'; import TechStackCard from 'components/Results/TechStack'; +import SecurityTxtCard from 'components/Results/SecurityTxt'; import SelfScanMsg from 'components/misc/SelfScanMsg'; + import ProgressBar, { LoadingJob, LoadingState, initialJobs } from 'components/misc/ProgressBar'; import ActionButtons from 'components/misc/ActionButtons'; import keys from 'utils/get-keys'; @@ -351,6 +353,14 @@ const Results = (): JSX.Element => { fetchRequest: () => fetch(`${api}/screenshot?url=${address}`).then(res => parseJson(res)), }); + // Get a websites listed pages, from sitemap + const [securityTxtResults, updateSecurityTxtResults] = useMotherHook({ + jobId: 'security-txt', + updateLoadingJobs, + addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly }, + fetchRequest: () => fetch(`${api}/security-txt?url=${address}`).then(res => parseJson(res)), + }); + // Get site features from BuiltWith const [siteFeaturesResults, updateSiteFeaturesResults] = useMotherHook({ jobId: 'features', @@ -432,6 +442,7 @@ const Results = (): JSX.Element => { { id: 'dnssec', title: 'DNSSEC', result: dnsSecResults, Component: DnsSecCard, refresh: updateDnsSecResults }, { id: 'status', title: 'Server Status', result: serverStatusResults, Component: ServerStatusCard, refresh: updateServerStatusResults }, { id: 'ports', title: 'Open Ports', result: portsResults, Component: OpenPortsCard, refresh: updatePortsResults }, + { id: 'security-txt', title: 'Security.Txt', result: securityTxtResults, Component: SecurityTxtCard, refresh: updateSecurityTxtResults }, { id: 'screenshot', title: 'Screenshot', result: screenshotResult || lighthouseResults?.fullPageScreenshot?.screenshot, Component: ScreenshotCard, refresh: updateScreenshotResult }, { id: 'txt-records', title: 'TXT Records', result: txtRecordResults, Component: TxtRecordCard, refresh: updateTxtRecordResults }, { id: 'hsts', title: 'HSTS Check', result: hstsResults, Component: HstsCard, refresh: updateHstsResults }, diff --git a/src/utils/docs.ts b/src/utils/docs.ts index a193466..7ecbf21 100644 --- a/src/utils/docs.ts +++ b/src/utils/docs.ts @@ -3,7 +3,7 @@ interface Doc { title: string; description: string; use: string; - resources: string[]; + resources: string[] | { title: string, link: string}[]; screenshot?: string; } @@ -342,6 +342,26 @@ const docs: Doc[] = [ ], screenshot: 'https://i.ibb.co/GtrCQYq/Screenshot-from-2023-07-21-12-28-38.png', }, + { + id: 'security-txt', + title: 'Security.txt', + description: "The security.txt file tells researchers how they can responsibly disclose any security issues found on your site. " + + "The standard was proposed in RFC 9116, and specifies that this file should include a point of contact (email address), " + + "as well as optionally other info, like a link to the security disclosure policy, PGP key, proffered language, policy expiry and more. " + + "The file should be located at the root of your domain, either at /security.txt or /.well-known/security.txt.", + use: "This is important, as without a defined point of contact a security researcher may be unable to report a critical security issue, " + + "or may use insecure or possibly public channels to do so. From an OSINT perspective, you may also glean info about a site including " + + "their posture on security, their CSAF provider, and meta data from the PGP public key.", + resources: [ + { title: 'securitytxt.org', link: 'https://securitytxt.org/'}, + { title: 'RFC-9116 Proposal', link: 'https://datatracker.ietf.org/doc/html/rfc9116'}, + { title: 'RFC-9116 History', link: 'https://datatracker.ietf.org/doc/rfc9116/'}, + { title: 'Security.txt (Wikipedia)', link: 'https://en.wikipedia.org/wiki/Security.txt'}, + { title: 'Example security.txt (Cloudflare)', link: 'https://www.cloudflare.com/.well-known/security.txt'}, + { title: 'Tutorial for creating security.txt (Pieter Bakker)', link: 'https://pieterbakker.com/implementing-security-txt/'}, + ], + screenshot: 'https://i.ibb.co/tq1FT5r/Screenshot-from-2023-07-24-20-31-21.png', + }, ]; export const about = [