mirror of
https://github.com/Lissy93/web-check.git
synced 2025-02-03 20:19:19 +01:00
Re-wrote all the requests to use a custom, reusable hook
This commit is contained in:
parent
e9a8dd0d9d
commit
ad99063bdf
@ -1,31 +1,31 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
// import Heading from 'components/Form/Heading';
|
import ErrorBoundary from 'components/misc/ErrorBoundary';
|
||||||
|
import Heading from 'components/Form/Heading';
|
||||||
import colors from 'styles/colors';
|
import colors from 'styles/colors';
|
||||||
|
|
||||||
export const Card = styled.section`
|
export const StyledCard = styled.section`
|
||||||
background: ${colors.backgroundLighter};
|
background: ${colors.backgroundLighter};
|
||||||
box-shadow: 4px 4px 0px ${colors.bgShadowColor};
|
box-shadow: 4px 4px 0px ${colors.bgShadowColor};
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// interface CardProps {
|
interface CardProps {
|
||||||
// children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
// heading?: string,
|
heading?: string,
|
||||||
// };
|
};
|
||||||
|
|
||||||
// const Card = (props: CardProps): JSX.Element => {
|
export const Card = (props: CardProps): JSX.Element => {
|
||||||
// const { children, heading } = props;
|
const { children, heading } = props;
|
||||||
// return (
|
return (
|
||||||
// <StyledCard>
|
<ErrorBoundary title={heading}>
|
||||||
// { heading &&
|
<StyledCard>
|
||||||
// <Heading as="h3" size="small" align="left" color={colors.primary}>{heading}</Heading>
|
{ heading && <Heading as="h3" align="left" color={colors.primary}>{heading}</Heading> }
|
||||||
// }
|
{children}
|
||||||
// {children}
|
</StyledCard>
|
||||||
// </StyledCard>
|
</ErrorBoundary>
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
export default Card;
|
|
||||||
|
|
||||||
|
export default StyledCard;
|
||||||
|
@ -6,7 +6,7 @@ import Heading from 'components/Form/Heading';
|
|||||||
export interface RowProps {
|
export interface RowProps {
|
||||||
lbl: string,
|
lbl: string,
|
||||||
val: string,
|
val: string,
|
||||||
key?: string,
|
// key?: string,
|
||||||
children?: ReactNode,
|
children?: ReactNode,
|
||||||
rowList?: RowProps[],
|
rowList?: RowProps[],
|
||||||
title?: string,
|
title?: string,
|
||||||
@ -77,10 +77,10 @@ const copyToClipboard = (text: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ExpandableRow = (props: RowProps) => {
|
export const ExpandableRow = (props: RowProps) => {
|
||||||
const { lbl, val, key, title, rowList } = props;
|
const { lbl, val, title, rowList } = props;
|
||||||
return (
|
return (
|
||||||
<Details>
|
<Details>
|
||||||
<StyledExpandableRow key={key}>
|
<StyledExpandableRow key={`${lbl}-${val}`}>
|
||||||
<span className="lbl" title={title}>{lbl}</span>
|
<span className="lbl" title={title}>{lbl}</span>
|
||||||
<span className="val" title={val}>{val}</span>
|
<span className="val" title={val}>{val}</span>
|
||||||
</StyledExpandableRow>
|
</StyledExpandableRow>
|
||||||
@ -88,7 +88,7 @@ export const ExpandableRow = (props: RowProps) => {
|
|||||||
<SubRowList>
|
<SubRowList>
|
||||||
{ rowList?.map((row: RowProps, index: number) => {
|
{ rowList?.map((row: RowProps, index: number) => {
|
||||||
return (
|
return (
|
||||||
<SubRow key={row.key || `${row.lbl}-${index}`}>
|
<SubRow key={`${row.lbl}-${index}`}>
|
||||||
<span className="lbl" title={row.title}>{row.lbl}</span>
|
<span className="lbl" title={row.title}>{row.lbl}</span>
|
||||||
<span className="val" title={row.val} onClick={() => copyToClipboard(row.val)}>
|
<span className="val" title={row.val} onClick={() => copyToClipboard(row.val)}>
|
||||||
{formatValue(row.val)}
|
{formatValue(row.val)}
|
||||||
@ -109,7 +109,7 @@ export const ListRow = (props: { list: string[], title: string }) => {
|
|||||||
<Heading as="h4" size="small" align="left" color={colors.primary}>{title}</Heading>
|
<Heading as="h4" size="small" align="left" color={colors.primary}>{title}</Heading>
|
||||||
{ list.map((entry: string, index: number) => {
|
{ list.map((entry: string, index: number) => {
|
||||||
return (
|
return (
|
||||||
<Row lbl="" val="" key={`${title.toLocaleLowerCase()}-${index}`}>
|
<Row lbl="" val="" key={`${entry}-${title.toLocaleLowerCase()}-${index}`}>
|
||||||
<span>{ entry }</span>
|
<span>{ entry }</span>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
@ -119,10 +119,10 @@ export const ListRow = (props: { list: string[], title: string }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Row = (props: RowProps) => {
|
const Row = (props: RowProps) => {
|
||||||
const { lbl, val, key, title, children } = props;
|
const { lbl, val, title, children } = props;
|
||||||
if (children) return <StyledRow key={key}>{children}</StyledRow>;
|
if (children) return <StyledRow key={`${lbl}-${val}`}>{children}</StyledRow>;
|
||||||
return (
|
return (
|
||||||
<StyledRow key={key}>
|
<StyledRow key={`${lbl}-${val}`}>
|
||||||
{ lbl && <span className="lbl" title={title}>{lbl}</span> }
|
{ lbl && <span className="lbl" title={title}>{lbl}</span> }
|
||||||
<span className="val" title={val} onClick={() => copyToClipboard(val)}>
|
<span className="val" title={val} onClick={() => copyToClipboard(val)}>
|
||||||
{formatValue(val)}
|
{formatValue(val)}
|
||||||
|
@ -23,7 +23,7 @@ const RobotsTxtCard = (props: { robotTxt: RowProps[] }): JSX.Element => {
|
|||||||
{
|
{
|
||||||
props.robotTxt.map((row: RowProps, index: number) => {
|
props.robotTxt.map((row: RowProps, index: number) => {
|
||||||
return (
|
return (
|
||||||
<Row key={row.key || `${row.lbl}-${index}`} lbl={row.lbl} val={row.val} />
|
<Row key={`${row.lbl}-${index}`} lbl={row.lbl} val={row.val} />
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -125,14 +125,17 @@ const FancyBackground = (): JSX.Element => {
|
|||||||
var time2 = performance.now();
|
var time2 = performance.now();
|
||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
document.getElementsByClassName('dead')[0].textContent = this.deathCount;
|
const elemDead = document.getElementsByClassName('dead');
|
||||||
document.getElementsByClassName('alive')[0].textContent =
|
if (elemDead && elemDead.length > 0) elemDead[0].textContent = this.deathCount;
|
||||||
this.particles.length;
|
|
||||||
document.getElementsByClassName('fps')[0].textContent = Math.floor(
|
const elemAlive = document.getElementsByClassName('alive');
|
||||||
1000 / (time2 - time1)
|
if (elemAlive && elemAlive.length > 0) elemAlive[0].textContent = this.particles.length;
|
||||||
).toString();
|
|
||||||
document.getElementsByClassName('drawn')[0].textContent =
|
const elemFPS = document.getElementsByClassName('fps');
|
||||||
this.drawnInLastFrame;
|
if (elemFPS && elemFPS.length > 0) elemFPS[0].textContent = Math.round(1000 / (time2 - time1)).toString();
|
||||||
|
|
||||||
|
const elemDrawn = document.getElementsByClassName('drawn');
|
||||||
|
if (elemDrawn && elemDrawn.length > 0) elemDrawn[0].textContent = this.drawnInLastFrame;
|
||||||
};
|
};
|
||||||
App.birth = function () {
|
App.birth = function () {
|
||||||
var x, y;
|
var x, y;
|
||||||
|
@ -140,6 +140,7 @@ const jobNames = [
|
|||||||
'headers',
|
'headers',
|
||||||
'lighthouse',
|
'lighthouse',
|
||||||
'location',
|
'location',
|
||||||
|
'shodan',
|
||||||
// 'server-info',
|
// 'server-info',
|
||||||
'whois',
|
'whois',
|
||||||
] as const;
|
] as const;
|
||||||
@ -307,6 +308,7 @@ const ProgressLoader = (props: { loadStatus: LoadingJob[] }): JSX.Element => {
|
|||||||
color2={barColors[state][1]}
|
color2={barColors[state][1]}
|
||||||
title={`${state} (${Math.round(percentages[state])}%)`}
|
title={`${state} (${Math.round(percentages[state])}%)`}
|
||||||
width={percentages[state]}
|
width={percentages[state]}
|
||||||
|
key={`progress-bar-${state}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ProgressBarContainer>
|
</ProgressBarContainer>
|
||||||
|
67
src/hooks/motherOfAllHooks.ts
Normal file
67
src/hooks/motherOfAllHooks.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { LoadingState } from 'components/misc/ProgressBar';
|
||||||
|
import { AddressType } from 'utils/address-type-checker';
|
||||||
|
|
||||||
|
type UpdateLoadingJobsFunction = (job: string, newState: LoadingState, error?: string) => void;
|
||||||
|
|
||||||
|
interface AddressInfo {
|
||||||
|
address: string | undefined;
|
||||||
|
addressType: AddressType;
|
||||||
|
expectedAddressTypes: AddressType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseIpAddressProps<ResultType = any> {
|
||||||
|
addressInfo: AddressInfo;
|
||||||
|
updateLoadingJobs: UpdateLoadingJobsFunction;
|
||||||
|
jobId: string;
|
||||||
|
fetchRequest: () => Promise<ResultType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultType = any;
|
||||||
|
|
||||||
|
type ReturnType = [ResultType | undefined, React.Dispatch<React.SetStateAction<ResultType | undefined>>];
|
||||||
|
|
||||||
|
|
||||||
|
const useMotherOfAllHooks = <ResultType = any>(params: UseIpAddressProps<ResultType>): ReturnType => {
|
||||||
|
// Destructure params
|
||||||
|
const { addressInfo, fetchRequest, jobId, updateLoadingJobs } = params;
|
||||||
|
const { address, addressType, expectedAddressTypes } = addressInfo;
|
||||||
|
|
||||||
|
// Build useState that will be returned
|
||||||
|
const [result, setResult] = useState<ResultType>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Still waiting for this upstream, cancel job
|
||||||
|
if (!address || !addressType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// This job isn't needed for this address type, cancel job
|
||||||
|
if (!expectedAddressTypes.includes(addressType)) {
|
||||||
|
// updateLoadingJobs(jobId, 'skipped');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate fetch request, set results and update loading / error state
|
||||||
|
fetchRequest()
|
||||||
|
.then((res) => {
|
||||||
|
// All went to plan, set results and mark as done
|
||||||
|
setResult(res);
|
||||||
|
updateLoadingJobs(jobId, 'success');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
// Something fucked up, log the error
|
||||||
|
updateLoadingJobs(jobId, 'error', err.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [address, addressType]);
|
||||||
|
|
||||||
|
return [result, setResult];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useMotherOfAllHooks;
|
||||||
|
|
||||||
|
// I really fucking hate TypeScript sometimes....
|
||||||
|
// Feels like a weak attempt at trying to make JavaScript less crappy,
|
||||||
|
// when the real solution would be to just switch to a proper, typed, safe language
|
||||||
|
// ... Either that, or I'm just really shit at it.
|
@ -93,7 +93,7 @@ const Home = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function(error) {
|
.catch(function(error) {
|
||||||
console.log(error)
|
console.log('Failed to get IP address :\'(', error)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback, SetStateAction, Dispatch } from 'react';
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@ -7,6 +7,8 @@ import Heading from 'components/Form/Heading';
|
|||||||
import Card from 'components/Form/Card';
|
import Card from 'components/Form/Card';
|
||||||
import ErrorBoundary from 'components/misc/ErrorBoundary';
|
import ErrorBoundary from 'components/misc/ErrorBoundary';
|
||||||
import Footer from 'components/misc/Footer';
|
import Footer from 'components/misc/Footer';
|
||||||
|
import { RowProps } from 'components/Form/Row';
|
||||||
|
|
||||||
|
|
||||||
import ServerLocationCard from 'components/Results/ServerLocation';
|
import ServerLocationCard from 'components/Results/ServerLocation';
|
||||||
import ServerInfoCard from 'components/Results/ServerInfo';
|
import ServerInfoCard from 'components/Results/ServerInfo';
|
||||||
@ -24,14 +26,16 @@ import ProgressBar, { LoadingJob, LoadingState, initialJobs } from 'components/m
|
|||||||
import keys from 'utils/get-keys';
|
import keys from 'utils/get-keys';
|
||||||
import { determineAddressType, AddressType } from 'utils/address-type-checker';
|
import { determineAddressType, AddressType } from 'utils/address-type-checker';
|
||||||
|
|
||||||
|
import useMotherHook from 'hooks/motherOfAllHooks';
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getLocation, ServerLocation,
|
getLocation, ServerLocation,
|
||||||
getServerInfo, ServerInfo,
|
|
||||||
getHostNames, HostNames,
|
|
||||||
makeTechnologies, TechnologyGroup,
|
makeTechnologies, TechnologyGroup,
|
||||||
parseCookies, Cookie,
|
parseCookies, Cookie,
|
||||||
parseRobotsTxt,
|
parseRobotsTxt,
|
||||||
Whois,
|
applyWhoIsResults, Whois,
|
||||||
|
parseShodanResults, ShodanResults
|
||||||
} from 'utils/result-processor';
|
} from 'utils/result-processor';
|
||||||
|
|
||||||
const ResultsOuter = styled.div`
|
const ResultsOuter = styled.div`
|
||||||
@ -62,19 +66,6 @@ const Header = styled(Card)`
|
|||||||
const Results = (): JSX.Element => {
|
const Results = (): JSX.Element => {
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
|
|
||||||
const [ serverInfo, setServerInfo ] = useState<ServerInfo>();
|
|
||||||
const [ hostNames, setHostNames ] = useState<HostNames | null>();
|
|
||||||
const [ locationResults, setLocationResults ] = useState<ServerLocation>();
|
|
||||||
const [ whoIsResults, setWhoIsResults ] = useState<Whois>();
|
|
||||||
const [ technologyResults, setTechnologyResults ] = useState<TechnologyGroup[]>();
|
|
||||||
const [ lighthouseResults, setLighthouseResults ] = useState<any>();
|
|
||||||
const [ sslResults, setSslResults ] = useState<any>();
|
|
||||||
const [ headersResults, setHeadersResults ] = useState<any>();
|
|
||||||
const [ dnsResults, setDnsResults ] = useState<any>();
|
|
||||||
const [ robotsTxtResults, setRobotsTxtResults ] = useState<any>();
|
|
||||||
const [ cookieResults, setCookieResults ] = useState<Cookie[] | null>(null);
|
|
||||||
const [ screenshotResult, setScreenshotResult ] = useState<string>();
|
|
||||||
const [ ipAddress, setIpAddress ] = useState<undefined | string>(undefined);
|
|
||||||
const [ addressType, setAddressType ] = useState<AddressType>('empt');
|
const [ addressType, setAddressType ] = useState<AddressType>('empt');
|
||||||
const { address } = useParams();
|
const { address } = useParams();
|
||||||
|
|
||||||
@ -91,14 +82,132 @@ const Results = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (newState === 'error') {
|
if (newState === 'error') {
|
||||||
console.warn(`Error in ${job}: ${error}`);
|
console.log(
|
||||||
|
`%cWeb-Check Fetch Error - ${job}%c\n\nThe ${job} job failed with the following error:%c\n${error}`,
|
||||||
|
`background: ${colors.danger}; padding: 4px 8px; font-size: 16px;`,
|
||||||
|
`color: ${colors.danger};`,
|
||||||
|
`color: ${colors.warning};`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newJobs;
|
return newJobs;
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/* Cancel remaining jobs after 20 second timeout */
|
useEffect(() => {
|
||||||
|
setAddressType(determineAddressType(address || ''));
|
||||||
|
if (addressType === 'ipV4' && address) {
|
||||||
|
setIpAddress(address);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const urlTypeOnly = ['url'] as AddressType[]; // Many jobs only run with these address types
|
||||||
|
|
||||||
|
// Fetch and parse IP address for given URL
|
||||||
|
const [ipAddress, setIpAddress] = useMotherHook({
|
||||||
|
jobId: 'get-ip',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`http://localhost:8888/.netlify/functions/find-url-ip?address=${address}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => res.ip),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse SSL certificate info
|
||||||
|
const [sslResults] = useMotherHook({
|
||||||
|
jobId: 'ssl',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`/ssl-check?url=${address}`).then((res) => res.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse cookies info
|
||||||
|
const [cookieResults] = useMotherHook<Cookie[]>({
|
||||||
|
jobId: 'cookies',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`/get-cookies?url=${address}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => parseCookies(res.cookies)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse crawl rules from robots.txt
|
||||||
|
const [robotsTxtResults] = useMotherHook<RowProps[]>({
|
||||||
|
jobId: 'robots-txt',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`/read-robots-txt?url=${address}`)
|
||||||
|
.then(res => res.text())
|
||||||
|
.then(res => parseRobotsTxt(res)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse headers
|
||||||
|
const [headersResults] = useMotherHook({
|
||||||
|
jobId: 'headers',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`/get-headers?url=${address}`).then(res => res.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse DNS records
|
||||||
|
const [dnsResults] = useMotherHook({
|
||||||
|
jobId: 'dns',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`/get-dns?url=${address}`).then(res => res.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse Lighthouse performance data
|
||||||
|
const [lighthouseResults] = useMotherHook({
|
||||||
|
jobId: 'lighthouse',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`/lighthouse-report?url=${address}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => res.lighthouseResult),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get IP address location info
|
||||||
|
const [locationResults] = useMotherHook<ServerLocation>({
|
||||||
|
jobId: 'location',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address: ipAddress, addressType: 'ipV4', expectedAddressTypes: ['ipV4', 'ipV6'] },
|
||||||
|
fetchRequest: () => fetch(`https://ipapi.co/${ipAddress}/json/`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => getLocation(res)),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Get hostnames and associated domains from Shodan
|
||||||
|
const [shoadnResults] = useMotherHook<ShodanResults>({
|
||||||
|
jobId: 'shodan',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address: ipAddress, addressType: 'ipV4', expectedAddressTypes: ['ipV4', 'ipV6'] },
|
||||||
|
fetchRequest: () => fetch(`https://api.shodan.io/shodan/host/${ipAddress}?key=${keys.shodan}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => parseShodanResults(res)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse domain whois results
|
||||||
|
const [whoIsResults] = useMotherHook<Whois>({
|
||||||
|
jobId: 'whois',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`https://api.whoapi.com/?domain=${address}&r=whois&apikey=${keys.whoApi}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => applyWhoIsResults(res)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch and parse built-with results
|
||||||
|
const [technologyResults] = useMotherHook<TechnologyGroup[]>({
|
||||||
|
jobId: 'built-with',
|
||||||
|
updateLoadingJobs,
|
||||||
|
addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly },
|
||||||
|
fetchRequest: () => fetch(`https://api.builtwith.com/v21/api.json?KEY=${keys.builtWith}&LOOKUP=${address}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => makeTechnologies(res)),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Cancel remaining jobs after 10 second timeout */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkJobs = () => {
|
const checkJobs = () => {
|
||||||
loadingJobs.forEach(job => {
|
loadingJobs.forEach(job => {
|
||||||
@ -113,237 +222,6 @@ const Results = (): JSX.Element => {
|
|||||||
};
|
};
|
||||||
}, [loadingJobs, updateLoadingJobs]); // dependencies for the effect
|
}, [loadingJobs, updateLoadingJobs]); // dependencies for the effect
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setAddressType(determineAddressType(address || ''));
|
|
||||||
if (addressType === 'ipV4') {
|
|
||||||
setIpAddress(address);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
/* Get IP address from URL */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('get-ip', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const fetchIpAddress = () => {
|
|
||||||
fetch(`/find-url-ip?address=${address}`)
|
|
||||||
.then(function(response) {
|
|
||||||
response.json().then(jsonData => {
|
|
||||||
setIpAddress(jsonData.ip);
|
|
||||||
updateLoadingJobs('get-ip', 'success');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(function(error) {
|
|
||||||
updateLoadingJobs('get-ip', 'error', error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (!ipAddress) {
|
|
||||||
fetchIpAddress();
|
|
||||||
}
|
|
||||||
}, [address, addressType]);
|
|
||||||
|
|
||||||
/* Get SSL info */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('ssl', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch(`/ssl-check?url=${address}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
if (Object.keys(response).length > 0) {
|
|
||||||
setSslResults(response);
|
|
||||||
updateLoadingJobs('ssl', 'success');
|
|
||||||
} else {
|
|
||||||
updateLoadingJobs('ssl', 'error', 'No SSL Cert found');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('ssl', 'error', err));
|
|
||||||
}, [address, addressType])
|
|
||||||
|
|
||||||
/* Get Cookies */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('cookies', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch(`/get-cookies?url=${address}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
setCookieResults(parseCookies(response.cookies));
|
|
||||||
updateLoadingJobs('cookies', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('cookies', 'error', err));
|
|
||||||
}, [address, addressType])
|
|
||||||
|
|
||||||
/* Get Robots.txt */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('robots-txt', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch(`/read-robots-txt?url=${address}`)
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(response => {
|
|
||||||
setRobotsTxtResults(parseRobotsTxt(response));
|
|
||||||
updateLoadingJobs('robots-txt', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('robots-txt', 'error', err));
|
|
||||||
}, [address, addressType])
|
|
||||||
|
|
||||||
/* Get Headers */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('headers', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch(`/get-headers?url=${address}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
setHeadersResults(response);
|
|
||||||
updateLoadingJobs('headers', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('headers', 'error', err));
|
|
||||||
}, [address, addressType])
|
|
||||||
|
|
||||||
/* Get DNS records */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('dns', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch(`/get-dns?url=${address}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
setDnsResults(response);
|
|
||||||
updateLoadingJobs('dns', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('dns', 'error', err));
|
|
||||||
}, [address, addressType])
|
|
||||||
|
|
||||||
/* Get Lighthouse report */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('lighthouse', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch(`/lighthouse-report?url=${address}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
setLighthouseResults(response.lighthouseResult);
|
|
||||||
setScreenshotResult(response.lighthouseResult?.fullPageScreenshot?.screenshot?.data);
|
|
||||||
updateLoadingJobs('lighthouse', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// if (err.errorType === 'TimeoutError') {
|
|
||||||
// Netlify limits to 10 seconds, we can try again client-side...
|
|
||||||
const params = 'category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&category=PWA&strategy=mobile';
|
|
||||||
const endpoint = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${address}&${params}&key=${keys.googleCloud}`;
|
|
||||||
fetch(endpoint)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
setLighthouseResults(response.lightHouseResult);
|
|
||||||
setScreenshotResult(response?.lighthouseResult?.fullPageScreenshot?.screenshot?.data);
|
|
||||||
updateLoadingJobs('lighthouse', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('lighthouse', 'error', err));
|
|
||||||
});
|
|
||||||
}, [address, addressType])
|
|
||||||
|
|
||||||
|
|
||||||
/* Get IP address location info */
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchIpLocation = () => {
|
|
||||||
fetch(`https://ipapi.co/${ipAddress}/json/`)
|
|
||||||
.then(function(response) {
|
|
||||||
response.json().then(jsonData => {
|
|
||||||
setLocationResults(getLocation(jsonData));
|
|
||||||
updateLoadingJobs('location', 'success');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(function(error) {
|
|
||||||
updateLoadingJobs('location', 'error', error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (ipAddress) {
|
|
||||||
fetchIpLocation();
|
|
||||||
}
|
|
||||||
}, [ipAddress]);
|
|
||||||
|
|
||||||
/* Get hostnames and server info from Shodan */
|
|
||||||
useEffect(() => {
|
|
||||||
const applyShodanResults = (response: any) => {
|
|
||||||
setServerInfo(getServerInfo(response));
|
|
||||||
setHostNames(getHostNames(response));
|
|
||||||
}
|
|
||||||
const fetchShodanData = () => {
|
|
||||||
const apiKey = keys.shodan;
|
|
||||||
fetch(`https://api.shodan.io/shodan/host/${ipAddress}?key=${apiKey}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
if (!response.error) {
|
|
||||||
applyShodanResults(response)
|
|
||||||
updateLoadingJobs('server-info', 'success');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('server-info', 'error', err));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (ipAddress) {
|
|
||||||
fetchShodanData();
|
|
||||||
}
|
|
||||||
}, [ipAddress]);
|
|
||||||
|
|
||||||
/* Get BuiltWith tech stack */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('built-with', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const apiKey = keys.builtWith;
|
|
||||||
const endpoint = `https://api.builtwith.com/v21/api.json?KEY=${apiKey}&LOOKUP=${address}`;
|
|
||||||
fetch(endpoint)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
setTechnologyResults(makeTechnologies(response));
|
|
||||||
updateLoadingJobs('built-with', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('built-with', 'error', err));
|
|
||||||
}, [address, addressType]);
|
|
||||||
|
|
||||||
/* Get WhoIs info for a given domain name */
|
|
||||||
useEffect(() => {
|
|
||||||
if (addressType !== 'url') {
|
|
||||||
updateLoadingJobs('whois', 'skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const applyWhoIsResults = (response: any) => {
|
|
||||||
const whoIsResults: Whois = {
|
|
||||||
created: response.date_created,
|
|
||||||
expires: response.date_expires,
|
|
||||||
updated: response.date_updated,
|
|
||||||
nameservers: response.nameservers,
|
|
||||||
};
|
|
||||||
setWhoIsResults(whoIsResults);
|
|
||||||
}
|
|
||||||
const fetchWhoIsData = () => {
|
|
||||||
const apiKey = keys.whoApi;
|
|
||||||
fetch(`https://api.whoapi.com/?domain=${address}&r=whois&apikey=${apiKey}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
if (!response.error) applyWhoIsResults(response)
|
|
||||||
updateLoadingJobs('whois', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => updateLoadingJobs('whois', 'error', err));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (addressType === 'url') {
|
|
||||||
fetchWhoIsData();
|
|
||||||
}
|
|
||||||
}, [addressType, address]);
|
|
||||||
|
|
||||||
const makeSiteName = (address: string): string => {
|
const makeSiteName = (address: string): string => {
|
||||||
try {
|
try {
|
||||||
return new URL(address).hostname.replace('www.', '');
|
return new URL(address).hostname.replace('www.', '');
|
||||||
@ -377,7 +255,7 @@ const Results = (): JSX.Element => {
|
|||||||
{ headersResults && <HeadersCard headers={headersResults} />}
|
{ headersResults && <HeadersCard headers={headersResults} />}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<ErrorBoundary title="Host Names">
|
<ErrorBoundary title="Host Names">
|
||||||
{ hostNames && <HostNamesCard hosts={hostNames} />}
|
{ shoadnResults?.hostnames && <HostNamesCard hosts={shoadnResults.hostnames} />}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<ErrorBoundary title="Domain Info">
|
<ErrorBoundary title="Domain Info">
|
||||||
{ whoIsResults && <WhoIsCard {...whoIsResults} />}
|
{ whoIsResults && <WhoIsCard {...whoIsResults} />}
|
||||||
@ -392,7 +270,7 @@ const Results = (): JSX.Element => {
|
|||||||
{ cookieResults && <CookiesCard cookies={cookieResults} />}
|
{ cookieResults && <CookiesCard cookies={cookieResults} />}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<ErrorBoundary title="Screenshot">
|
<ErrorBoundary title="Screenshot">
|
||||||
{ screenshotResult && <ScreenshotCard screenshot={screenshotResult} />}
|
{ lighthouseResults?.fullPageScreenshot?.screenshot?.data && <ScreenshotCard screenshot={lighthouseResults.fullPageScreenshot.screenshot.data} />}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<ErrorBoundary title="Technologies">
|
<ErrorBoundary title="Technologies">
|
||||||
{ technologyResults && <BuiltWithCard technologies={technologyResults} />}
|
{ technologyResults && <BuiltWithCard technologies={technologyResults} />}
|
||||||
@ -401,7 +279,7 @@ const Results = (): JSX.Element => {
|
|||||||
{ robotsTxtResults && <RobotsTxtCard robotTxt={robotsTxtResults} />}
|
{ robotsTxtResults && <RobotsTxtCard robotTxt={robotsTxtResults} />}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<ErrorBoundary title="Server Info">
|
<ErrorBoundary title="Server Info">
|
||||||
{ serverInfo && <ServerInfoCard {...serverInfo} />}
|
{ shoadnResults?.serverInfo && <ServerInfoCard {...shoadnResults.serverInfo} />}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</ResultsContent>
|
</ResultsContent>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { RowProps } from 'components/Form/Row';
|
||||||
|
|
||||||
export interface ServerLocation {
|
export interface ServerLocation {
|
||||||
city: string,
|
city: string,
|
||||||
region: string,
|
region: string,
|
||||||
@ -68,7 +70,7 @@ export const getServerInfo = (response: any): ServerInfo => {
|
|||||||
isp: response.isp,
|
isp: response.isp,
|
||||||
os: response.os,
|
os: response.os,
|
||||||
ip: response.ip_str,
|
ip: response.ip_str,
|
||||||
ports: response.ports.toString(),
|
ports: response?.ports?.toString(),
|
||||||
loc: response.city ? `${response.city}, ${response.country_name}` : '',
|
loc: response.city ? `${response.city}, ${response.country_name}` : '',
|
||||||
type: response.tags ? response.tags.toString() : '',
|
type: response.tags ? response.tags.toString() : '',
|
||||||
};
|
};
|
||||||
@ -91,6 +93,18 @@ export const getHostNames = (response: any): HostNames | null => {
|
|||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface ShodanResults {
|
||||||
|
hostnames: HostNames | null,
|
||||||
|
serverInfo: ServerInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseShodanResults = (response: any): ShodanResults => {
|
||||||
|
return {
|
||||||
|
hostnames: getHostNames(response),
|
||||||
|
serverInfo: getServerInfo(response),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface Technology {
|
export interface Technology {
|
||||||
Categories?: string[];
|
Categories?: string[];
|
||||||
Parent?: string;
|
Parent?: string;
|
||||||
@ -140,21 +154,16 @@ export const parseCookies = (cookiesHeader: string): Cookie[] => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
type RobotsRule = {
|
export const parseRobotsTxt = (content: string): RowProps[] => {
|
||||||
lbl: string;
|
|
||||||
val: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseRobotsTxt = (content: string): RobotsRule[] => {
|
|
||||||
const lines = content.split('\n');
|
const lines = content.split('\n');
|
||||||
const rules: RobotsRule[] = [];
|
const rules: RowProps[] = [];
|
||||||
|
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
line = line.trim(); // This removes trailing and leading whitespaces
|
line = line.trim(); // This removes trailing and leading whitespaces
|
||||||
|
|
||||||
let match = line.match(/^(Allow|Disallow):\s*(\S*)$/i);
|
let match = line.match(/^(Allow|Disallow):\s*(\S*)$/i);
|
||||||
if (match) {
|
if (match) {
|
||||||
const rule: RobotsRule = {
|
const rule: RowProps = {
|
||||||
lbl: match[1],
|
lbl: match[1],
|
||||||
val: match[2],
|
val: match[2],
|
||||||
};
|
};
|
||||||
@ -163,7 +172,7 @@ export const parseRobotsTxt = (content: string): RobotsRule[] => {
|
|||||||
} else {
|
} else {
|
||||||
match = line.match(/^(User-agent):\s*(\S*)$/i);
|
match = line.match(/^(User-agent):\s*(\S*)$/i);
|
||||||
if (match) {
|
if (match) {
|
||||||
const rule: RobotsRule = {
|
const rule: RowProps = {
|
||||||
lbl: match[1],
|
lbl: match[1],
|
||||||
val: match[2],
|
val: match[2],
|
||||||
};
|
};
|
||||||
@ -176,4 +185,13 @@ export const parseRobotsTxt = (content: string): RobotsRule[] => {
|
|||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const applyWhoIsResults = (response: any) => {
|
||||||
|
const whoIsResults: Whois = {
|
||||||
|
created: response.date_created,
|
||||||
|
expires: response.date_expires,
|
||||||
|
updated: response.date_updated,
|
||||||
|
nameservers: response.nameservers,
|
||||||
|
};
|
||||||
|
return whoIsResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user