Merge pull request #12 from Lissy93/FEAT/security-txt

Adds support for fetching, parsing and displaying the security.txt file
Fixes #7
This commit is contained in:
Alicia Sykes 2023-07-24 20:55:07 +01:00 committed by GitHub
commit c8d29c78a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 3 deletions

View File

@ -38,6 +38,7 @@
"axios": "^1.4.0",
"chrome-aws-lambda": "^10.1.0",
"flatted": "^3.2.7",
"follow-redirects": "^1.15.2",
"got": "^13.0.0",
"jest-styled-components": "^7.1.1",
"netlify-cli": "^15.9.1",

View File

@ -34,7 +34,7 @@ const StyledExpandableRow = styled(StyledRow).attrs({
as: "summary"
})``;
const Details = styled.details`
export const Details = styled.details`
transition: all 0.2s ease-in-out;
summary {
padding-left: 1rem;

View File

@ -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 (
<Card heading={props.title} actionButtons={props.actionButtons} styles={cardStyles}>
<Row lbl="Present" val={securityTxt.isPresent ? '✅ Yes' : '❌ No'} />
{ securityTxt.isPresent && (
<>
<Row lbl="File Location" val={securityTxt.foundIn} />
<Row lbl="PGP Signed" val={securityTxt.isPgpSigned ? '✅ Yes' : '❌ No'} />
{securityTxt.fields && Object.keys(securityTxt.fields).map((field: string, index: number) => {
if (securityTxt.fields[field].includes('http')) return (
<Row lbl="" val="" key={`policy-url-row-${index}`}>
<span className="lbl">{field}</span>
<span className="val"><a href={securityTxt.fields[field]}>{getPagePath(securityTxt.fields[field])}</a></span>
</Row>
);
return (
<Row lbl={field} val={securityTxt.fields[field]} key={`policy-row-${index}`} />
);
})}
<Details>
<summary>View Full Policy</summary>
<pre>{securityTxt.content}</pre>
</Details>
</>
)}
{!securityTxt.isPresent && (<small>
Having a security.txt ensures security researchers know how and where to safely report vulnerabilities.
</small>)}
</Card>
);
}
export default SecurityTxtCard;

View File

@ -201,6 +201,7 @@ const jobNames = [
'txt-records',
'sitemap',
'hsts',
'security-txt',
// 'whois',
'features',
'carbon',

View File

@ -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 },

View File

@ -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 = [

View File

@ -7802,7 +7802,7 @@ folder-walker@3.2.0:
dependencies:
from2 "^2.1.0"
follow-redirects@^1.0.0, follow-redirects@^1.15.0:
follow-redirects@^1.0.0, follow-redirects@^1.15.0, follow-redirects@^1.15.2:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==