aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components')
-rw-r--r--frontend/src/components/Header.js28
-rw-r--r--frontend/src/components/Header.scss17
-rw-r--r--frontend/src/components/Timeline.js2
-rw-r--r--frontend/src/components/pages/ConfigurationPage.js1
-rw-r--r--frontend/src/components/pages/ConfigurationPage.scss10
-rw-r--r--frontend/src/components/pages/MainPage.js72
-rw-r--r--frontend/src/components/pages/MainPage.scss20
-rw-r--r--frontend/src/components/pages/common.scss16
-rw-r--r--frontend/src/components/panels/StreamsPane.js40
-rw-r--r--frontend/src/components/panels/StreamsPane.scss12
10 files changed, 128 insertions, 90 deletions
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index c46d768..4db05a5 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -46,19 +46,17 @@ class Header extends Component {
render() {
return (
- <header className="header container-fluid">
- <div className="row">
- <div className={classNames({"col-auto": this.props.configured, "col": !this.props.configured})}>
- <h1 className="header-title type-wrap">
- <Link to="/">
- <span style={{whiteSpace: "pre"}} ref={(el) => {
- this.el = el;
- }}/>
- </Link>
- </h1>
- </div>
+ <header className={classNames("header", {"configured": this.props.configured})}>
+ <div className="header-content">
+ <h1 className="header-title type-wrap">
+ <Link to="/">
+ <span style={{whiteSpace: "pre"}} ref={(el) => {
+ this.el = el;
+ }}/>
+ </Link>
+ </h1>
- {this.props.configured && <div className="col-auto">
+ {this.props.configured &&
<div className="filters-bar">
<StringConnectionsFilter filterName="service_port"
defaultFilterValue="all_ports"
@@ -71,9 +69,9 @@ class Header extends Component {
<ExitSearchFilter/>
<AdvancedFilters onClick={this.props.onOpenFilters}/>
</div>
- </div>}
+ }
- {this.props.configured && <div className="col">
+ {this.props.configured &&
<div className="header-buttons">
<Link to={"/searches" + this.props.location.search}>
<ButtonField variant="pink" name="searches" bordered/>
@@ -91,7 +89,7 @@ class Header extends Component {
<ButtonField variant="blue" name="config" bordered/>
</Link>
</div>
- </div>}
+ }
</div>
</header>
);
diff --git a/frontend/src/components/Header.scss b/frontend/src/components/Header.scss
index fff28e6..c6a88b7 100644
--- a/frontend/src/components/Header.scss
+++ b/frontend/src/components/Header.scss
@@ -1,16 +1,18 @@
@import "../colors";
.header {
- height: 80px;
- padding: 15px 30px;
+ padding: 15px;
- > .row {
+ .header-content {
+ display: flex;
+ width: 100%;
+ padding: 0 10px;
background-color: $color-primary-0;
}
.header-title {
width: 200px;
- margin: 5px 0 5px -5px;
+ margin: 5px 0 5px 0;
}
.header-buttons {
@@ -24,6 +26,7 @@
}
.filters-bar {
+ flex: 1;
padding: 3px 0;
.filter,
@@ -38,4 +41,10 @@
border-radius: 5px;
}
}
+
+ @media screen and (max-width: 1530px) {
+ &.configured .header-title {
+ display: none;
+ }
+ }
}
diff --git a/frontend/src/components/Timeline.js b/frontend/src/components/Timeline.js
index 9ecbd80..5443a3b 100644
--- a/frontend/src/components/Timeline.js
+++ b/frontend/src/components/Timeline.js
@@ -256,7 +256,7 @@ class Timeline extends Component {
enablePanZoom={true} utc={false}
onTimeRangeChanged={this.handleTimeRangeChange}>
- <ChartRow height="125">
+ <ChartRow height={this.props.height - 70}>
<YAxis id="axis1" hideAxisLine
min={this.aggregateSeries("min")}
max={this.aggregateSeries("max")} width="35" type="linear" transition={300}/>
diff --git a/frontend/src/components/pages/ConfigurationPage.js b/frontend/src/components/pages/ConfigurationPage.js
index 8f9b68b..c8646fb 100644
--- a/frontend/src/components/pages/ConfigurationPage.js
+++ b/frontend/src/components/pages/ConfigurationPage.js
@@ -28,7 +28,6 @@ import TextField from "../fields/TextField";
import Header from "../Header";
import LinkPopover from "../objects/LinkPopover";
import "../panels/common.scss";
-import "./common.scss";
import "./ConfigurationPage.scss";
class ConfigurationPage extends Component {
diff --git a/frontend/src/components/pages/ConfigurationPage.scss b/frontend/src/components/pages/ConfigurationPage.scss
index 4254547..6ff933c 100644
--- a/frontend/src/components/pages/ConfigurationPage.scss
+++ b/frontend/src/components/pages/ConfigurationPage.scss
@@ -1,6 +1,7 @@
@import "../../colors";
.configuration-page {
+ height: 100vh;
background-color: $color-primary-0;
.header-title {
@@ -10,16 +11,10 @@
.configuration-pane {
display: flex;
justify-content: center;
- height: 100%;
- padding-top: 100px;
.section-content {
- background-color: $color-primary-3;
margin-top: 15px;
- }
-
- .section-table table {
- background-color: red !important;
+ background-color: $color-primary-3;
}
.section-footer {
@@ -27,4 +22,3 @@
}
}
}
-
diff --git a/frontend/src/components/pages/MainPage.js b/frontend/src/components/pages/MainPage.js
index c4dcd20..3bf8065 100644
--- a/frontend/src/components/pages/MainPage.js
+++ b/frontend/src/components/pages/MainPage.js
@@ -16,6 +16,8 @@
*/
import React, {Component} from "react";
+import {ReflexContainer, ReflexElement, ReflexSplitter} from "react-reflex";
+import "react-reflex/styles.css"
import {Route, Switch} from "react-router-dom";
import Filters from "../dialogs/Filters";
import Header from "../Header";
@@ -27,12 +29,22 @@ import SearchPane from "../panels/SearchPane";
import ServicesPane from "../panels/ServicesPane";
import StreamsPane from "../panels/StreamsPane";
import Timeline from "../Timeline";
-import "./common.scss";
import "./MainPage.scss";
class MainPage extends Component {
- state = {};
+ state = {
+ timelineHeight: 210
+ };
+
+ handleTimelineResize = (e) => {
+ if (this.timelineTimeoutHandle) {
+ clearTimeout(this.timelineTimeoutHandle);
+ }
+
+ this.timelineTimeoutHandle = setTimeout(() =>
+ this.setState({timelineHeight: e.domElement.clientHeight}), 100);
+ };
render() {
let modal;
@@ -41,34 +53,42 @@ class MainPage extends Component {
}
return (
- <div className="page main-page">
- <div className="page-header">
- <Header onOpenFilters={() => this.setState({filterWindowOpen: true})} configured={true}/>
+ <ReflexContainer orientation="horizontal" className="page main-page">
+ <div className="fuck-css">
+ <ReflexElement className="page-header">
+ <Header onOpenFilters={() => this.setState({filterWindowOpen: true})} configured={true}/>
+ {modal}
+ </ReflexElement>
</div>
- <div className="page-content">
- <div className="pane connections-pane">
- <Connections onSelected={(c) => this.setState({selectedConnection: c})}/>
- </div>
- <div className="pane details-pane">
- <Switch>
- <Route path="/searches" children={<SearchPane/>}/>
- <Route path="/pcaps" children={<PcapsPane/>}/>
- <Route path="/rules" children={<RulesPane/>}/>
- <Route path="/services" children={<ServicesPane/>}/>
- <Route exact path="/connections/:id"
- children={<StreamsPane connection={this.state.selectedConnection}/>}/>
- <Route children={<MainPane version={this.props.version}/>}/>
- </Switch>
- </div>
+ <ReflexElement className="page-content" flex={1}>
+ <ReflexContainer orientation="vertical" className="page-content">
+ <ReflexElement className="pane connections-pane">
+ <Connections onSelected={(c) => this.setState({selectedConnection: c})}/>
+ </ReflexElement>
- {modal}
- </div>
+ <ReflexSplitter/>
- <div className="page-footer">
- <Timeline/>
- </div>
- </div>
+ <ReflexElement className="pane details-pane">
+ <Switch>
+ <Route path="/searches" children={<SearchPane/>}/>
+ <Route path="/pcaps" children={<PcapsPane/>}/>
+ <Route path="/rules" children={<RulesPane/>}/>
+ <Route path="/services" children={<ServicesPane/>}/>
+ <Route exact path="/connections/:id"
+ children={<StreamsPane connection={this.state.selectedConnection}/>}/>
+ <Route children={<MainPane version={this.props.version}/>}/>
+ </Switch>
+ </ReflexElement>
+ </ReflexContainer>
+ </ReflexElement>
+
+ <ReflexSplitter propagate={true}/>
+
+ <ReflexElement className="page-footer" onResize={this.handleTimelineResize}>
+ <Timeline height={this.state.timelineHeight}/>
+ </ReflexElement>
+ </ReflexContainer>
);
}
}
diff --git a/frontend/src/components/pages/MainPage.scss b/frontend/src/components/pages/MainPage.scss
index 4ca54c0..8d1fa96 100644
--- a/frontend/src/components/pages/MainPage.scss
+++ b/frontend/src/components/pages/MainPage.scss
@@ -1,25 +1,23 @@
@import "../../colors";
.main-page {
+ height: 100vh !important;
+
+ .page-footer {
+ overflow: hidden !important;
+ min-height: 210px;
+ }
+
.page-content {
- display: flex;
- flex: 1;
- padding: 0 15px;
- background-color: $color-primary-2;
+ padding: 0 7.5px;
.connections-pane {
- flex: 1 0;
margin-right: 7.5px;
}
.details-pane {
- position: relative;
- flex: 1 1;
+ overflow: hidden;
margin-left: 7.5px;
}
}
-
- .page-footer {
- flex: 0;
- }
}
diff --git a/frontend/src/components/pages/common.scss b/frontend/src/components/pages/common.scss
deleted file mode 100644
index fcf5c20..0000000
--- a/frontend/src/components/pages/common.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-.page {
- position: relative;
- display: flex;
- flex-direction: column;
- height: 100vh;
-
- .page-header,
- .page-footer {
- flex: 0;
- }
-
- .page-content {
- overflow: hidden;
- flex: 1;
- }
-}
diff --git a/frontend/src/components/panels/StreamsPane.js b/frontend/src/components/panels/StreamsPane.js
index 9470d7d..4c16cf1 100644
--- a/frontend/src/components/panels/StreamsPane.js
+++ b/frontend/src/components/panels/StreamsPane.js
@@ -21,12 +21,14 @@ import {Row} from "react-bootstrap";
import ReactJson from "react-json-view";
import backend from "../../backend";
import log from "../../log";
+import rules from "../../model/rules";
import {downloadBlob, getHeaderValue} from "../../utils";
import ButtonField from "../fields/ButtonField";
import ChoiceField from "../fields/ChoiceField";
import MessageAction from "../objects/MessageAction";
import "./StreamsPane.scss";
+const reactStringReplace = require("react-string-replace");
const classNames = require("classnames");
class StreamsPane extends Component {
@@ -77,11 +79,9 @@ class StreamsPane extends Component {
};
tryParseConnectionMessage = (connectionMessage) => {
+ const isClient = connectionMessage["from_client"];
if (connectionMessage.metadata == null) {
- return connectionMessage.content;
- }
- if (connectionMessage["is_metadata_continuation"]) {
- return <span style={{"fontSize": "12px"}}>**already parsed in previous messages**</span>;
+ return this.highlightRules(connectionMessage.content, isClient);
}
let unrollMap = (obj) => obj == null ? null : Object.entries(obj).map(([key, value]) =>
@@ -96,7 +96,7 @@ class StreamsPane extends Component {
return <span className="type-http-request">
<p style={{"marginBottom": "7px"}}><strong>{m.method}</strong> {url} {m.protocol}</p>
{unrollMap(m.headers)}
- <div style={{"margin": "20px 0"}}>{m.body}</div>
+ <div style={{"margin": "20px 0"}}>{this.highlightRules(m.body, isClient)}</div>
{unrollMap(m.trailers)}
</span>;
case "http-response":
@@ -108,20 +108,44 @@ class StreamsPane extends Component {
body = <ReactJson src={json} theme="grayscale" collapsed={false} displayDataTypes={false}/>;
} catch (e) {
log.error(e);
+ body = m.body;
}
}
return <span className="type-http-response">
<p style={{"marginBottom": "7px"}}>{m.protocol} <strong>{m.status}</strong></p>
{unrollMap(m.headers)}
- <div style={{"margin": "20px 0"}}>{body}</div>
+ <div style={{"margin": "20px 0"}}>{this.highlightRules(body, isClient)}</div>
{unrollMap(m.trailers)}
</span>;
default:
- return connectionMessage.content;
+ return this.highlightRules(connectionMessage.content, isClient);
}
};
+ highlightRules = (content, isClient) => {
+ let streamContent = content;
+ this.props.connection["matched_rules"].forEach(ruleId => {
+ const rule = rules.ruleById(ruleId);
+ rule.patterns.forEach(pattern => {
+ if ((!isClient && pattern.direction === 1) || (isClient && pattern.direction === 2)) {
+ return;
+ }
+ let flags = "";
+ pattern["caseless"] && (flags += "i");
+ pattern["dot_all"] && (flags += "s");
+ pattern["multi_line"] && (flags += "m");
+ pattern["unicode_property"] && (flags += "u");
+ const regex = new RegExp("(" + pattern.regex + ")", flags);
+ streamContent = reactStringReplace(streamContent, regex, (match, i) => (
+ <span key={i} className="matched-occurrence" style={{"backgroundColor": rule.color}}>{match}</span>
+ ));
+ });
+ });
+
+ return streamContent;
+ };
+
connectionsActions = (connectionMessage) => {
if (!connectionMessage.metadata) {
return null;
@@ -187,7 +211,7 @@ class StreamsPane extends Component {
};
const content = this.state.messages || [];
- let payload = content.map((c, i) =>
+ let payload = content.filter((c) => !c["is_metadata_continuation"]).map((c, i) =>
<div key={`content-${i}`}
className={classNames("connection-message", c["from_client"] ? "from-client" : "from-server")}>
<div className="connection-message-header container-fluid">
diff --git a/frontend/src/components/panels/StreamsPane.scss b/frontend/src/components/panels/StreamsPane.scss
index 1f641f3..3857068 100644
--- a/frontend/src/components/panels/StreamsPane.scss
+++ b/frontend/src/components/panels/StreamsPane.scss
@@ -18,6 +18,12 @@
&:hover::-webkit-scrollbar-thumb {
background: $color-secondary-2;
}
+
+ .matched-occurrence {
+ font-weight: 500;
+ color: $color-primary-4;
+ border-radius: 2px;
+ }
}
.connection-message {
@@ -32,8 +38,11 @@
.connection-message-info {
font-size: 11px;
+ overflow: hidden;
margin-top: 6px;
margin-left: -10px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
.connection-message-actions {
@@ -97,8 +106,11 @@
.header-info {
font-size: 12px;
+ overflow: hidden;
padding-top: 7px;
padding-left: 25px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
.header-actions {