🦄 Came back to this project after a year.... implemented lots of things

This commit is contained in:
Alicia Sykes 2023-06-18 16:02:57 +01:00
parent a6043d51d4
commit 920ab64410
9 changed files with 227 additions and 24 deletions

View File

@ -8,7 +8,6 @@ export const Card = styled.section`
box-shadow: 4px 4px 0px ${colors.bgShadowColor}; box-shadow: 4px 4px 0px ${colors.bgShadowColor};
border-radius: 8px; border-radius: 8px;
padding: 1rem; padding: 1rem;
margin: 1rem;
`; `;
// interface CardProps { // interface CardProps {

View File

@ -0,0 +1,69 @@
import styled from 'styled-components';
import { TechnologyGroup, Technology } from 'utils/result-processor';
import colors from 'styles/colors';
import Card from 'components/Form/Card';
import Heading from 'components/Form/Heading';
const Outer = styled(Card)`
grid-row: span 2
`;
const Row = styled.div`
display: flex;
justify-content: space-between;
padding: 0.25rem;
&:not(:last-child) { border-bottom: 1px solid ${colors.primary}; }
span.lbl { font-weight: bold; }
span.val {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;
const DataRow = (props: { lbl: string, val: string }) => {
const { lbl, val } = props;
return (
<Row>
<span className="lbl">{lbl}</span>
<span className="val" title={val}>{val}</span>
</Row>
);
};
const ListRow = (props: { list: Technology[], title: string }) => {
const { list, title } = props;
return (
<>
<Heading as="h3" size="small" align="left" color={colors.primary}>{title}</Heading>
{ list.map((entry: Technology, index: number) => {
return (
<Row key={`${title.toLocaleLowerCase()}-${index}`}><span>{ entry.Name }</span></Row>
)}
)}
</>
);
}
const BuiltWithCard = (props: { technologies: TechnologyGroup[] }): JSX.Element => {
// const { created, updated, expires, nameservers } = whois;
const { technologies } = props;
return (
<Outer>
<Heading as="h3" size="small" align="left" color={colors.primary}>Technologies</Heading>
{ technologies.map((group: TechnologyGroup) => {
return (
<ListRow key={group.tag} title={group.tag} list={group.technologies} />
);
})}
{/* { created && <DataRow lbl="Created" val={formatDate(created)} /> }
{ updated && <DataRow lbl="Updated" val={formatDate(updated)} /> }
{ expires && <DataRow lbl="Expires" val={formatDate(expires)} /> }
{ nameservers && <ListRow title="Name Servers" list={nameservers} /> } */}
</Outer>
);
}
export default BuiltWithCard;

View File

@ -6,7 +6,8 @@ import Card from 'components/Form/Card';
import Heading from 'components/Form/Heading'; import Heading from 'components/Form/Heading';
const Outer = styled(Card)` const Outer = styled(Card)`
max-width: 24rem; max-height: 20rem;
overflow: auto;
`; `;
const Row = styled.div` const Row = styled.div`

View File

@ -5,9 +5,7 @@ import colors from 'styles/colors';
import Card from 'components/Form/Card'; import Card from 'components/Form/Card';
import Heading from 'components/Form/Heading'; import Heading from 'components/Form/Heading';
const Outer = styled(Card)` const Outer = styled(Card)``;
max-width: 24rem;
`;
const Row = styled.div` const Row = styled.div`
display: flex; display: flex;
@ -34,7 +32,7 @@ const DataRow = (props: { lbl: string, val: string }) => {
}; };
const ServerInfoCard = (info: ServerInfo): JSX.Element => { const ServerInfoCard = (info: ServerInfo): JSX.Element => {
const { org, asn, isp, os } = info; const { org, asn, isp, os, ports } = info;
return ( return (
<Outer> <Outer>
<Heading as="h3" size="small" align="left" color={colors.primary}>Server Info</Heading> <Heading as="h3" size="small" align="left" color={colors.primary}>Server Info</Heading>
@ -42,6 +40,7 @@ const ServerInfoCard = (info: ServerInfo): JSX.Element => {
{ (isp && isp !== org) && <DataRow lbl="Service Provider" val={isp} /> } { (isp && isp !== org) && <DataRow lbl="Service Provider" val={isp} /> }
{ os && <DataRow lbl="Operating System" val={os} /> } { os && <DataRow lbl="Operating System" val={os} /> }
{ asn && <DataRow lbl="ASN Code" val={asn} /> } { asn && <DataRow lbl="ASN Code" val={asn} /> }
{ ports && <DataRow lbl="Ports" val={ports} /> }
</Outer> </Outer>
); );
} }

View File

@ -9,7 +9,7 @@ import Flag from 'components/misc/Flag';
import { TextSizes } from 'styles/typography'; import { TextSizes } from 'styles/typography';
const Outer = styled(Card)` const Outer = styled(Card)`
max-width: 24rem; grid-row: span 2
`; `;
const Row = styled.div` const Row = styled.div`

View File

@ -0,0 +1,71 @@
import styled from 'styled-components';
import { Whois } from 'utils/result-processor';
import colors from 'styles/colors';
import Card from 'components/Form/Card';
import Heading from 'components/Form/Heading';
const Outer = styled(Card)``;
const Row = styled.div`
display: flex;
justify-content: space-between;
padding: 0.25rem;
&:not(:last-child) { border-bottom: 1px solid ${colors.primary}; }
span.lbl { font-weight: bold; }
span.val {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;
const formatDate = (dateString: string): string => {
const date = new Date(dateString);
const formatter = new Intl.DateTimeFormat('en-GB', {
day: 'numeric',
month: 'long',
year: 'numeric'
});
return formatter.format(date);
}
const DataRow = (props: { lbl: string, val: string }) => {
const { lbl, val } = props;
return (
<Row>
<span className="lbl">{lbl}</span>
<span className="val" title={val}>{val}</span>
</Row>
);
};
const ListRow = (props: { list: string[], title: string }) => {
const { list, title } = props;
return (
<>
<Heading as="h3" size="small" align="left" color={colors.primary}>{title}</Heading>
{ list.map((entry: string, index: number) => {
return (
<Row key={`${title.toLocaleLowerCase()}-${index}`}><span>{ entry }</span></Row>
)}
)}
</>
);
}
const ServerInfoCard = (whois: Whois): JSX.Element => {
const { created, updated, expires, nameservers } = whois;
return (
<Outer>
<Heading as="h3" size="small" align="left" color={colors.primary}>Who Is Info</Heading>
{ created && <DataRow lbl="Created" val={formatDate(created)} /> }
{ updated && <DataRow lbl="Updated" val={formatDate(updated)} /> }
{ expires && <DataRow lbl="Expires" val={formatDate(expires)} /> }
{ nameservers && <ListRow title="Name Servers" list={nameservers} /> }
</Outer>
);
}
export default ServerInfoCard;

View File

@ -8,6 +8,8 @@ import Card from 'components/Form/Card';
import ServerLocationCard from 'components/Results/ServerLocation'; import ServerLocationCard from 'components/Results/ServerLocation';
import ServerInfoCard from 'components/Results/ServerInfo'; import ServerInfoCard from 'components/Results/ServerInfo';
import HostNamesCard from 'components/Results/HostNames'; import HostNamesCard from 'components/Results/HostNames';
import WhoIsCard from 'components/Results/WhoIs';
import BuiltWithCard from 'components/Results/BuiltWith';
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';
@ -15,6 +17,8 @@ import {
getLocation, ServerLocation, getLocation, ServerLocation,
getServerInfo, ServerInfo, getServerInfo, ServerInfo,
getHostNames, HostNames, getHostNames, HostNames,
makeTechnologies, TechnologyGroup,
Whois,
} from 'utils/result-processor'; } from 'utils/result-processor';
const ResultsOuter = styled.div` const ResultsOuter = styled.div`
@ -24,13 +28,19 @@ const ResultsOuter = styled.div`
const ResultsContent = styled.section` const ResultsContent = styled.section`
width: 95vw; width: 95vw;
display: flex;
flex-wrap: wrap; display: grid;
grid-auto-flow: dense;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 1rem;
margin: auto;
width: calc(100% - 2rem);
`; `;
const Header = styled(Card)` const Header = styled(Card)`
margin: 1rem; margin: 1rem;
display: flex; display: flex;
flex-wrap: wrap;
align-items: baseline; align-items: baseline;
justify-content: space-between; justify-content: space-between;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
@ -45,6 +55,8 @@ interface ResultsType {
const Results = (): JSX.Element => { const Results = (): JSX.Element => {
const [ results, setResults ] = useState<ResultsType>({}); const [ results, setResults ] = useState<ResultsType>({});
const [ locationResults, setLocationResults ] = useState<ServerLocation>(); const [ locationResults, setLocationResults ] = useState<ServerLocation>();
const [ whoIsResults, setWhoIsResults ] = useState<Whois>();
const [ technologyResults, setTechnologyResults ] = useState<TechnologyGroup[]>();
const [ ipAddress, setIpAddress ] = useState<undefined | string>(undefined); 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();
@ -62,7 +74,6 @@ const Results = (): JSX.Element => {
const fetchIpAddress = () => { const fetchIpAddress = () => {
fetch(`/find-url-ip?address=${address}`) fetch(`/find-url-ip?address=${address}`)
.then(function(response) { .then(function(response) {
console.log(response);
response.json().then(jsonData => { response.json().then(jsonData => {
console.log('Get IP Address', jsonData); console.log('Get IP Address', jsonData);
setIpAddress(jsonData.ip); setIpAddress(jsonData.ip);
@ -107,6 +118,7 @@ const Results = (): JSX.Element => {
fetch(`https://api.shodan.io/shodan/host/${ipAddress}?key=${apiKey}`) fetch(`https://api.shodan.io/shodan/host/${ipAddress}?key=${apiKey}`)
.then(response => response.json()) .then(response => response.json())
.then(response => { .then(response => {
console.log(response);
if (!response.error) applyShodanResults(response) if (!response.error) applyShodanResults(response)
}) })
.catch(err => console.error(err)); .catch(err => console.error(err));
@ -117,11 +129,29 @@ const Results = (): JSX.Element => {
fetchShodanData(); fetchShodanData();
} }
}, [ipAddress]); }, [ipAddress]);
/* Get BuiltWith tech stack */
useEffect(() => {
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 => {
console.log(response);
setTechnologyResults(makeTechnologies(response));
});
}, [address]);
/* Get WhoIs info for a given domain name */ /* Get WhoIs info for a given domain name */
useEffect(() => { useEffect(() => {
const applyWhoIsResults = (response: any) => { const applyWhoIsResults = (response: any) => {
console.log('WhoIs Response', response); const whoIsResults: Whois = {
created: response.date_created,
expires: response.date_expires,
updated: response.date_updated,
nameservers: response.nameservers,
};
setWhoIsResults(whoIsResults);
} }
const fetchWhoIsData = () => { const fetchWhoIsData = () => {
const apiKey = keys.whoApi; const apiKey = keys.whoApi;
@ -148,6 +178,8 @@ const Results = (): JSX.Element => {
{ locationResults && <ServerLocationCard {...locationResults} />} { locationResults && <ServerLocationCard {...locationResults} />}
{ results.serverInfo && <ServerInfoCard {...results.serverInfo} />} { results.serverInfo && <ServerInfoCard {...results.serverInfo} />}
{ results.hostNames && <HostNamesCard hosts={results.hostNames} />} { results.hostNames && <HostNamesCard hosts={results.hostNames} />}
{ whoIsResults && <WhoIsCard {...whoIsResults} />}
{ technologyResults && <BuiltWithCard technologies={technologyResults} />}
</ResultsContent> </ResultsContent>
</ResultsOuter> </ResultsOuter>
); );

View File

@ -1,7 +1,8 @@
const keys = { const keys = {
shodan: process.env.SHODAN_API_KEY, shodan: process.env.REACT_APP_SHODAN_API_KEY,
whoApi: process.env.WHO_API_KEY, whoApi: process.env.REACT_APP_WHO_API_KEY,
builtWith: process.env.REACT_APP_BUILT_WITH_API_KEY,
}; };
export default keys; export default keys;

View File

@ -19,6 +19,13 @@ export interface ServerLocation {
countryPopulation: number, countryPopulation: number,
}; };
export interface Whois {
created: string,
expires: string,
updated: string,
nameservers: string[],
}
export const getLocation = (response: any): ServerLocation => { export const getLocation = (response: any): ServerLocation => {
return { return {
city: response.city, city: response.city,
@ -48,6 +55,8 @@ export interface ServerInfo {
asn: string, asn: string,
isp: string, isp: string,
os?: string, os?: string,
ip?: string,
ports?: string,
}; };
export const getServerInfo = (response: any): ServerInfo => { export const getServerInfo = (response: any): ServerInfo => {
@ -56,6 +65,8 @@ export const getServerInfo = (response: any): ServerInfo => {
asn: response.asn, asn: response.asn,
isp: response.isp, isp: response.isp,
os: response.os, os: response.os,
ip: response.ip_str,
ports: response.ports.toString(),
}; };
}; };
@ -67,17 +78,37 @@ export interface HostNames {
export const getHostNames = (response: any): HostNames => { export const getHostNames = (response: any): HostNames => {
const { hostnames, domains } = response; const { hostnames, domains } = response;
const results: HostNames = { const results: HostNames = {
domains: [], domains: domains || [],
hostnames: [], hostnames: hostnames || [],
}; };
if (!hostnames || !domains) return results;
hostnames.forEach((host: string) => {
if (domains.includes(host)) {
results.domains.push(host);
} else {
results.hostnames.push(host);
}
});
return results; return results;
}; };
export interface Technology {
Categories?: string[];
Parent?: string;
Name: string;
Description: string;
Link: string;
Tag: string;
FirstDetected: number;
LastDetected: number;
IsPremium: string;
}
export interface TechnologyGroup {
tag: string;
technologies: Technology[];
}
export const makeTechnologies = (response: any): TechnologyGroup[] => {
let flatArray = response.Results[0].Result.Paths
.reduce((accumulator: any, obj: any) => accumulator.concat(obj.Technologies), []);
let technologies = flatArray.reduce((groups: any, item: any) => {
let tag = item.Tag;
if (!groups[tag]) groups[tag] = [];
groups[tag].push(item);
return groups;
}, {});
return technologies;
};