- {/* */}
+
diff --git a/frontend/src/components/Timeline.js b/frontend/src/components/Timeline.js
index 6b8806f..bc42a01 100644
--- a/frontend/src/components/Timeline.js
+++ b/frontend/src/components/Timeline.js
@@ -35,8 +35,12 @@ import log from "../log";
import dispatcher from "../dispatcher";
const minutes = 60 * 1000;
+const _ = require('lodash');
const classNames = require('classnames');
+const leftSelectionPaddingMultiplier = 24;
+const rightSelectionPaddingMultiplier = 8;
+
class Timeline extends Component {
state = {
@@ -50,25 +54,30 @@ class Timeline extends Component {
this.selectionTimeout = null;
}
- filteredPort = () => {
+ additionalFilters = () => {
const urlParams = new URLSearchParams(this.props.location.search);
- return urlParams.get("service_port");
+ if (this.state.metric === "matched_rules") {
+ return urlParams.getAll("matched_rules") || [];
+ } else {
+ return urlParams.get("service_port");
+ }
};
componentDidMount() {
- const filteredPort = this.filteredPort();
- this.setState({filteredPort});
- this.loadStatistics(this.state.metric, filteredPort).then(() => log.debug("Statistics loaded after mount"));
+ const additionalFilters = this.additionalFilters();
+ this.setState({filters: additionalFilters});
+ this.loadStatistics(this.state.metric, additionalFilters).then(() => log.debug("Statistics loaded after mount"));
dispatcher.register("connection_updates", payload => {
this.setState({
selection: new TimeRange(payload.from, payload.to),
});
+ this.adjustSelection();
});
dispatcher.register("notifications", payload => {
if (payload.event === "services.edit") {
- this.loadServices().then(() => log.debug("Services reloaded after notification update"));
+ this.loadServices().then(() => this.adjustSelection());
}
});
@@ -79,27 +88,48 @@ class Timeline extends Component {
}
componentDidUpdate(prevProps, prevState, snapshot) {
- const filteredPort = this.filteredPort();
- if (this.state.filteredPort !== filteredPort) {
- this.setState({filteredPort});
- this.loadStatistics(this.state.metric, filteredPort).then(() =>
- log.debug("Statistics reloaded after filtered port changes"));
+ const additionalFilters = this.additionalFilters();
+ const updateStatistics = () => {
+ this.setState({filters: additionalFilters});
+ this.loadStatistics(this.state.metric, additionalFilters).then(() =>
+ log.debug("Statistics reloaded after filters changes"));
+ };
+
+ if (this.state.metric === "matched_rules") {
+ if (!Array.isArray(this.state.filters) ||
+ !_.isEqual(_.sortBy(additionalFilters), _.sortBy(this.state.filters))) {
+ updateStatistics();
+ }
+ } else {
+ if (this.state.filters !== additionalFilters) {
+ updateStatistics();
+ }
}
}
- loadStatistics = async (metric, filteredPort) => {
+ loadStatistics = async (metric, filters) => {
const urlParams = new URLSearchParams();
urlParams.set("metric", metric);
- let services = await this.loadServices();
- if (filteredPort && services[filteredPort]) {
- const service = services[filteredPort];
- services = {};
- services[filteredPort] = service;
- }
+ let columns = [];
+ if (metric === "matched_rules") {
+ let rules = await this.loadRules();
+ filters.forEach(id => {
+ urlParams.append("matched_rules", id);
+ });
+ columns = rules.map(r => r.id);
+ } else {
+ let services = await this.loadServices();
+ const filteredPort = filters;
+ if (filteredPort && services[filters]) {
+ const service = services[filteredPort];
+ services = {};
+ services[filteredPort] = service;
+ }
- const ports = Object.keys(services);
- ports.forEach(s => urlParams.append("ports", s));
+ columns = Object.keys(services);
+ columns.forEach(port => urlParams.append("ports", port));
+ }
const metrics = (await backend.get("/api/statistics?" + urlParams)).json;
if (metrics.length === 0) {
@@ -109,8 +139,8 @@ class Timeline extends Component {
const zeroFilledMetrics = [];
const toTime = m => new Date(m["range_start"]).getTime();
let i = 0;
- for (let interval = toTime(metrics[0]); interval <= toTime(metrics[metrics.length - 1]); interval += minutes) {
- if (interval === toTime(metrics[i])) {
+ for (let interval = toTime(metrics[0]) - minutes; interval <= toTime(metrics[metrics.length - 1]) + minutes; interval += minutes) {
+ if (i < metrics.length && interval === toTime(metrics[i])) {
const m = metrics[i++];
m["range_start"] = new Date(m["range_start"]);
zeroFilledMetrics.push(m);
@@ -118,30 +148,31 @@ class Timeline extends Component {
const m = {};
m["range_start"] = new Date(interval);
m[metric] = {};
- ports.forEach(p => m[metric][p] = 0);
+ columns.forEach(c => m[metric][c] = 0);
zeroFilledMetrics.push(m);
}
}
const series = new TimeSeries({
name: "statistics",
- columns: ["time"].concat(ports),
- points: zeroFilledMetrics.map(m => [m["range_start"]].concat(ports.map(p => m[metric][p] || 0)))
+ columns: ["time"].concat(columns),
+ points: zeroFilledMetrics.map(m => [m["range_start"]].concat(columns.map(c =>
+ ((metric in m) && (m[metric] != null)) ? (m[metric][c] || 0) : 0
+ )))
});
const start = series.range().begin();
const end = series.range().end();
- start.setTime(start.getTime() - minutes);
- end.setTime(end.getTime() + minutes);
this.setState({
metric,
series,
timeRange: new TimeRange(start, end),
+ columns,
start,
end
});
- log.debug(`Loaded statistics for metric "${metric}" for services [${ports}]`);
+ log.debug(`Loaded statistics for metric "${metric}"`);
};
loadServices = async () => {
@@ -150,10 +181,22 @@ class Timeline extends Component {
return services;
};
+ loadRules = async () => {
+ const rules = (await backend.get("/api/rules")).json;
+ this.setState({rules});
+ return rules;
+ };
+
createStyler = () => {
- return styler(Object.keys(this.state.services).map(port => {
- return {key: port, color: this.state.services[port].color, width: 2};
- }));
+ if (this.state.metric === "matched_rules") {
+ return styler(this.state.rules.map(rule => {
+ return {key: rule.id, color: rule.color, width: 2};
+ }));
+ } else {
+ return styler(Object.keys(this.state.services).map(port => {
+ return {key: port, color: this.state.services[port].color, width: 2};
+ }));
+ }
};
handleTimeRangeChange = (timeRange) => {
@@ -179,6 +222,15 @@ class Timeline extends Component {
}, 1000);
};
+ adjustSelection = () => {
+ const seriesRange = this.state.series.range();
+ const selection = this.state.selection;
+ const delta = selection.end() - selection.begin();
+ const start = Math.max(selection.begin().getTime() - delta * leftSelectionPaddingMultiplier, seriesRange.begin().getTime());
+ const end = Math.min(selection.end().getTime() + delta * rightSelectionPaddingMultiplier, seriesRange.end().getTime());
+ this.setState({timeRange: new TimeRange(start, end)});
+ };
+
aggregateSeries = (func) => {
const values = this.state.series.columns().map(c => this.state.series[func](c));
return Math[func](...values);
@@ -207,7 +259,7 @@ class Timeline extends Component {
max={this.aggregateSeries("max")} width="35" type="linear" transition={300}/>
this.loadStatistics(metric, this.state.filteredPort)
+ "server_bytes_per_service", "duration_per_service", "matched_rules"]}
+ onChange={(metric) => this.loadStatistics(metric, this.state.filters)
.then(() => log.debug("Statistics loaded after metric changes"))}
value={this.state.metric}/>
diff --git a/frontend/src/components/Timeline.scss b/frontend/src/components/Timeline.scss
index db8d9c8..262da1e 100644
--- a/frontend/src/components/Timeline.scss
+++ b/frontend/src/components/Timeline.scss
@@ -12,6 +12,7 @@
position: absolute;
top: 5px;
right: 10px;
+ width: 180px;
}
&.pulse-timeline {
diff --git a/frontend/src/components/filters/BooleanConnectionsFilter.js b/frontend/src/components/filters/BooleanConnectionsFilter.js
index a9a420e..c611a0d 100644
--- a/frontend/src/components/filters/BooleanConnectionsFilter.js
+++ b/frontend/src/components/filters/BooleanConnectionsFilter.js
@@ -17,65 +17,49 @@
import React, {Component} from 'react';
import {withRouter} from "react-router-dom";
-import {Redirect} from "react-router";
import CheckField from "../fields/CheckField";
+import dispatcher from "../../dispatcher";
class BooleanConnectionsFilter extends Component {
- constructor(props) {
- super(props);
- this.state = {
- filterActive: "false"
- };
-
- this.filterChanged = this.filterChanged.bind(this);
- this.needRedirect = false;
- }
+ state = {
+ filterActive: "false"
+ };
componentDidMount() {
let params = new URLSearchParams(this.props.location.search);
this.setState({filterActive: this.toBoolean(params.get(this.props.filterName)).toString()});
+
+ this.connectionsFiltersCallback = payload => {
+ const name = this.props.filterName;
+ if (name in payload && this.state.filterActive !== payload[name]) {
+ this.setState({filterActive: payload[name]});
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
}
- componentDidUpdate(prevProps, prevState, snapshot) {
- let urlParams = new URLSearchParams(this.props.location.search);
- let externalActive = this.toBoolean(urlParams.get(this.props.filterName));
- let filterActive = this.toBoolean(this.state.filterActive);
- // if the filterActive state is changed by another component (and not by filterChanged func) and
- // the query string is not equals at the filterActive state, update the state of the component
- if (this.toBoolean(prevState.filterActive) === filterActive && filterActive !== externalActive) {
- this.setState({filterActive: externalActive.toString()});
- }
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
- toBoolean(value) {
+ toBoolean = (value) => {
return value !== null && value.toLowerCase() === "true";
- }
+ };
- filterChanged() {
- this.needRedirect = true;
- this.setState({filterActive: (!this.toBoolean(this.state.filterActive)).toString()});
- }
+ filterChanged = () => {
+ const newValue = (!this.toBoolean(this.state.filterActive)).toString();
+ const urlParams = {};
+ urlParams[this.props.filterName] = newValue === "true" ? "true" : null;
+ dispatcher.dispatch("connections_filters", urlParams);
+ this.setState({filterActive: newValue});
+ };
render() {
- let redirect = null;
- if (this.needRedirect) {
- let urlParams = new URLSearchParams(this.props.location.search);
- if (this.toBoolean(this.state.filterActive)) {
- urlParams.set(this.props.filterName, "true");
- } else {
- urlParams.delete(this.props.filterName);
- }
- redirect =
;
-
- this.needRedirect = false;
- }
-
return (
- {redirect}
+ onChange={this.filterChanged}/>
);
}
diff --git a/frontend/src/components/filters/ExitSearchFilter.js b/frontend/src/components/filters/ExitSearchFilter.js
index cfee298..68ca686 100644
--- a/frontend/src/components/filters/ExitSearchFilter.js
+++ b/frontend/src/components/filters/ExitSearchFilter.js
@@ -28,11 +28,16 @@ class ExitSearchFilter extends Component {
let params = new URLSearchParams(this.props.location.search);
this.setState({performedSearch: params.get("performed_search")});
- dispatcher.register("connections_filters", payload => {
+ this.connectionsFiltersCallback = payload => {
if (this.state.performedSearch !== payload["performed_search"]) {
this.setState({performedSearch: payload["performed_search"]});
}
- });
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
render() {
diff --git a/frontend/src/components/filters/FiltersDispatcher.js b/frontend/src/components/filters/FiltersDispatcher.js
deleted file mode 100644
index 3769055..0000000
--- a/frontend/src/components/filters/FiltersDispatcher.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * This file is part of caronte (https://github.com/eciavatta/caronte).
- * Copyright (c) 2020 Emiliano Ciavatta.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
.
- */
-
-import React, {Component} from 'react';
-import {withRouter} from "react-router-dom";
-import {Redirect} from "react-router";
-import dispatcher from "../../dispatcher";
-
-class FiltersDispatcher extends Component {
-
- state = {};
-
- componentDidMount() {
- let params = new URLSearchParams(this.props.location.search);
- this.setState({params});
-
- dispatcher.register("connections_filters", payload => {
- const params = this.state.params;
-
- Object.entries(payload).forEach(([key, value]) => {
- if (value == null) {
- params.delete(key);
- } else {
- params.set(key, value);
- }
- });
-
- this.needRedirect = true;
- this.setState({params});
- });
- }
-
- render() {
- if (this.needRedirect) {
- this.needRedirect = false;
- return
;
- }
-
- return null;
- }
-}
-
-export default withRouter(FiltersDispatcher);
diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js
index fc0ad4d..4c993dc 100644
--- a/frontend/src/components/filters/RulesConnectionsFilter.js
+++ b/frontend/src/components/filters/RulesConnectionsFilter.js
@@ -17,87 +17,74 @@
import React, {Component} from 'react';
import {withRouter} from "react-router-dom";
-import {Redirect} from "react-router";
import './RulesConnectionsFilter.scss';
import ReactTags from 'react-tag-autocomplete';
import backend from "../../backend";
+import dispatcher from "../../dispatcher";
const classNames = require('classnames');
+const _ = require('lodash');
class RulesConnectionsFilter extends Component {
- constructor(props) {
- super(props);
- this.state = {
- mounted: false,
- rules: [],
- activeRules: []
- };
-
- this.needRedirect = false;
- }
+ state = {
+ rules: [],
+ activeRules: []
+ };
componentDidMount() {
- let params = new URLSearchParams(this.props.location.search);
+ const params = new URLSearchParams(this.props.location.search);
let activeRules = params.getAll("matched_rules") || [];
backend.get("/api/rules").then(res => {
let rules = res.json.flatMap(rule => rule.enabled ? [{id: rule.id, name: rule.name}] : []);
activeRules = rules.filter(rule => activeRules.some(id => rule.id === id));
- this.setState({rules, activeRules, mounted: true});
+ this.setState({rules, activeRules});
});
+
+ this.connectionsFiltersCallback = payload => {
+ if ("matched_rules" in payload && !_.isEqual(payload["matched_rules"].sort(), this.state.activeRules.sort())) {
+ const newRules = this.state.rules.filter(r => payload["matched_rules"].includes(r.id));
+ this.setState({
+ activeRules: newRules.map(r => {
+ return {id: r.id, name: r.name};
+ })
+ });
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
}
- componentDidUpdate(prevProps, prevState, snapshot) {
- let urlParams = new URLSearchParams(this.props.location.search);
- let externalRules = urlParams.getAll("matched_rules") || [];
- let activeRules = this.state.activeRules.map(r => r.id);
- let compareRules = (first, second) => first.sort().join(",") === second.sort().join(",");
- if (this.state.mounted &&
- compareRules(prevState.activeRules.map(r => r.id), activeRules) &&
- !compareRules(externalRules, activeRules)) {
- this.setState({activeRules: externalRules.map(id => this.state.rules.find(r => r.id === id))});
- }
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
- onDelete(i) {
- const activeRules = this.state.activeRules.slice(0);
+ onDelete = (i) => {
+ const activeRules = _.clone(this.state.activeRules);
activeRules.splice(i, 1);
- this.needRedirect = true;
- this.setState({ activeRules });
- }
+ this.setState({activeRules});
+ dispatcher.dispatch("connections_filters", {"matched_rules": activeRules.map(r => r.id)});
+ };
- onAddition(rule) {
+ onAddition = (rule) => {
if (!this.state.activeRules.includes(rule)) {
const activeRules = [].concat(this.state.activeRules, rule);
- this.needRedirect = true;
this.setState({activeRules});
+ dispatcher.dispatch("connections_filters", {"matched_rules": activeRules.map(r => r.id)});
}
- }
+ };
render() {
- let redirect = null;
-
- if (this.needRedirect) {
- let urlParams = new URLSearchParams(this.props.location.search);
- urlParams.delete("matched_rules");
- this.state.activeRules.forEach(rule => urlParams.append("matched_rules", rule.id));
- redirect = ;
-
- this.needRedirect = false;
- }
-
return (
-
+
- suggestion.name.startsWith(query) && !this.state.activeRules.includes(suggestion)} />
+ suggestion.name.startsWith(query) && !this.state.activeRules.includes(suggestion)}/>
-
- {redirect}
);
}
diff --git a/frontend/src/components/filters/StringConnectionsFilter.js b/frontend/src/components/filters/StringConnectionsFilter.js
index a3b45dc..c833220 100644
--- a/frontend/src/components/filters/StringConnectionsFilter.js
+++ b/frontend/src/components/filters/StringConnectionsFilter.js
@@ -17,37 +17,36 @@
import React, {Component} from 'react';
import {withRouter} from "react-router-dom";
-import {Redirect} from "react-router";
import InputField from "../fields/InputField";
+import dispatcher from "../../dispatcher";
class StringConnectionsFilter extends Component {
- constructor(props) {
- super(props);
- this.state = {
- fieldValue: "",
- filterValue: null,
- timeoutHandle: null,
- invalidValue: false
- };
- this.needRedirect = false;
- this.filterChanged = this.filterChanged.bind(this);
- }
+ state = {
+ fieldValue: "",
+ filterValue: null,
+ timeoutHandle: null,
+ invalidValue: false
+ };
componentDidMount() {
let params = new URLSearchParams(this.props.location.search);
this.updateStateFromFilterValue(params.get(this.props.filterName));
+
+ this.connectionsFiltersCallback = payload => {
+ const name = this.props.filterName;
+ if (name in payload && this.state.filterValue !== payload[name]) {
+ this.updateStateFromFilterValue(payload[name]);
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
}
- componentDidUpdate(prevProps, prevState, snapshot) {
- let urlParams = new URLSearchParams(this.props.location.search);
- let filterValue = urlParams.get(this.props.filterName);
- if (prevState.filterValue === this.state.filterValue && this.state.filterValue !== filterValue) {
- this.updateStateFromFilterValue(filterValue);
- }
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
- updateStateFromFilterValue(filterValue) {
+ updateStateFromFilterValue = (filterValue) => {
if (filterValue !== null) {
let fieldValue = filterValue;
if (typeof this.props.decodeFunc === "function") {
@@ -70,15 +69,21 @@ class StringConnectionsFilter extends Component {
} else {
this.setState({fieldValue: "", filterValue: null});
}
- }
+ };
- isValueValid(value) {
+ isValueValid = (value) => {
return typeof this.props.validateFunc !== "function" ||
(typeof this.props.validateFunc === "function" && this.props.validateFunc(value));
- }
+ };
- filterChanged(fieldValue) {
- if (this.state.timeoutHandle !== null) {
+ changeFilterValue = (value) => {
+ const urlParams = {};
+ urlParams[this.props.filterName] = value;
+ dispatcher.dispatch("connections_filters", urlParams);
+ };
+
+ filterChanged = (fieldValue) => {
+ if (this.state.timeoutHandle) {
clearTimeout(this.state.timeoutHandle);
}
@@ -87,11 +92,12 @@ class StringConnectionsFilter extends Component {
}
if (fieldValue === "") {
- this.needRedirect = true;
this.setState({fieldValue: "", filterValue: null, invalidValue: false});
- return;
+ return this.changeFilterValue(null);
}
+
+
if (this.isValueValid(fieldValue)) {
let filterValue = fieldValue;
if (filterValue !== "" && typeof this.props.encodeFunc === "function") {
@@ -101,40 +107,27 @@ class StringConnectionsFilter extends Component {
this.setState({
fieldValue: fieldValue,
timeoutHandle: setTimeout(() => {
- this.needRedirect = true;
this.setState({filterValue: filterValue});
+ this.changeFilterValue(filterValue);
}, 500),
invalidValue: false
});
} else {
- this.needRedirect = true;
this.setState({
fieldValue: fieldValue,
invalidValue: true
});
}
- }
+ };
render() {
- let redirect = null;
- if (this.needRedirect) {
- let urlParams = new URLSearchParams(this.props.location.search);
- if (this.state.filterValue !== null) {
- urlParams.set(this.props.filterName, this.state.filterValue);
- } else {
- urlParams.delete(this.props.filterName);
- }
- redirect =
;
- this.needRedirect = false;
- }
let active = this.state.filterValue !== null;
return (
- {redirect}
+ value={this.state.fieldValue} inline={true} small={true}/>
);
}
diff --git a/frontend/src/components/objects/Connection.js b/frontend/src/components/objects/Connection.js
index e0e942a..96f2235 100644
--- a/frontend/src/components/objects/Connection.js
+++ b/frontend/src/components/objects/Connection.js
@@ -23,6 +23,7 @@ import {dateTimeToTime, durationBetween, formatSize} from "../../utils";
import ButtonField from "../fields/ButtonField";
import LinkPopover from "./LinkPopover";
import TextField from "../fields/TextField";
+import dispatcher from "../../dispatcher";
const classNames = require('classnames');
@@ -99,7 +100,8 @@ class Connection extends Component {
this.props.addServicePortFilter(conn["port_dst"])}/>
+ onClick={() => dispatcher.dispatch("connections_filters",
+ {"service_port": conn["port_dst"].toString()})}/>
{conn["ip_src"]}
diff --git a/frontend/src/components/objects/ConnectionMatchedRules.js b/frontend/src/components/objects/ConnectionMatchedRules.js
index 73d5c5d..92bde49 100644
--- a/frontend/src/components/objects/ConnectionMatchedRules.js
+++ b/frontend/src/components/objects/ConnectionMatchedRules.js
@@ -18,20 +18,25 @@
import React, {Component} from 'react';
import './ConnectionMatchedRules.scss';
import ButtonField from "../fields/ButtonField";
+import dispatcher from "../../dispatcher";
+import {withRouter} from "react-router-dom";
class ConnectionMatchedRules extends Component {
- constructor(props) {
- super(props);
- this.state = {
- };
- }
+ onMatchedRulesSelected = (id) => {
+ const params = new URLSearchParams(this.props.location.search);
+ const rules = params.getAll("matched_rules");
+ if (!rules.includes(id)) {
+ rules.push(id);
+ dispatcher.dispatch("connections_filters",{"matched_rules": rules});
+ }
+ };
render() {
const matchedRules = this.props.matchedRules.map(mr => {
const rule = this.props.rules.find(r => r.id === mr);
- return
this.props.addMatchedRulesFilter(rule.id)} name={rule.name}
- color={rule.color} small />;
+ return this.onMatchedRulesSelected(rule.id)} name={rule.name}
+ color={rule.color} small/>;
});
return (
@@ -43,4 +48,4 @@ class ConnectionMatchedRules extends Component {
}
}
-export default ConnectionMatchedRules;
+export default withRouter(ConnectionMatchedRules);
diff --git a/frontend/src/components/pages/MainPage.js b/frontend/src/components/pages/MainPage.js
index da57e1a..0b06f55 100644
--- a/frontend/src/components/pages/MainPage.js
+++ b/frontend/src/components/pages/MainPage.js
@@ -29,7 +29,6 @@ import Header from "../Header";
import Filters from "../dialogs/Filters";
import MainPane from "../panels/MainPane";
import SearchPane from "../panels/SearchPane";
-import FiltersDispatcher from "../filters/FiltersDispatcher";
class MainPage extends Component {
@@ -70,8 +69,6 @@ class MainPage extends Component {
-
-
);
diff --git a/frontend/src/components/panels/ConnectionsPane.js b/frontend/src/components/panels/ConnectionsPane.js
index 1f79ab8..33dd7c1 100644
--- a/frontend/src/components/panels/ConnectionsPane.js
+++ b/frontend/src/components/panels/ConnectionsPane.js
@@ -19,13 +19,13 @@ import React, {Component} from 'react';
import './ConnectionsPane.scss';
import Connection from "../objects/Connection";
import Table from 'react-bootstrap/Table';
-import {Redirect} from 'react-router';
import {withRouter} from "react-router-dom";
import backend from "../../backend";
import ConnectionMatchedRules from "../objects/ConnectionMatchedRules";
import log from "../../log";
import ButtonField from "../fields/ButtonField";
import dispatcher from "../../dispatcher";
+import {Redirect} from "react-router";
const classNames = require('classnames');
@@ -50,60 +50,91 @@ class ConnectionsPane extends Component {
}
componentDidMount() {
- const initialParams = {limit: this.queryLimit};
+ let urlParams = new URLSearchParams(this.props.location.search);
+ this.setState({urlParams});
+
+ const additionalParams = {limit: this.queryLimit};
const match = this.props.location.pathname.match(/^\/connections\/([a-f0-9]{24})$/);
if (match != null) {
const id = match[1];
- initialParams.from = id;
+ additionalParams.from = id;
backend.get(`/api/connections/${id}`)
- .then(res => this.connectionSelected(res.json, false))
+ .then(res => this.connectionSelected(res.json))
.catch(error => log.error("Error loading initial connection", error));
}
- this.loadConnections(initialParams, true).then(() => log.debug("Connections loaded"));
+ this.loadConnections(additionalParams, urlParams, true).then(() => log.debug("Connections loaded"));
+
+ this.connectionsFiltersCallback = payload => {
+ const params = this.state.urlParams;
+ const initialParams = params.toString();
+
+ Object.entries(payload).forEach(([key, value]) => {
+ if (value == null) {
+ params.delete(key);
+ } else if (Array.isArray(value)) {
+ params.delete(key);
+ value.forEach(v => params.append(key, v));
+ } else {
+ params.set(key, value);
+ }
+ });
+
+ if (initialParams === params.toString()) {
+ return;
+ }
- dispatcher.register("timeline_updates", payload => {
+ log.debug("Update following url params:", payload);
+ this.queryStringRedirect = true;
+ this.setState({urlParams});
+
+ this.loadConnections({limit: this.queryLimit}, urlParams)
+ .then(() => log.info("ConnectionsPane reloaded after query string update"));
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
+
+ this.timelineUpdatesCallback = payload => {
this.connectionsListRef.current.scrollTop = 0;
this.loadConnections({
started_after: Math.round(payload.from.getTime() / 1000),
started_before: Math.round(payload.to.getTime() / 1000),
limit: this.maxConnections
}).then(() => log.info(`Loading connections between ${payload.from} and ${payload.to}`));
- });
+ };
+ dispatcher.register("timeline_updates", this.timelineUpdatesCallback);
- dispatcher.register("notifications", payload => {
+ this.notificationsCallback = payload => {
if (payload.event === "rules.new" || payload.event === "rules.edit") {
this.loadRules().then(() => log.debug("Loaded connection rules after notification update"));
}
- });
-
- dispatcher.register("notifications", payload => {
if (payload.event === "services.edit") {
this.loadServices().then(() => log.debug("Services reloaded after notification update"));
}
- });
+ };
+ dispatcher.register("notifications", this.notificationsCallback);
- dispatcher.register("pulse_connections_view", payload => {
+ this.pulseConnectionsViewCallback = payload => {
this.setState({pulseConnectionsView: true});
setTimeout(() => this.setState({pulseConnectionsView: false}), payload.duration);
- });
+ };
+ dispatcher.register("pulse_connections_view", this.pulseConnectionsViewCallback);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.timelineUpdatesCallback);
+ dispatcher.unregister(this.notificationsCallback);
+ dispatcher.unregister(this.pulseConnectionsViewCallback);
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
- connectionSelected = (c, doRedirect = true) => {
- this.doSelectedConnectionRedirect = doRedirect;
+ connectionSelected = (c) => {
+ this.connectionSelectedRedirect = true;
this.setState({selected: c.id});
this.props.onSelected(c);
log.debug(`Connection ${c.id} selected`);
};
- componentDidUpdate(prevProps, prevState, snapshot) {
- if (prevProps.location.search !== this.props.location.search) {
- this.loadConnections({limit: this.queryLimit})
- .then(() => log.info("ConnectionsPane reloaded after query string update"));
- }
- }
-
handleScroll = (e) => {
if (this.disableScrollHandler) {
this.lastScrollPosition = e.currentTarget.scrollTop;
@@ -135,27 +166,12 @@ class ConnectionsPane extends Component {
this.lastScrollPosition = e.currentTarget.scrollTop;
};
- addServicePortFilter = (port) => {
- const urlParams = new URLSearchParams(this.props.location.search);
- urlParams.set("service_port", port);
- this.doQueryStringRedirect = true;
- this.setState({queryString: urlParams});
- };
-
- addMatchedRulesFilter = (matchedRule) => {
- const urlParams = new URLSearchParams(this.props.location.search);
- const oldMatchedRules = urlParams.getAll("matched_rules") || [];
-
- if (!oldMatchedRules.includes(matchedRule)) {
- urlParams.append("matched_rules", matchedRule);
- this.doQueryStringRedirect = true;
- this.setState({queryString: urlParams});
+ async loadConnections(additionalParams, initialParams = null, isInitial = false) {
+ if (!initialParams) {
+ initialParams = this.state.urlParams;
}
- };
-
- async loadConnections(params, isInitial = false) {
- const urlParams = new URLSearchParams(this.props.location.search);
- for (const [name, value] of Object.entries(params)) {
+ const urlParams = new URLSearchParams(initialParams.toString());
+ for (const [name, value] of Object.entries(additionalParams)) {
urlParams.set(name, value);
}
@@ -173,7 +189,7 @@ class ConnectionsPane extends Component {
let firstConnection = this.state.firstConnection;
let lastConnection = this.state.lastConnection;
- if (params !== undefined && params.from !== undefined && params.to === undefined) {
+ if (additionalParams !== undefined && additionalParams.from !== undefined && additionalParams.to === undefined) {
if (res.length > 0) {
if (!isInitial) {
res = res.slice(1);
@@ -189,7 +205,7 @@ class ConnectionsPane extends Component {
firstConnection = connections[0];
}
}
- } else if (params !== undefined && params.to !== undefined && params.from === undefined) {
+ } else if (additionalParams !== undefined && additionalParams.to !== undefined && additionalParams.from === undefined) {
if (res.length > 0) {
connections = res.slice(0, res.length - 1).concat(this.state.connections);
firstConnection = connections[0];
@@ -235,12 +251,12 @@ class ConnectionsPane extends Component {
render() {
let redirect;
- if (this.doSelectedConnectionRedirect) {
- redirect = ;
- this.doSelectedConnectionRedirect = false;
- } else if (this.doQueryStringRedirect) {
- redirect = ;
- this.doQueryStringRedirect = false;
+ if (this.connectionSelectedRedirect) {
+ redirect = ;
+ this.connectionSelectedRedirect = false;
+ } else if (this.queryStringRedirect) {
+ redirect = ;
+ this.queryStringRedirect = false;
}
let loading = null;
@@ -288,12 +304,10 @@ class ConnectionsPane extends Component {
selected={this.state.selected === c.id}
onMarked={marked => c.marked = marked}
onEnabled={enabled => c.hidden = !enabled}
- addServicePortFilter={this.addServicePortFilter}
services={this.state.services}/>,
c.matched_rules.length > 0 &&
+ rules={this.state.rules}/>
];
})
}
diff --git a/frontend/src/components/panels/SearchPane.scss b/frontend/src/components/panels/SearchPane.scss
index 15fc7da..63e11fb 100644
--- a/frontend/src/components/panels/SearchPane.scss
+++ b/frontend/src/components/panels/SearchPane.scss
@@ -4,6 +4,7 @@
.searches-list {
overflow: hidden;
+ flex: 2 1;
.section-content {
height: 100%;
diff --git a/frontend/src/components/panels/StreamsPane.js b/frontend/src/components/panels/StreamsPane.js
index bd1964e..1aa5c53 100644
--- a/frontend/src/components/panels/StreamsPane.js
+++ b/frontend/src/components/panels/StreamsPane.js
@@ -107,7 +107,7 @@ class StreamsPane extends Component {
const json = JSON.parse(m.body);
body = ;
} catch (e) {
- console.log(e);
+ log.error(e);
}
}
diff --git a/frontend/src/dispatcher.js b/frontend/src/dispatcher.js
index 943f7ec..fa08d48 100644
--- a/frontend/src/dispatcher.js
+++ b/frontend/src/dispatcher.js
@@ -15,6 +15,8 @@
* along with this program. If not, see .
*/
+const _ = require('lodash');
+
class Dispatcher {
constructor() {
@@ -44,6 +46,10 @@ class Dispatcher {
}
};
+ unregister = (callback) => {
+ this.listeners = _.without(callback);
+ };
+
}
const dispatcher = new Dispatcher();
diff --git a/search_controller.go b/search_controller.go
index 723cd93..5ed762a 100644
--- a/search_controller.go
+++ b/search_controller.go
@@ -76,6 +76,10 @@ func NewSearchController(storage Storage) *SearchController {
log.WithError(err).Panic("failed to retrieve performed searches")
}
+ if searches == nil {
+ searches = []PerformedSearch{}
+ }
+
return &SearchController{
storage: storage,
performedSearches: searches,
diff --git a/statistics_controller.go b/statistics_controller.go
index 57c7d95..fda7494 100644
--- a/statistics_controller.go
+++ b/statistics_controller.go
@@ -26,19 +26,19 @@ import (
type StatisticRecord struct {
RangeStart time.Time `json:"range_start" bson:"_id"`
- ConnectionsPerService map[uint16]int `json:"connections_per_service,omitempty" bson:"connections_per_service"`
- ClientBytesPerService map[uint16]int `json:"client_bytes_per_service,omitempty" bson:"client_bytes_per_service"`
- ServerBytesPerService map[uint16]int `json:"server_bytes_per_service,omitempty" bson:"server_bytes_per_service"`
- TotalBytesPerService map[uint16]int `json:"total_bytes_per_service,omitempty" bson:"total_bytes_per_service"`
- DurationPerService map[uint16]int64 `json:"duration_per_service,omitempty" bson:"duration_per_service"`
- MatchedRules map[RowID]int64 `json:"matched_rules,omitempty" bson:"matched_rules"`
+ ConnectionsPerService map[uint16]int `json:"connections_per_service" bson:"connections_per_service"`
+ ClientBytesPerService map[uint16]int `json:"client_bytes_per_service" bson:"client_bytes_per_service"`
+ ServerBytesPerService map[uint16]int `json:"server_bytes_per_service" bson:"server_bytes_per_service"`
+ TotalBytesPerService map[uint16]int `json:"total_bytes_per_service" bson:"total_bytes_per_service"`
+ DurationPerService map[uint16]int64 `json:"duration_per_service" bson:"duration_per_service"`
+ MatchedRules map[string]int64 `json:"matched_rules" bson:"matched_rules"`
}
type StatisticsFilter struct {
RangeFrom time.Time `form:"range_from"`
RangeTo time.Time `form:"range_to"`
Ports []uint16 `form:"ports"`
- RulesIDs []RowID `form:"rules_ids"`
+ RulesIDs []string `form:"rules_ids"`
Metric string `form:"metric"`
}
@@ -57,7 +57,7 @@ func NewStatisticsController(storage Storage) StatisticsController {
func (sc *StatisticsController) GetStatistics(context context.Context, filter StatisticsFilter) []StatisticRecord {
var statisticRecords []StatisticRecord
- query := sc.storage.Find(Statistics).Context(context)
+ query := sc.storage.Find(Statistics).Context(context).Sort("_id", true)
if !filter.RangeFrom.IsZero() {
query = query.Filter(OrderedDocument{{"_id", UnorderedDocument{"$lt": filter.RangeFrom}}})
}
@@ -81,7 +81,7 @@ func (sc *StatisticsController) GetStatistics(context context.Context, filter St
}
for _, ruleID := range filter.RulesIDs {
if filter.Metric == "" || filter.Metric == "matched_rules" {
- query = query.Projection(OrderedDocument{{fmt.Sprintf("matched_rules.%s", ruleID.Hex()), 1}})
+ query = query.Projection(OrderedDocument{{fmt.Sprintf("matched_rules.%s", ruleID), 1}})
}
}
@@ -91,6 +91,7 @@ func (sc *StatisticsController) GetStatistics(context context.Context, filter St
}
}
+ log.Println(query)
if err := query.All(&statisticRecords); err != nil {
log.WithError(err).WithField("filter", filter).Error("failed to retrieve statistics")
return []StatisticRecord{}
--
cgit v1.2.3-70-g09d2
From 2fb8993008752063fa13f253784e9e92552e339d Mon Sep 17 00:00:00 2001
From: Emiliano Ciavatta
Date: Fri, 16 Oct 2020 11:13:21 +0200
Subject: Refactor js files
---
frontend/src/components/Header.js | 18 ++--
frontend/src/components/Notifications.js | 8 +-
frontend/src/components/Timeline.js | 30 +++---
frontend/src/components/dialogs/Filters.js | 6 +-
frontend/src/components/fields/ButtonField.js | 8 +-
frontend/src/components/fields/CheckField.js | 12 +--
frontend/src/components/fields/InputField.js | 20 ++--
frontend/src/components/fields/TagField.js | 12 +--
frontend/src/components/fields/TextField.js | 10 +-
.../src/components/fields/extensions/ColorField.js | 17 ++--
.../components/fields/extensions/NumericField.js | 6 +-
frontend/src/components/filters/AdvancedFilters.js | 4 +-
.../components/filters/BooleanConnectionsFilter.js | 6 +-
.../src/components/filters/ExitSearchFilter.js | 4 +-
.../components/filters/RulesConnectionsFilter.js | 6 +-
.../components/filters/StringConnectionsFilter.js | 20 +---
frontend/src/components/objects/Connection.js | 12 +--
frontend/src/components/objects/LinkPopover.js | 15 +--
frontend/src/components/pages/ConfigurationPage.js | 43 ++++-----
frontend/src/components/pages/MainPage.js | 20 ++--
.../src/components/pages/ServiceUnavailablePage.js | 4 +-
frontend/src/components/panels/ConnectionsPane.js | 4 +-
frontend/src/components/panels/MainPane.js | 12 +--
frontend/src/components/panels/PcapsPane.js | 34 +++----
frontend/src/components/panels/SearchPane.js | 106 +++++++++++----------
frontend/src/components/panels/StreamsPane.js | 18 ++--
frontend/src/dispatcher.js | 2 +-
frontend/src/index.js | 20 ++--
frontend/src/notifications.js | 2 +-
frontend/src/serviceWorker.js | 12 +--
frontend/src/setupProxy.js | 10 +-
frontend/src/setupTests.js | 2 +-
frontend/src/utils.js | 20 ++--
33 files changed, 258 insertions(+), 265 deletions(-)
(limited to 'frontend/src/components/pages/MainPage.js')
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index 119958b..ab16dd1 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -15,17 +15,17 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
-import Typed from 'typed.js';
-import './Header.scss';
+import React, {Component} from "react";
import {Link, withRouter} from "react-router-dom";
+import Typed from "typed.js";
+import {cleanNumber, validatePort} from "../utils";
import ButtonField from "./fields/ButtonField";
+import AdvancedFilters from "./filters/AdvancedFilters";
+import BooleanConnectionsFilter from "./filters/BooleanConnectionsFilter";
import ExitSearchFilter from "./filters/ExitSearchFilter";
-import {cleanNumber, validatePort} from "../utils";
-import StringConnectionsFilter from "./filters/StringConnectionsFilter";
import RulesConnectionsFilter from "./filters/RulesConnectionsFilter";
-import BooleanConnectionsFilter from "./filters/BooleanConnectionsFilter";
-import AdvancedFilters from "./filters/AdvancedFilters";
+import StringConnectionsFilter from "./filters/StringConnectionsFilter";
+import "./Header.scss";
class Header extends Component {
@@ -49,7 +49,7 @@ class Header extends Component {
- {
+ {
this.el = el;
}}/>
@@ -67,7 +67,7 @@ class Header extends Component {
-
+
diff --git a/frontend/src/components/Notifications.js b/frontend/src/components/Notifications.js
index ad681a2..56a4508 100644
--- a/frontend/src/components/Notifications.js
+++ b/frontend/src/components/Notifications.js
@@ -15,12 +15,12 @@
* along with this program. If not, see
+
diff --git a/frontend/src/components/fields/InputField.js b/frontend/src/components/fields/InputField.js
index 80cce3b..823989d 100644
--- a/frontend/src/components/fields/InputField.js
+++ b/frontend/src/components/fields/InputField.js
@@ -15,12 +15,12 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
-import './InputField.scss';
-import './common.scss';
+import React, {Component} from "react";
import {randomClassName} from "../../utils";
+import "./common.scss";
+import "./InputField.scss";
-const classNames = require('classnames');
+const classNames = require("classnames");
class InputField extends Component {
@@ -59,23 +59,23 @@ class InputField extends Component {
}
return (
-
- { name &&
+ {name &&
{name}:
}
- { type === "file" &&
- {value.name || this.props.placeholder} }
+ {type === "file" &&
+ {value.name || this.props.placeholder} }
+ readOnly={this.props.readonly}/>
- { type !== "file" && value !== "" &&
+ {type !== "file" && value !== "" &&
handler(null)}>del
diff --git a/frontend/src/components/fields/TagField.js b/frontend/src/components/fields/TagField.js
index 89445b6..9a36da4 100644
--- a/frontend/src/components/fields/TagField.js
+++ b/frontend/src/components/fields/TagField.js
@@ -15,14 +15,14 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
-import './TagField.scss';
-import './common.scss';
-import {randomClassName} from "../../utils";
+import React, {Component} from "react";
import ReactTags from "react-tag-autocomplete";
+import {randomClassName} from "../../utils";
+import "./common.scss";
+import "./TagField.scss";
-const classNames = require('classnames');
-const _ = require('lodash');
+const classNames = require("classnames");
+const _ = require("lodash");
class TagField extends Component {
diff --git a/frontend/src/components/fields/TextField.js b/frontend/src/components/fields/TextField.js
index 9237c0c..4dd77bd 100644
--- a/frontend/src/components/fields/TextField.js
+++ b/frontend/src/components/fields/TextField.js
@@ -15,12 +15,12 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
-import './TextField.scss';
-import './common.scss';
+import React, {Component} from "react";
import {randomClassName} from "../../utils";
+import "./common.scss";
+import "./TextField.scss";
-const classNames = require('classnames');
+const classNames = require("classnames");
class TextField extends Component {
@@ -50,7 +50,7 @@ class TextField extends Component {
{"field-invalid": this.props.invalid}, {"field-small": this.props.small})}>
{name &&
{name}: }
+ readOnly={this.props.readonly} value={this.props.value} ref={this.props.textRef}/>
{error &&
error: {error}
}
);
diff --git a/frontend/src/components/fields/extensions/ColorField.js b/frontend/src/components/fields/extensions/ColorField.js
index f1c0caf..fd30988 100644
--- a/frontend/src/components/fields/extensions/ColorField.js
+++ b/frontend/src/components/fields/extensions/ColorField.js
@@ -15,22 +15,21 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
+import React, {Component} from "react";
import {OverlayTrigger, Popover} from "react-bootstrap";
-import './ColorField.scss';
-import InputField from "../InputField";
import validation from "../../../validation";
+import InputField from "../InputField";
+import "./ColorField.scss";
class ColorField extends Component {
constructor(props) {
super(props);
- this.state = {
- };
+ this.state = {};
- this.colors = ["#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB", "#1E88E5", "#039BE5", "#00ACC1",
- "#00897B", "#43A047", "#7CB342", "#9E9D24", "#F9A825", "#FB8C00", "#F4511E", "#6D4C41"];
+ this.colors = ["#e53935", "#d81b60", "#8e24aa", "#5e35b1", "#3949ab", "#1e88e5", "#039be5", "#00acc1",
+ "#00897b", "#43a047", "#7cb342", "#9e9d24", "#f9a825", "#fb8c00", "#f4511e", "#6d4c41"];
}
componentDidUpdate(prevProps, prevState, snapshot) {
@@ -55,7 +54,7 @@ class ColorField extends Component {
this.props.onChange(color);
}
document.body.click(); // magic to close popup
- }} />);
+ }}/>);
const popover = (
@@ -82,7 +81,7 @@ class ColorField extends Component {
+ error={null}/>
pick
diff --git a/frontend/src/components/fields/extensions/NumericField.js b/frontend/src/components/fields/extensions/NumericField.js
index d4d027d..a6cba26 100644
--- a/frontend/src/components/fields/extensions/NumericField.js
+++ b/frontend/src/components/fields/extensions/NumericField.js
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
+import React, {Component} from "react";
import InputField from "../InputField";
class NumericField extends Component {
@@ -35,7 +35,7 @@ class NumericField extends Component {
}
onChange = (value) => {
- value = value.toString().replace(/[^\d]/gi, '');
+ value = value.toString().replace(/[^\d]/gi, "");
let intValue = 0;
if (value !== "") {
intValue = parseInt(value, 10);
@@ -53,7 +53,7 @@ class NumericField extends Component {
render() {
return (
+ invalid={this.state.invalid}/>
);
}
diff --git a/frontend/src/components/filters/AdvancedFilters.js b/frontend/src/components/filters/AdvancedFilters.js
index f5ba825..2b479ed 100644
--- a/frontend/src/components/filters/AdvancedFilters.js
+++ b/frontend/src/components/filters/AdvancedFilters.js
@@ -15,11 +15,11 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
import dispatcher from "../../dispatcher";
-import ButtonField from "../fields/ButtonField";
import {updateParams} from "../../utils";
+import ButtonField from "../fields/ButtonField";
class AdvancedFilters extends Component {
diff --git a/frontend/src/components/filters/BooleanConnectionsFilter.js b/frontend/src/components/filters/BooleanConnectionsFilter.js
index 9558323..0355167 100644
--- a/frontend/src/components/filters/BooleanConnectionsFilter.js
+++ b/frontend/src/components/filters/BooleanConnectionsFilter.js
@@ -15,10 +15,10 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
-import CheckField from "../fields/CheckField";
import dispatcher from "../../dispatcher";
+import CheckField from "../fields/CheckField";
class BooleanConnectionsFilter extends Component {
@@ -30,7 +30,7 @@ class BooleanConnectionsFilter extends Component {
let params = new URLSearchParams(this.props.location.search);
this.setState({filterActive: this.toBoolean(params.get(this.props.filterName)).toString()});
- this.connectionsFiltersCallback = payload => {
+ this.connectionsFiltersCallback = (payload) => {
const name = this.props.filterName;
if (name in payload && this.state.filterActive !== payload[name]) {
this.setState({filterActive: payload[name]});
diff --git a/frontend/src/components/filters/ExitSearchFilter.js b/frontend/src/components/filters/ExitSearchFilter.js
index 68ca686..72cfb0c 100644
--- a/frontend/src/components/filters/ExitSearchFilter.js
+++ b/frontend/src/components/filters/ExitSearchFilter.js
@@ -15,10 +15,10 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
-import CheckField from "../fields/CheckField";
import dispatcher from "../../dispatcher";
+import CheckField from "../fields/CheckField";
class ExitSearchFilter extends Component {
diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js
index 8e40d30..86eae7e 100644
--- a/frontend/src/components/filters/RulesConnectionsFilter.js
+++ b/frontend/src/components/filters/RulesConnectionsFilter.js
@@ -15,14 +15,14 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
import backend from "../../backend";
import dispatcher from "../../dispatcher";
import TagField from "../fields/TagField";
-const classNames = require('classnames');
-const _ = require('lodash');
+const classNames = require("classnames");
+const _ = require("lodash");
class RulesConnectionsFilter extends Component {
diff --git a/frontend/src/components/filters/StringConnectionsFilter.js b/frontend/src/components/filters/StringConnectionsFilter.js
index 18b3784..c3c5925 100644
--- a/frontend/src/components/filters/StringConnectionsFilter.js
+++ b/frontend/src/components/filters/StringConnectionsFilter.js
@@ -15,10 +15,10 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
-import InputField from "../fields/InputField";
import dispatcher from "../../dispatcher";
+import InputField from "../fields/InputField";
class StringConnectionsFilter extends Component {
@@ -56,15 +56,9 @@ class StringConnectionsFilter extends Component {
fieldValue = this.props.replaceFunc(fieldValue);
}
if (this.isValueValid(fieldValue)) {
- this.setState({
- fieldValue: fieldValue,
- filterValue: filterValue
- });
+ this.setState({fieldValue, filterValue: filterValue});
} else {
- this.setState({
- fieldValue: fieldValue,
- invalidValue: true
- });
+ this.setState({fieldValue, invalidValue: true});
}
} else {
this.setState({fieldValue: "", filterValue: null});
@@ -97,7 +91,6 @@ class StringConnectionsFilter extends Component {
}
-
if (this.isValueValid(fieldValue)) {
let filterValue = fieldValue;
if (filterValue !== "" && typeof this.props.encodeFunc === "function") {
@@ -113,10 +106,7 @@ class StringConnectionsFilter extends Component {
invalidValue: false
});
} else {
- this.setState({
- fieldValue: fieldValue,
- invalidValue: true
- });
+ this.setState({fieldValue, invalidValue: true});
}
};
diff --git a/frontend/src/components/objects/Connection.js b/frontend/src/components/objects/Connection.js
index 96f2235..f838606 100644
--- a/frontend/src/components/objects/Connection.js
+++ b/frontend/src/components/objects/Connection.js
@@ -15,17 +15,17 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
-import './Connection.scss';
+import React, {Component} from "react";
import {Form} from "react-bootstrap";
import backend from "../../backend";
+import dispatcher from "../../dispatcher";
import {dateTimeToTime, durationBetween, formatSize} from "../../utils";
import ButtonField from "../fields/ButtonField";
-import LinkPopover from "./LinkPopover";
import TextField from "../fields/TextField";
-import dispatcher from "../../dispatcher";
+import "./Connection.scss";
+import LinkPopover from "./LinkPopover";
-const classNames = require('classnames');
+const classNames = require("classnames");
class Connection extends Component {
@@ -59,7 +59,7 @@ class Connection extends Component {
}
if (name === "copy") {
this.copyTextarea.current.select();
- document.execCommand('copy');
+ document.execCommand("copy");
this.setState({copiedMessage: true});
setTimeout(() => this.setState({copiedMessage: false}), 3000);
}
diff --git a/frontend/src/components/objects/LinkPopover.js b/frontend/src/components/objects/LinkPopover.js
index 3c5bf67..551a819 100644
--- a/frontend/src/components/objects/LinkPopover.js
+++ b/frontend/src/components/objects/LinkPopover.js
@@ -15,10 +15,10 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
-import {randomClassName} from "../../utils";
+import React, {Component} from "react";
import {OverlayTrigger, Popover} from "react-bootstrap";
-import './LinkPopover.scss';
+import {randomClassName} from "../../utils";
+import "./LinkPopover.scss";
class LinkPopover extends Component {
@@ -39,10 +39,11 @@ class LinkPopover extends Component {
);
return (this.props.content ?
-
- {this.props.text}
- :
- {this.props.text}
+
+ {this.props.text}
+ :
+ {this.props.text}
);
}
}
diff --git a/frontend/src/components/pages/ConfigurationPage.js b/frontend/src/components/pages/ConfigurationPage.js
index 6ab8ae3..2bd2da7 100644
--- a/frontend/src/components/pages/ConfigurationPage.js
+++ b/frontend/src/components/pages/ConfigurationPage.js
@@ -15,19 +15,19 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
-import '../panels/common.scss';
-import './ConfigurationPage.scss';
-import LinkPopover from "../objects/LinkPopover";
+import React, {Component} from "react";
import {Col, Container, Row} from "react-bootstrap";
-import InputField from "../fields/InputField";
-import TextField from "../fields/TextField";
-import ButtonField from "../fields/ButtonField";
-import CheckField from "../fields/CheckField";
-import {createCurlCommand} from "../../utils";
import Table from "react-bootstrap/Table";
-import validation from "../../validation";
import backend from "../../backend";
+import {createCurlCommand} from "../../utils";
+import validation from "../../validation";
+import ButtonField from "../fields/ButtonField";
+import CheckField from "../fields/CheckField";
+import InputField from "../fields/InputField";
+import TextField from "../fields/TextField";
+import LinkPopover from "../objects/LinkPopover";
+import "../panels/common.scss";
+import "./ConfigurationPage.scss";
class ConfigurationPage extends Component {
@@ -40,8 +40,7 @@ class ConfigurationPage extends Component {
"flag_regex": "",
"auth_required": false
},
- "accounts": {
- }
+ "accounts": {}
},
newUsername: "",
newPassword: ""
@@ -50,9 +49,9 @@ class ConfigurationPage extends Component {
saveSettings = () => {
if (this.validateSettings(this.state.settings)) {
- backend.post("/setup", this.state.settings).then(_ => {
+ backend.post("/setup", this.state.settings).then((_) => {
this.props.onConfigured();
- }).catch(res => {
+ }).catch((res) => {
this.setState({setupStatusCode: res.status, setupResponse: JSON.stringify(res.json)});
});
}
@@ -102,14 +101,14 @@ class ConfigurationPage extends Component {
const accounts = Object.entries(settings.accounts).map(([username, password]) =>
{username}
-
+
this.updateParam((s) => delete s.accounts[username]) }/>
+ onClick={() => this.updateParam((s) => delete s.accounts[username])}/>
).concat(
this.setState({newUsername: v})} />
+ onChange={(v) => this.setState({newUsername: v})}/>
this.setState({newPassword: v})} />
+ onChange={(v) => this.setState({newPassword: v})}/>
);
@@ -122,7 +121,7 @@ class ConfigurationPage extends Component {
POST /setup
+ placement="left"/>
@@ -131,10 +130,10 @@ class ConfigurationPage extends Component {
this.updateParam((s) => s.config.server_address = v)} />
+ onChange={(v) => this.updateParam((s) => s.config.server_address = v)}/>
this.updateParam((s) => s.config.flag_regex = v)}
- error={this.state.flagRegexError} />
+ error={this.state.flagRegexError}/>
this.updateParam((s) => s.config.auth_required = v)}/>
@@ -166,7 +165,7 @@ class ConfigurationPage extends Component {
-
+
diff --git a/frontend/src/components/pages/MainPage.js b/frontend/src/components/pages/MainPage.js
index 0b06f55..a542e3f 100644
--- a/frontend/src/components/pages/MainPage.js
+++ b/frontend/src/components/pages/MainPage.js
@@ -15,20 +15,20 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
-import './MainPage.scss';
-import './common.scss';
-import Connections from "../panels/ConnectionsPane";
-import StreamsPane from "../panels/StreamsPane";
+import React, {Component} from "react";
import {BrowserRouter as Router, Route, Switch} from "react-router-dom";
-import Timeline from "../Timeline";
-import PcapsPane from "../panels/PcapsPane";
-import RulesPane from "../panels/RulesPane";
-import ServicesPane from "../panels/ServicesPane";
-import Header from "../Header";
import Filters from "../dialogs/Filters";
+import Header from "../Header";
+import Connections from "../panels/ConnectionsPane";
import MainPane from "../panels/MainPane";
+import PcapsPane from "../panels/PcapsPane";
+import RulesPane from "../panels/RulesPane";
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 {
diff --git a/frontend/src/components/pages/ServiceUnavailablePage.js b/frontend/src/components/pages/ServiceUnavailablePage.js
index f27d84d..deb4cf8 100644
--- a/frontend/src/components/pages/ServiceUnavailablePage.js
+++ b/frontend/src/components/pages/ServiceUnavailablePage.js
@@ -15,8 +15,8 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
-import './MainPage.scss';
+import React, {Component} from "react";
+import "./MainPage.scss";
class ServiceUnavailablePage extends Component {
diff --git a/frontend/src/components/panels/ConnectionsPane.js b/frontend/src/components/panels/ConnectionsPane.js
index 89859e6..ea47059 100644
--- a/frontend/src/components/panels/ConnectionsPane.js
+++ b/frontend/src/components/panels/ConnectionsPane.js
@@ -85,8 +85,8 @@ class ConnectionsPane extends Component {
this.timelineUpdatesCallback = payload => {
this.connectionsListRef.current.scrollTop = 0;
this.loadConnections({
- started_after: Math.round(payload.from.getTime() / 1000),
- started_before: Math.round(payload.to.getTime() / 1000),
+ "started_after": Math.round(payload.from.getTime() / 1000),
+ "started_before": Math.round(payload.to.getTime() / 1000),
limit: this.maxConnections
}).then(() => log.info(`Loading connections between ${payload.from} and ${payload.to}`));
};
diff --git a/frontend/src/components/panels/MainPane.js b/frontend/src/components/panels/MainPane.js
index 8aa8ad8..ce72be5 100644
--- a/frontend/src/components/panels/MainPane.js
+++ b/frontend/src/components/panels/MainPane.js
@@ -15,15 +15,15 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
-import './common.scss';
-import './MainPane.scss';
+import React, {Component} from "react";
import Typed from "typed.js";
import dispatcher from "../../dispatcher";
-import RulesPane from "./RulesPane";
-import StreamsPane from "./StreamsPane";
+import "./common.scss";
+import "./MainPane.scss";
import PcapsPane from "./PcapsPane";
+import RulesPane from "./RulesPane";
import ServicesPane from "./ServicesPane";
+import StreamsPane from "./StreamsPane";
class MainPane extends Component {
@@ -94,7 +94,7 @@ class MainPane extends Component {
- {
+ {
this.el = el;
}}/>
diff --git a/frontend/src/components/panels/PcapsPane.js b/frontend/src/components/panels/PcapsPane.js
index 900aacc..fd3db75 100644
--- a/frontend/src/components/panels/PcapsPane.js
+++ b/frontend/src/components/panels/PcapsPane.js
@@ -15,18 +15,18 @@
* along with this program. If not, see
.
*/
-import React, {Component} from 'react';
-import './PcapsPane.scss';
-import './common.scss';
+import React, {Component} from "react";
import Table from "react-bootstrap/Table";
import backend from "../../backend";
+import dispatcher from "../../dispatcher";
import {createCurlCommand, dateTimeToTime, durationBetween, formatSize} from "../../utils";
-import InputField from "../fields/InputField";
+import ButtonField from "../fields/ButtonField";
import CheckField from "../fields/CheckField";
+import InputField from "../fields/InputField";
import TextField from "../fields/TextField";
-import ButtonField from "../fields/ButtonField";
import LinkPopover from "../objects/LinkPopover";
-import dispatcher from "../../dispatcher";
+import "./common.scss";
+import "./PcapsPane.scss";
class PcapsPane extends Component {
@@ -45,7 +45,7 @@ class PcapsPane extends Component {
componentDidMount() {
this.loadSessions();
- dispatcher.register("notifications", payload => {
+ dispatcher.register("notifications", (payload) => {
if (payload.event === "pcap.upload" || payload.event === "pcap.file") {
this.loadSessions();
}
@@ -56,8 +56,8 @@ class PcapsPane extends Component {
loadSessions = () => {
backend.get("/api/pcap/sessions")
- .then(res => this.setState({sessions: res.json, sessionsStatusCode: res.status}))
- .catch(res => this.setState({
+ .then((res) => this.setState({sessions: res.json, sessionsStatusCode: res.status}))
+ .catch((res) => this.setState({
sessions: res.json, sessionsStatusCode: res.status,
sessionsResponse: JSON.stringify(res.json)
}));
@@ -72,14 +72,14 @@ class PcapsPane extends Component {
const formData = new FormData();
formData.append("file", this.state.uploadSelectedFile);
formData.append("flush_all", this.state.uploadFlushAll);
- backend.postFile("/api/pcap/upload", formData).then(res => {
+ backend.postFile("/api/pcap/upload", formData).then((res) => {
this.setState({
uploadStatusCode: res.status,
uploadResponse: JSON.stringify(res.json)
});
this.resetUpload();
this.loadSessions();
- }).catch(res => this.setState({
+ }).catch((res) => this.setState({
uploadStatusCode: res.status,
uploadResponse: JSON.stringify(res.json)
})
@@ -96,14 +96,14 @@ class PcapsPane extends Component {
file: this.state.fileValue,
flush_all: this.state.processFlushAll,
delete_original_file: this.state.deleteOriginalFile
- }).then(res => {
+ }).then((res) => {
this.setState({
processStatusCode: res.status,
processResponse: JSON.stringify(res.json)
});
this.resetProcess();
this.loadSessions();
- }).catch(res => this.setState({
+ }).catch((res) => this.setState({
processStatusCode: res.status,
processResponse: JSON.stringify(res.json)
})
@@ -130,7 +130,7 @@ class PcapsPane extends Component {
};
render() {
- let sessions = this.state.sessions.map(s =>
+ let sessions = this.state.sessions.map((s) =>
{s["id"].substring(0, 8)}
{dateTimeToTime(s["started_at"])}
@@ -229,7 +229,7 @@ class PcapsPane extends Component {
options:
this.setState({uploadFlushAll: v})}/>
+ onChange={(v) => this.setState({uploadFlushAll: v})}/>
@@ -254,9 +254,9 @@ class PcapsPane extends Component {
this.setState({processFlushAll: v})}/>
+ onChange={(v) => this.setState({processFlushAll: v})}/>
this.setState({deleteOriginalFile: v})}/>
+ onChange={(v) => this.setState({deleteOriginalFile: v})}/>
diff --git a/frontend/src/components/panels/SearchPane.js b/frontend/src/components/panels/SearchPane.js
index 1fb48ef..d3c0c8b 100644
--- a/frontend/src/components/panels/SearchPane.js
+++ b/frontend/src/components/panels/SearchPane.js
@@ -15,21 +15,21 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
-import './common.scss';
-import './SearchPane.scss';
+import React, {Component} from "react";
import Table from "react-bootstrap/Table";
-import InputField from "../fields/InputField";
-import TextField from "../fields/TextField";
import backend from "../../backend";
-import ButtonField from "../fields/ButtonField";
-import LinkPopover from "../objects/LinkPopover";
-import {createCurlCommand, dateTimeToTime, durationBetween} from "../../utils";
import dispatcher from "../../dispatcher";
-import TagField from "../fields/TagField";
+import {createCurlCommand, dateTimeToTime, durationBetween} from "../../utils";
+import ButtonField from "../fields/ButtonField";
import CheckField from "../fields/CheckField";
+import InputField from "../fields/InputField";
+import TagField from "../fields/TagField";
+import TextField from "../fields/TextField";
+import LinkPopover from "../objects/LinkPopover";
+import "./common.scss";
+import "./SearchPane.scss";
-const _ = require('lodash');
+const _ = require("lodash");
class SearchPane extends Component {
@@ -104,15 +104,15 @@ class SearchPane extends Component {
validateSearch = (options) => {
let valid = true;
- if (options.text_search.exact_phrase && options.text_search.exact_phrase.length < 3) {
+ if (options["text_search"]["exact_phrase"] && options["text_search"]["exact_phrase"].length < 3) {
this.setState({exactPhraseError: "text_search.exact_phrase.length < 3"});
valid = false;
}
- if (options.regex_search.pattern && options.regex_search.pattern.length < 3) {
+ if (options["regex_search"].pattern && options["regex_search"].pattern.length < 3) {
this.setState({patternError: "regex_search.pattern.length < 3"});
valid = false;
}
- if (options.regex_search.not_pattern && options.regex_search.not_pattern.length < 3) {
+ if (options["regex_search"]["not_pattern"] && options["regex_search"]["not_pattern"].length < 3) {
this.setState({exactPhraseError: "regex_search.not_pattern.length < 3"});
valid = false;
}
@@ -128,25 +128,25 @@ class SearchPane extends Component {
extractPattern = (options) => {
let pattern = "";
if (_.isEqual(options.regex_search, this.searchOptions.regex_search)) { // is text search
- if (options.text_search.exact_phrase) {
- pattern += `"${options.text_search.exact_phrase}"`;
+ if (options["text_search"]["exact_phrase"]) {
+ pattern += `"${options["text_search"]["exact_phrase"]}"`;
} else {
- pattern += options.text_search.terms.join(" ");
- if (options.text_search.excluded_terms) {
- pattern += " -" + options.text_search.excluded_terms.join(" -");
+ pattern += options["text_search"].terms.join(" ");
+ if (options["text_search"]["excluded_terms"]) {
+ pattern += " -" + options["text_search"]["excluded_terms"].join(" -");
}
}
- options.text_search.case_sensitive && (pattern += "/s");
+ options["text_search"]["case_sensitive"] && (pattern += "/s");
} else { // is regex search
- if (options.regex_search.pattern) {
- pattern += "/" + options.regex_search.pattern + "/";
+ if (options["regex_search"].pattern) {
+ pattern += "/" + options["regex_search"].pattern + "/";
} else {
- pattern += "!/" + options.regex_search.not_pattern + "/";
+ pattern += "!/" + options["regex_search"]["not_pattern"] + "/";
}
- options.regex_search.case_insensitive && (pattern += "i");
- options.regex_search.multi_line && (pattern += "m");
- options.regex_search.ignore_whitespaces && (pattern += "x");
- options.regex_search.dot_character && (pattern += "s");
+ options["regex_search"]["case_insensitive"] && (pattern += "i");
+ options["regex_search"]["multi_line"] && (pattern += "m");
+ options["regex_search"]["ignore_whitespaces"] && (pattern += "x");
+ options["regex_search"]["dot_character"] && (pattern += "s");
}
return pattern;
@@ -222,25 +222,29 @@ class SearchPane extends Component {
- {return {name: t};})}
+ {
+ return {name: t};
+ })}
name="terms" min={3} inline allowNew={true}
- readonly={regexOptionsModified || options.text_search.exact_phrase}
- onChange={(tags) => this.updateParam(s => s.text_search.terms = tags.map(t => t.name))}/>
- {return {name: t};})}
+ readonly={regexOptionsModified || options["text_search"]["exact_phrase"]}
+ onChange={(tags) => this.updateParam(s => s["text_search"].terms = tags.map(t => t.name))}/>
+ {
+ return {name: t};
+ })}
name="excluded_terms" min={3} inline allowNew={true}
- readonly={regexOptionsModified || options.text_search.exact_phrase}
- onChange={(tags) => this.updateParam(s => s.text_search.excluded_terms = tags.map(t => t.name))}/>
+ readonly={regexOptionsModified || options["text_search"]["exact_phrase"]}
+ onChange={(tags) => this.updateParam(s => s["text_search"]["excluded_terms"] = tags.map(t => t.name))}/>
or
- this.updateParam(s => s.text_search.exact_phrase = v)}
- readonly={regexOptionsModified || (Array.isArray(options.text_search.terms) && options.text_search.terms.length > 0)}/>
+ onChange={v => this.updateParam(s => s["text_search"]["exact_phrase"] = v)}
+ readonly={regexOptionsModified || (Array.isArray(options["text_search"].terms) && options["text_search"].terms.length > 0)}/>
- this.updateParam(s => s.text_search.case_sensitive = v)}/>
+ onChange={(v) => this.updateParam(s => s["text_search"]["case_sensitive"] = v)}/>
@@ -248,30 +252,30 @@ class SearchPane extends Component {
-
this.updateParam(s => s.regex_search.pattern = v)}/>
+ readonly={textOptionsModified || options["regex_search"]["not_pattern"]}
+ onChange={v => this.updateParam(s => s["regex_search"].pattern = v)}/>
or
- this.updateParam(s => s.regex_search.not_pattern = v)}/>
+ readonly={textOptionsModified || options["regex_search"].pattern}
+ onChange={v => this.updateParam(s => s["regex_search"]["not_pattern"] = v)}/>
- this.updateParam(s => s.regex_search.case_insensitive = v)}/>
- this.updateParam(s => s["regex_search"]["case_insensitive"] = v)}/>
+ this.updateParam(s => s.regex_search.multi_line = v)}/>
- this.updateParam(s => s["regex_search"]["multi_line"] = v)}/>
+ this.updateParam(s => s.regex_search.ignore_whitespaces = v)}/>
- this.updateParam(s => s["regex_search"]["ignore_whitespaces"] = v)}/>
+ this.updateParam(s => s.regex_search.dot_character = v)}/>
+ onChange={(v) => this.updateParam(s => s["regex_search"]["dot_character"] = v)}/>
diff --git a/frontend/src/components/panels/StreamsPane.js b/frontend/src/components/panels/StreamsPane.js
index be39777..41ab33d 100644
--- a/frontend/src/components/panels/StreamsPane.js
+++ b/frontend/src/components/panels/StreamsPane.js
@@ -15,19 +15,19 @@
* along with this program. If not, see .
*/
-import React, {Component} from 'react';
-import './StreamsPane.scss';
-import {Row} from 'react-bootstrap';
-import MessageAction from "../objects/MessageAction";
+import DOMPurify from "dompurify";
+import React, {Component} from "react";
+import {Row} from "react-bootstrap";
+import ReactJson from "react-json-view"
import backend from "../../backend";
+import log from "../../log";
+import {downloadBlob, getHeaderValue} from "../../utils";
import ButtonField from "../fields/ButtonField";
import ChoiceField from "../fields/ChoiceField";
-import DOMPurify from 'dompurify';
-import ReactJson from 'react-json-view'
-import {downloadBlob, getHeaderValue} from "../../utils";
-import log from "../../log";
+import MessageAction from "../objects/MessageAction";
+import "./StreamsPane.scss";
-const classNames = require('classnames');
+const classNames = require("classnames");
class StreamsPane extends Component {
diff --git a/frontend/src/dispatcher.js b/frontend/src/dispatcher.js
index 881970f..ef5dbde 100644
--- a/frontend/src/dispatcher.js
+++ b/frontend/src/dispatcher.js
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-const _ = require('lodash');
+const _ = require("lodash");
class Dispatcher {
diff --git a/frontend/src/index.js b/frontend/src/index.js
index 4bc2730..d00df88 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -15,21 +15,21 @@
* along with this program. If not, see .
*/
-import React from 'react';
-import ReactDOM from 'react-dom';
-import 'bootstrap/dist/css/bootstrap.css';
-import './index.scss';
-import App from './components/App';
-import * as serviceWorker from './serviceWorker';
+import "bootstrap/dist/css/bootstrap.css";
+import React from "react";
+import ReactDOM from "react-dom";
+import App from "./components/App";
+import "./index.scss";
import notifications from "./notifications";
+import * as serviceWorker from "./serviceWorker";
notifications.createWebsocket();
ReactDOM.render(
- //
- ,
- // ,
- document.getElementById('root')
+ //
+ ,
+ // ,
+ document.getElementById("root")
);
serviceWorker.unregister();
diff --git a/frontend/src/notifications.js b/frontend/src/notifications.js
index f04036d..3c83b87 100644
--- a/frontend/src/notifications.js
+++ b/frontend/src/notifications.js
@@ -15,8 +15,8 @@
* along with this program. If not, see .
*/
-import log from "./log";
import dispatcher from "./dispatcher";
+import log from "./log";
class Notifications {
diff --git a/frontend/src/serviceWorker.js b/frontend/src/serviceWorker.js
index b04b771..c633a91 100644
--- a/frontend/src/serviceWorker.js
+++ b/frontend/src/serviceWorker.js
@@ -57,7 +57,7 @@ export function register(config) {
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
- .then(registration => {
+ .then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
@@ -93,7 +93,7 @@ function registerValidSW(swUrl, config) {
};
};
})
- .catch(error => {
+ .catch((error) => {
console.error('Error during service worker registration:', error);
});
}
@@ -103,7 +103,7 @@ function checkValidServiceWorker(swUrl, config) {
fetch(swUrl, {
headers: { 'Service-Worker': 'script' },
})
- .then(response => {
+ .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
@@ -111,7 +111,7 @@ function checkValidServiceWorker(swUrl, config) {
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
- navigator.serviceWorker.ready.then(registration => {
+ navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
@@ -131,10 +131,10 @@ function checkValidServiceWorker(swUrl, config) {
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
- .then(registration => {
+ .then((registration) => {
registration.unregister();
})
- .catch(error => {
+ .catch((error) => {
console.error(error.message);
});
}
diff --git a/frontend/src/setupProxy.js b/frontend/src/setupProxy.js
index f2e1c39..fb60b75 100644
--- a/frontend/src/setupProxy.js
+++ b/frontend/src/setupProxy.js
@@ -15,10 +15,10 @@
* along with this program. If not, see .
*/
-const { createProxyMiddleware } = require('http-proxy-middleware');
+const {createProxyMiddleware} = require("http-proxy-middleware");
-module.exports = function(app) {
- app.use(createProxyMiddleware("/api", { target: "http://localhost:3333" }));
- app.use(createProxyMiddleware("/setup", { target: "http://localhost:3333" }));
- app.use(createProxyMiddleware("/ws", { target: "http://localhost:3333", ws: true }));
+module.exports = function (app) {
+ app.use(createProxyMiddleware("/api", {target: "http://localhost:3333"}));
+ app.use(createProxyMiddleware("/setup", {target: "http://localhost:3333"}));
+ app.use(createProxyMiddleware("/ws", {target: "http://localhost:3333", ws: true}));
};
diff --git a/frontend/src/setupTests.js b/frontend/src/setupTests.js
index 74b1a27..9b1d6d0 100644
--- a/frontend/src/setupTests.js
+++ b/frontend/src/setupTests.js
@@ -2,4 +2,4 @@
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom/extend-expect';
+import "@testing-library/jest-dom/extend-expect";
\ No newline at end of file
diff --git a/frontend/src/utils.js b/frontend/src/utils.js
index 06414ac..0f0927e 100644
--- a/frontend/src/utils.js
+++ b/frontend/src/utils.js
@@ -18,15 +18,15 @@
const timeRegex = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/;
export function createCurlCommand(subCommand, method = null, json = null, data = null) {
- const full = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
+ const full = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
let contentType = null;
let content = null;
if (json != null) {
- contentType = ' -H "Content-Type: application/json" \\\n';
+ contentType = " -H \"Content-Type: application/json\" \\\n";
content = ` -d '${JSON.stringify(json)}'`;
} else if (data != null) {
- contentType = ' -H "Content-Type: multipart/form-data" \\\n';
+ contentType = " -H \"Content-Type: multipart/form-data\" \\\n";
content = " " + Object.entries(data).map(([key, value]) => `-F "${key}=${value}"`).join(" \\\n ");
}
@@ -66,13 +66,13 @@ export function timeToTimestamp(time) {
let d = new Date();
let matches = time.match(timeRegex);
- if (matches[1] !== undefined) {
+ if (matches[1]) {
d.setHours(matches[1]);
}
- if (matches[2] !== undefined) {
+ if (matches[2]) {
d.setMinutes(matches[2]);
}
- if (matches[3] !== undefined) {
+ if (matches[3]) {
d.setSeconds(matches[3]);
}
@@ -84,7 +84,7 @@ export function timestampToTime(timestamp) {
let hours = d.getHours();
let minutes = "0" + d.getMinutes();
let seconds = "0" + d.getSeconds();
- return hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
+ return hours + ":" + minutes.substr(-2) + ":" + seconds.substr(-2);
}
export function timestampToDateTime(timestamp) {
@@ -100,7 +100,7 @@ export function dateTimeToTime(dateTime) {
let hours = dateTime.getHours();
let minutes = "0" + dateTime.getMinutes();
let seconds = "0" + dateTime.getSeconds();
- return hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
+ return hours + ":" + minutes.substr(-2) + ":" + seconds.substr(-2);
}
export function durationBetween(from, to) {
@@ -131,7 +131,7 @@ export function randomClassName() {
export function getHeaderValue(request, key) {
if (request && request.headers) {
- return request.headers[Object.keys(request.headers).find(k => k.toLowerCase() === key.toLowerCase())];
+ return request.headers[Object.keys(request.headers).find((k) => k.toLowerCase() === key.toLowerCase())];
}
return undefined;
}
@@ -154,7 +154,7 @@ export function updateParams(urlParams, payload) {
params.delete(key);
} else if (Array.isArray(value)) {
params.delete(key);
- value.forEach(v => params.append(key, v));
+ value.forEach((v) => params.append(key, v));
} else {
params.set(key, value);
}
--
cgit v1.2.3-70-g09d2
From 79b8b2fa3e8563c986da8baa3a761f2d4f0c6f47 Mon Sep 17 00:00:00 2001
From: Emiliano Ciavatta
Date: Fri, 16 Oct 2020 19:05:44 +0200
Subject: Minor improvements
---
application_router.go | 2 +-
frontend/src/components/App.js | 29 ++++++++-----
frontend/src/components/Header.js | 12 +++---
frontend/src/components/fields/InputField.js | 2 +-
frontend/src/components/objects/CopyLinkPopover.js | 4 +-
frontend/src/components/pages/ConfigurationPage.js | 12 ++++--
.../src/components/pages/ConfigurationPage.scss | 30 +++++++++----
frontend/src/components/pages/MainPage.js | 50 +++++++++++-----------
frontend/src/components/panels/ConnectionsPane.js | 4 +-
frontend/src/components/panels/SearchPane.js | 6 +--
rules_manager.go | 5 +++
11 files changed, 93 insertions(+), 63 deletions(-)
(limited to 'frontend/src/components/pages/MainPage.js')
diff --git a/application_router.go b/application_router.go
index 656b63e..4048dc5 100644
--- a/application_router.go
+++ b/application_router.go
@@ -39,7 +39,7 @@ func CreateApplicationRouter(applicationContext *ApplicationContext,
router.Use(static.Serve("/", static.LocalFile("./frontend/build", true)))
- for _, path := range []string{"/connections/:id", "/pcaps", "/rules", "/services", "/config"} {
+ for _, path := range []string{"/connections/:id", "/pcaps", "/rules", "/services", "/config", "/searches"} {
router.GET(path, func(c *gin.Context) {
c.File("./frontend/build/index.html")
})
diff --git a/frontend/src/components/App.js b/frontend/src/components/App.js
index e12a99d..96083cd 100644
--- a/frontend/src/components/App.js
+++ b/frontend/src/components/App.js
@@ -16,6 +16,7 @@
*/
import React, {Component} from "react";
+import {BrowserRouter as Router} from "react-router-dom";
import dispatcher from "../dispatcher";
import Notifications from "./Notifications";
import ConfigurationPage from "./pages/ConfigurationPage";
@@ -27,15 +28,7 @@ class App extends Component {
state = {};
componentDidMount() {
- dispatcher.register("notifications", (payload) => {
- if (payload.event === "connected") {
- this.setState({
- connected: true,
- configured: payload.message["is_configured"],
- version: payload.message["version"]
- });
- }
- });
+ dispatcher.register("notifications", this.handleNotifications);
setInterval(() => {
if (document.title.endsWith("❚")) {
@@ -46,16 +39,30 @@ class App extends Component {
}, 500);
}
+ componentWillUnmount() {
+ dispatcher.unregister(this.handleNotifications);
+ }
+
+ handleNotifications = (payload) => {
+ if (payload.event === "connected") {
+ this.setState({
+ connected: true,
+ configured: payload.message["is_configured"],
+ version: payload.message["version"]
+ });
+ }
+ };
+
render() {
return (
- <>
+
{this.state.connected ?
(this.state.configured ? :
this.setState({configured: true})}/>) :
}
- >
+
);
}
}
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index ab16dd1..c46d768 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -27,6 +27,8 @@ import RulesConnectionsFilter from "./filters/RulesConnectionsFilter";
import StringConnectionsFilter from "./filters/StringConnectionsFilter";
import "./Header.scss";
+const classNames = require("classnames");
+
class Header extends Component {
componentDidMount() {
@@ -46,7 +48,7 @@ class Header extends Component {
return (
-
+
{
@@ -56,7 +58,7 @@ class Header extends Component {
-
+ {this.props.configured &&
+
}
-
+ {this.props.configured &&
@@ -89,7 +91,7 @@ class Header extends Component {
-
+
}
);
diff --git a/frontend/src/components/fields/InputField.js b/frontend/src/components/fields/InputField.js
index 823989d..e2ea020 100644
--- a/frontend/src/components/fields/InputField.js
+++ b/frontend/src/components/fields/InputField.js
@@ -75,7 +75,7 @@ class InputField extends Component {
aria-describedby={this.id} onChange={handler} {...inputProps}
readOnly={this.props.readonly}/>
- {type !== "file" && value !== "" &&
+ {type !== "file" && value !== "" && !this.props.readonly &&
handler(null)}>del
diff --git a/frontend/src/components/objects/CopyLinkPopover.js b/frontend/src/components/objects/CopyLinkPopover.js
index fa9266f..b951603 100644
--- a/frontend/src/components/objects/CopyLinkPopover.js
+++ b/frontend/src/components/objects/CopyLinkPopover.js
@@ -37,10 +37,10 @@ class CopyLinkPopover extends Component {
};
render() {
- const copyPopoverContent =
+ const copyPopoverContent =
{this.state.copiedMessage ? Copied! :
Click to copy }
-
+
;
return (
diff --git a/frontend/src/components/pages/ConfigurationPage.js b/frontend/src/components/pages/ConfigurationPage.js
index 4f0ce21..8f9b68b 100644
--- a/frontend/src/components/pages/ConfigurationPage.js
+++ b/frontend/src/components/pages/ConfigurationPage.js
@@ -25,8 +25,10 @@ import ButtonField from "../fields/ButtonField";
import CheckField from "../fields/CheckField";
import InputField from "../fields/InputField";
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 {
@@ -113,9 +115,13 @@ class ConfigurationPage extends Component {
);
return (
-
-
-
+
+
+
+
+
+
+
POST /setup
diff --git a/frontend/src/components/pages/ConfigurationPage.scss b/frontend/src/components/pages/ConfigurationPage.scss
index 4509865..4254547 100644
--- a/frontend/src/components/pages/ConfigurationPage.scss
+++ b/frontend/src/components/pages/ConfigurationPage.scss
@@ -1,18 +1,30 @@
@import "../../colors";
.configuration-page {
- display: flex;
- align-items: center;
- justify-content: center;
- height: 100%;
background-color: $color-primary-0;
- .pane {
- flex-basis: 900px;
- margin-bottom: 200px;
+ .header-title {
+ margin: 50px auto;
}
- .pane-container {
- padding-bottom: 1px;
+ .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;
+ }
+
+ .section-footer {
+ background-color: $color-primary-3;
+ }
}
}
+
diff --git a/frontend/src/components/pages/MainPage.js b/frontend/src/components/pages/MainPage.js
index a542e3f..c4dcd20 100644
--- a/frontend/src/components/pages/MainPage.js
+++ b/frontend/src/components/pages/MainPage.js
@@ -16,7 +16,7 @@
*/
import React, {Component} from "react";
-import {BrowserRouter as Router, Route, Switch} from "react-router-dom";
+import {Route, Switch} from "react-router-dom";
import Filters from "../dialogs/Filters";
import Header from "../Header";
import Connections from "../panels/ConnectionsPane";
@@ -42,34 +42,32 @@ class MainPage extends Component {
return (
-
-
- this.setState({filterWindowOpen: true})}/>
-
-
-
-
- this.setState({selectedConnection: c})}/>
-
-
-
- }/>
- }/>
- }/>
- }/>
- }/>
- }/>
-
-
+
+ this.setState({filterWindowOpen: true})} configured={true}/>
+
- {modal}
+
+
+ this.setState({selectedConnection: c})}/>
-
-
-
+
+
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
-
+
+ {modal}
+
+
+
+
+
);
}
diff --git a/frontend/src/components/panels/ConnectionsPane.js b/frontend/src/components/panels/ConnectionsPane.js
index 457c249..6418b3e 100644
--- a/frontend/src/components/panels/ConnectionsPane.js
+++ b/frontend/src/components/panels/ConnectionsPane.js
@@ -226,11 +226,11 @@ class ConnectionsPane extends Component {
}
loadRules = async () => {
- return backend.get("/api/rules").then(res => this.setState({rules: res.json}));
+ return backend.get("/api/rules").then((res) => this.setState({rules: res.json}));
};
loadServices = async () => {
- return backend.get("/api/services").then(res => this.setState({services: res.json}));
+ return backend.get("/api/services").then((res) => this.setState({services: res.json}));
};
render() {
diff --git a/frontend/src/components/panels/SearchPane.js b/frontend/src/components/panels/SearchPane.js
index 4c9f229..4ef5632 100644
--- a/frontend/src/components/panels/SearchPane.js
+++ b/frontend/src/components/panels/SearchPane.js
@@ -236,13 +236,13 @@ class SearchPane extends Component {
})}
name="terms" min={3} inline allowNew={true}
readonly={regexOptionsModified || options["text_search"]["exact_phrase"]}
- onChange={(tags) => this.updateParam((s) => s["text_search"].terms = tags.map(t => t.name))}/>
-
{
+ onChange={(tags) => this.updateParam((s) => s["text_search"].terms = tags.map((t) => t.name))}/>
+ {
return {name: t};
})}
name="excluded_terms" min={3} inline allowNew={true}
readonly={regexOptionsModified || options["text_search"]["exact_phrase"]}
- onChange={(tags) => this.updateParam((s) => s["text_search"]["excluded_terms"] = tags.map(t => t.name))}/>
+ onChange={(tags) => this.updateParam((s) => s["text_search"]["excluded_terms"] = tags.map((t) => t.name))}/>
or
diff --git a/rules_manager.go b/rules_manager.go
index a6d969f..5d6cded 100644
--- a/rules_manager.go
+++ b/rules_manager.go
@@ -24,6 +24,7 @@ import (
"github.com/flier/gohs/hyperscan"
"github.com/go-playground/validator/v10"
log "github.com/sirupsen/logrus"
+ "sort"
"sync"
"time"
)
@@ -213,6 +214,10 @@ func (rm *rulesManagerImpl) GetRules() []Rule {
rules = append(rules, rule)
}
+ sort.Slice(rules, func(i, j int) bool {
+ return rules[i].ID.Timestamp().Before(rules[j].ID.Timestamp())
+ })
+
return rules
}
--
cgit v1.2.3-70-g09d2