From dcd03d8a3f94505c53e667a4b967a29b698c5728 Mon Sep 17 00:00:00 2001 From: Ernst Widerberg Date: Wed, 30 Mar 2022 12:53:34 +0200 Subject: Merge redesign (refs/archive/redesign-2022-03-30) --- src/components/App.js | 12 +-- src/components/List.js | 148 ------------------------------------ src/components/ListItem.js | 41 ++++++++++ src/components/ListView.js | 153 ++++++++++++++++++++++++++++++++++++++ src/components/ObjectComponent.js | 135 --------------------------------- src/components/ObjectView.js | 56 -------------- src/components/ScanDetail.js | 118 +++++++++++++++++++++++++++++ src/components/ScanView.js | 55 ++++++++++++++ src/styles/main.css | 71 +++++++++++------- 9 files changed, 416 insertions(+), 373 deletions(-) delete mode 100644 src/components/List.js create mode 100644 src/components/ListItem.js create mode 100644 src/components/ListView.js delete mode 100644 src/components/ObjectComponent.js delete mode 100644 src/components/ObjectView.js create mode 100644 src/components/ScanDetail.js create mode 100644 src/components/ScanView.js (limited to 'src') diff --git a/src/components/App.js b/src/components/App.js index cc89ca8..8eda93e 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -9,9 +9,9 @@ import { import Error from "./Error"; import Header from "./Header"; -import List from "./List"; +import ListView from "./ListView"; import Login from "./Login"; -import ObjectView from "./ObjectView"; +import ScanView from "./ScanView"; import "../styles/main.css"; @@ -63,13 +63,13 @@ class App extends React.Component {
- - @@ -80,9 +80,9 @@ class App extends React.Component { } } -function MakeObjectView(props) { +function MakeScanView(props) { let { id } = useParams(); - return ; + return ; } export default App; diff --git a/src/components/List.js b/src/components/List.js deleted file mode 100644 index acfc369..0000000 --- a/src/components/List.js +++ /dev/null @@ -1,148 +0,0 @@ -import React from "react"; - -import Pagination from "@mui/material/Pagination"; - -import ObjectComponent from "./ObjectComponent"; -import SearchForm from "./SearchForm"; - -class List extends React.Component { - constructor(props) { - super(props); - this.state = { - objects: [], - filter: { - field: null, - value: null - }, - page: 1, - totalPages: 1 - }; - - this.filter = this.filter.bind(this); - this.filterString = this.filterString.bind(this); - this.getData = this.getData.bind(this); - this.queryString = this.queryString.bind(this); - this.setPage = this.setPage.bind(this); - } - - componentDidMount() { - this.getData(); - } - - // - // Helpers - // - - filterString() { - return this.state.filter.field == null || - this.state.filter.value == null - ? null - : this.state.filter.field + "=" + this.state.filter.value; - } - - queryString() { - return [ - `limit=${window.injectedEnv.PER_PAGE}`, - `skip=${(this.state.page - 1) * window.injectedEnv.PER_PAGE}`, - this.filterString() - ] - .filter(x => x !== null) - .join("&"); - } - - // Fetch data from external source, update state - getData() { - fetch( - window.injectedEnv.COLLECTOR_URL + - "/sc/v0/get?" + - this.queryString(), - { - headers: { - Authorization: "Bearer " + this.props.token - } - } - ) - // TODO: Look at `status` or return code or both? - .then(resp => { - if (resp.status !== 200) - throw new Error( - `Unexpected HTTP response code from soc_collector: ${resp.status} ${resp.statusText}` - ); - this.setState({ - totalPages: parseInt(resp.headers.get("X-Total-Count")) - }); - return resp.json(); - }) - .then(json => { - if (json.status != "success") - throw new Error( - `Unexpected status from soc_collector: ${json.status}` - ); - this.setState({ - objects: json.docs - }); - }) - .catch(e => this.props.setError(e)); - } - - // - // Event handlers - // - - filter(field, value) { - this.setState( - { - filter: { - field: field, - value: value - }, - page: 1 - }, - this.getData - ); - } - - setPage(event, value) { - this.setState({ page: value }, () => { - this.getData(); - window.scrollTo(0, 0); - }); - } - - render() { - return ( -
-
-
- -
-
- {this.state.objects.map(data => { - return ( - - ); - })} -
- -
- ); - } -} - -export default List; diff --git a/src/components/ListItem.js b/src/components/ListItem.js new file mode 100644 index 0000000..661f8c2 --- /dev/null +++ b/src/components/ListItem.js @@ -0,0 +1,41 @@ +import React from "react"; + +import Card from "@mui/material/Card"; + +class ListItem extends React.Component { + render() { + return ( + (window.location = `/${this.props._id}`)} + > + {this.props.timestamp_in_utc} + + {this.props.ip}:{this.props.port} + + {this.props.domain} + {this.props.system_name} + + + {this.props.cve} + + + + ); + } +} + +export default ListItem; diff --git a/src/components/ListView.js b/src/components/ListView.js new file mode 100644 index 0000000..f68efe4 --- /dev/null +++ b/src/components/ListView.js @@ -0,0 +1,153 @@ +import React from "react"; + +import Pagination from "@mui/material/Pagination"; + +import ListItem from "./ListItem"; +import SearchForm from "./SearchForm"; + +class ListView extends React.Component { + constructor(props) { + super(props); + this.state = { + scans: [], + filter: { + field: null, + value: null + }, + page: 1, + totalPages: 1 + }; + + this.filter = this.filter.bind(this); + this.filterString = this.filterString.bind(this); + this.getData = this.getData.bind(this); + this.queryString = this.queryString.bind(this); + this.setPage = this.setPage.bind(this); + } + + componentDidMount() { + this.getData(); + } + + // + // Helpers + // + + filterString() { + return this.state.filter.field == null || + this.state.filter.value == null + ? null + : this.state.filter.field + "=" + this.state.filter.value; + } + + queryString() { + return [ + `limit=${window.injectedEnv.PER_PAGE}`, + `skip=${(this.state.page - 1) * window.injectedEnv.PER_PAGE}`, + this.filterString() + ] + .filter(x => x !== null) + .join("&"); + } + + // Fetch data from external source, update state + getData() { + fetch( + window.injectedEnv.COLLECTOR_URL + + "/sc/v0/get?" + + this.queryString(), + { + headers: { + Authorization: "Bearer " + this.props.token + } + } + ) + // TODO: Look at `status` or return code or both? + .then(resp => { + if (resp.status !== 200) + throw new Error( + `Unexpected HTTP response code from soc_collector: ${resp.status} ${resp.statusText}` + ); + this.setState({ + totalPages: parseInt(resp.headers.get("X-Total-Count")) + }); + return resp.json(); + }) + .then(json => { + if (json.status != "success") + throw new Error( + `Unexpected status from soc_collector: ${json.status}` + ); + this.setState({ + scans: json.docs + }); + }) + .catch(e => this.props.setError(e)); + } + + // + // Event handlers + // + + filter(field, value) { + this.setState( + { + filter: { + field: field, + value: value + }, + page: 1 + }, + this.getData + ); + } + + setPage(event, value) { + this.setState({ page: value }, () => { + this.getData(); + window.scrollTo(0, 0); + }); + } + + render() { + return ( +
+
+
+ +
+ + + {this.state.scans + .map(scan => + scan.result.map(res => ( + + )) + ) + .flat()} + +
+ +
+ ); + } +} + +export default ListView; diff --git a/src/components/ObjectComponent.js b/src/components/ObjectComponent.js deleted file mode 100644 index 2ae0b6e..0000000 --- a/src/components/ObjectComponent.js +++ /dev/null @@ -1,135 +0,0 @@ -import React from "react"; - -import Accordion from "@mui/material/Accordion"; -import AccordionDetails from "@mui/material/AccordionDetails"; -import AccordionSummary from "@mui/material/AccordionSummary"; -import Alert from "@mui/material/Alert"; -import Card from "@mui/material/Card"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; - -class ObjectComponent extends React.Component { - render() { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Domain{this.props.domain}
Endpoint{`${this.props.ip}:${this.props.port}`}
Hostname{this.props.ptr}
Owner{this.props.whois_description}
ASN{`${this.props.asn} (${this.props.asn_country_code})`}
Abuse mail{this.props.abuse_mail}
Scan finished at{this.props.timestamp_in_utc}
-
- - ); - } -} - -function Details(props) { - let content = ( - <> - {props.user_presentation.description && ( - - {props.user_presentation.description} - - )} - - - ); - if (props.summary) { - return ( -
- - } - > - {content} - -
- ); - } - return content; -} - -function UserPresentation(props) { - return ( -
- {Object.entries(props.data).map( - ([key, { data, display_name, description }]) => ( - - ) - )} -
- ); -} - -function UserPresentationElement(props) { - return ( - - {props.display_name}: {props.data.toString()} - {props.description && ( - - {props.description} - - )} - - ); -} - -function GenericTable(props) { - return ( - - - {Object.entries(props.data).map(([key, value]) => { - return ( - - - - - ); - })} - -
{key}{value}
- ); -} - -export default ObjectComponent; diff --git a/src/components/ObjectView.js b/src/components/ObjectView.js deleted file mode 100644 index 76479c2..0000000 --- a/src/components/ObjectView.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from "react"; - -import ObjectComponent from "./ObjectComponent"; - -class ObjectView extends React.Component { - constructor(props) { - super(props); - this.state = { - object: null - }; - - this.getData = this.getData.bind(this); - } - - componentDidMount() { - this.getData(); - } - - getData() { - fetch(`${window.injectedEnv.COLLECTOR_URL}/sc/v0/get/${this.props.id}`, { - headers: { - Authorization: "Bearer " + this.props.token - } - }) - // TODO: Look at `status` or return code or both? - .then(resp => { - if (resp.status !== 200) - throw new Error( - `Unexpected HTTP response code from soc_collector: ${resp.status} ${resp.statusText}` - ); - return resp.json(); - }) - .then(json => { - if (json.status != "success") - throw new Error( - `Unexpected status from soc_collector: ${json.status}` - ); - this.setState({ - object: json.docs - }); - }) - .catch(e => this.props.setError(e)); - } - - render() { - return ( -
- {this.state.object === null ? null : ( - - )} -
- ); - } -} - -export default ObjectView; diff --git a/src/components/ScanDetail.js b/src/components/ScanDetail.js new file mode 100644 index 0000000..a5f6d16 --- /dev/null +++ b/src/components/ScanDetail.js @@ -0,0 +1,118 @@ +import React from "react"; + +import Alert from "@mui/material/Alert"; +import Card from "@mui/material/Card"; + +class ScanDetail extends React.Component { + render() { + return ( + + +

General info

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Domain{this.props.domain}
Endpoint{`${this.props.ip}:${this.props.port}`}
Hostname{this.props.ptr}
Owner{this.props.whois_description}
ASN{`${this.props.asn} (${this.props.asn_country_code})`}
Abuse mail{this.props.abuse_mail}
+ + {this.props.user_presentation.description && ( + <> +
+ + {this.props.user_presentation.description} + + + )} + +

Custom info

+
+ +

Latest scan | {this.props.timestamp_in_utc}

+
+ {this.props.result + .sort((a, b) => (a.vulnerable ? -1 : 1)) + .map(cve => ( + + ))} +
+ + ); + } +} + +function Details(props) { + return ( + <> + + + ); +} + +function UserPresentation(props) { + return ( + + {Object.entries(props.data).map( + ([key, { data, display_name, description }]) => ( + + ) + )} +
+ ); +} + +function UserPresentationElement(props) { + return ( + + {props.display_name} + {props.data.toString()} + {props.description} + + ); +} + +function CVE(props) { + return ( + + {props.cve} + + ); +} + +export default ScanDetail; diff --git a/src/components/ScanView.js b/src/components/ScanView.js new file mode 100644 index 0000000..aefd287 --- /dev/null +++ b/src/components/ScanView.js @@ -0,0 +1,55 @@ +import React from "react"; + +import ScanDetail from "./ScanDetail"; + +class ScanView extends React.Component { + constructor(props) { + super(props); + this.state = { + object: null + }; + + this.getData = this.getData.bind(this); + } + + componentDidMount() { + this.getData(); + } + + getData() { + fetch( + `${window.injectedEnv.COLLECTOR_URL}/sc/v0/get/${this.props.id}`, + { + headers: { + Authorization: "Bearer " + this.props.token + } + } + ) + // TODO: Look at `status` or return code or both? + .then(resp => { + if (resp.status !== 200) + throw new Error( + `Unexpected HTTP response code from soc_collector: ${resp.status} ${resp.statusText}` + ); + return resp.json(); + }) + .then(json => { + if (json.status != "success") + throw new Error( + `Unexpected status from soc_collector: ${json.status}` + ); + this.setState({ + object: json.docs + }); + }) + .catch(e => this.props.setError(e)); + } + + render() { + return this.state.object === null ? null : ( + + ); + } +} + +export default ScanView; diff --git a/src/styles/main.css b/src/styles/main.css index 606c6d2..0b01a70 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -1,3 +1,5 @@ +/* App */ + body { background: #fff; font-size: 14px; @@ -53,45 +55,39 @@ a:visited { color: #9c9c9c; } -/* Object */ +/* ScanView */ -.object { +.scan-detail { padding: 2em; margin: 2em; - width: 40em; + width: 50em; margin-left: auto; margin-right: auto; } -.object.good { - background-color: green; -} - -.object.bad { - background-color: red; -} - -.object .id { +.scan-detail .id { float: right; font-family: monospace; } -.object td { +.scan-detail td { padding-right: 2em; } -.object .MuiAccordionSummary-content { - display: none !important; -} - -.object .MuiAccordion-root { - margin-top: 2em !important; +.scan-detail .cve { + background-color: #c6ff85; + border: 3px solid #62b800; + padding: 0.5em; + margin-top: 0.5em; + text-align: center; } -.object .MuiAccordionSummary-root { +.scan-detail .cve.vulnerable { + background-color: #ff8585; + border: 3px solid #f74343; } -/* List */ +/* ListView */ #list-container > #controls { display: flex; @@ -109,13 +105,32 @@ a:visited { display: flex; } -#list-container > #main > .MuiCard-root:nth-child(odd), -#list-container > #main > .MuiCard-root:nth-child(odd) .MuiAccordion-root, -#list-container - > #main - > .MuiCard-root:nth-child(odd) - .user-presentation-element { - background-color: #fcfcfc; +#list-container > #main { + width: 80em; + margin-left: auto; + margin-right: auto; + border-collapse: collapse; +} + +.list-item td { + padding-left: 2em; + padding-right: 2em; + margin: 0; + border-top: 1px solid grey; + border-bottom: 1px solid grey; +} + +.list-item .cve { + background-color: #c6ff85; + border: 3px solid #62b800; + padding: 0.5em; + margin: 0.5em; + text-align: center; +} + +.list-item .cve.vulnerable { + background-color: #ff8585; + border: 3px solid #f74343; } /* Login */ -- cgit v1.1