diff --git a/CHANGELOG.md b/CHANGELOG.md
index 295fc9442..18a655320 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,19 @@ file.
The format is based on [Keep a
Changelog](https://keepachangelog.com/en/1.0.0/).
+## [Unreleased]
+### Added
+
+### Changed
+
+### Removed
+
+### Fixed
+- A bug in network map page that caused delay of telemetry log loading. #1545
+
+### Security
+
+
## [1.12.0] - 2021-10-27
### Added
- A new exploiter that allows propagation via PowerShell Remoting. #1246
diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json
index da6200c25..f7c86151b 100644
--- a/monkey/monkey_island/cc/ui/package.json
+++ b/monkey/monkey_island/cc/ui/package.json
@@ -15,7 +15,7 @@
"release:patch": "npm version patch && npm publish && git push --follow-tags",
"serve": "node server.js --env=dev",
"serve:dist": "node server.js --env=dist",
- "start": "webpack-dev-server --mode development --open --history-api-fallback --port 8000",
+ "start": "webpack-dev-server --mode development --open --history-api-fallback --port 8000 --host local-ip",
"snyk-protect": "snyk protect",
"prepare": "npm run snyk-protect"
},
diff --git a/monkey/monkey_island/cc/ui/src/components/map/TelemetryLog.tsx b/monkey/monkey_island/cc/ui/src/components/map/TelemetryLog.tsx
new file mode 100644
index 000000000..45e303a9c
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/map/TelemetryLog.tsx
@@ -0,0 +1,102 @@
+import React, {useEffect, useRef, useState} from 'react';
+import AuthComponent from '../AuthComponent';
+import LoadingIcon from '../ui-components/LoadingIcon';
+
+
+const authComponent = new AuthComponent({});
+const telemetryUpdatePeriod = 5000; // UI will fetch telemetries every N milliseconds.
+
+const TelemetryLog = (props: { onStatusChange: Function }) => {
+
+ let [telemetriesLoading, setTelemetriesLoading] = useState(true);
+ let [telemetryUpdateInProgress, setTelemetryUpdateInProgress] = useState(false);
+ let [telemetry, setTelemetry] = useState([]);
+ let [lastTelemetryTimestamp, setLastTelemetryTimestamp] = useState(null);
+ let [isScrolledUp, setIsScrolledUp] = useState(false);
+ let [telemetryLines, setTelemetryLines] = useState(0);
+ let [telemetryCurrentLine, setTelemetryCurrentLine] = useState(0);
+
+ let scrollTop = 0;
+ const telemetryConsole = useRef(null);
+
+ useEffect(() => {
+ updateTelemetryFromServer();
+ const interval = setInterval(updateTelemetryFromServer, telemetryUpdatePeriod);
+ return function cleanup() {
+ clearInterval(interval);
+ };
+ }, []);
+
+ function updateTelemetryFromServer() {
+ if (telemetryUpdateInProgress) {
+ return
+ }
+ setTelemetryUpdateInProgress(true);
+ authComponent.authFetch('/api/telemetry-feed?timestamp=' + lastTelemetryTimestamp)
+ .then(res => res.json())
+ .then(res => {
+ if ('telemetries' in res) {
+ let newTelem = telemetry.concat(res['telemetries']);
+ setTelemetry(newTelem);
+ setLastTelemetryTimestamp(res['timestamp']);
+ setTelemetryUpdateInProgress(false);
+ setTelemetriesLoading(false);
+ props.onStatusChange();
+
+ let telemConsoleRef = telemetryConsole.current;
+ if (!isScrolledUp) {
+ telemConsoleRef.scrollTop = telemConsoleRef.scrollHeight - telemConsoleRef.clientHeight;
+ scrollTop = telemConsoleRef.scrollTop;
+ }
+ }
+ });
+ }
+
+ function handleScroll(e) {
+ let element = e.target;
+
+ let telemetryStyle = window.getComputedStyle(element);
+ let telemetryLineHeight = parseInt((telemetryStyle.lineHeight).replace('px', ''));
+
+ setIsScrolledUp((element.scrollTop < scrollTop));
+ setTelemetryCurrentLine(Math.trunc(element.scrollTop / telemetryLineHeight) + 1);
+ setTelemetryLines(Math.trunc(element.scrollHeight / telemetryLineHeight));
+ }
+
+ function renderTelemetryConsole() {
+ return (
+
+ {
+ telemetry.map(renderTelemetryEntry)
+ }
+
+ );
+ }
+
+ function renderTelemetryEntry(telemetry) {
+ return (
+
+ {telemetry.timestamp}
+ {telemetry.hostname}:
+ {telemetry.brief}
+
+ );
+ }
+
+ function renderTelemetryLineCount() {
+ return (
+
+ [{telemetryCurrentLine}/{telemetryLines}]
+
+ );
+ }
+
+ return (
+
+ {telemetriesLoading && }
+ {renderTelemetryLineCount()}
+ {renderTelemetryConsole()}
+
);
+}
+
+export default TelemetryLog;
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js
index da11c7ed6..6026cebb6 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js
@@ -10,6 +10,7 @@ import {getOptions, edgeGroupToColor} from 'components/map/MapOptions';
import AuthComponent from '../AuthComponent';
import '../../styles/components/Map.scss';
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
+import TelemetryLog from '../map/TelemetryLog';
class MapPageComponent extends AuthComponent {
constructor(props) {
@@ -20,17 +21,8 @@ class MapPageComponent extends AuthComponent {
selected: null,
selectedType: null,
killPressed: false,
- showKillDialog: false,
- telemetry: [],
- telemetryLastTimestamp: null,
- isScrolledUp: false,
- telemetryLines: 0,
- telemetryCurrentLine: 0,
- telemetryUpdateInProgress: false
+ showKillDialog: false
};
- this.telemConsole = React.createRef();
- this.handleScroll = this.handleScroll.bind(this);
- this.scrollTop = 0;
}
events = {
@@ -40,7 +32,7 @@ class MapPageComponent extends AuthComponent {
componentDidMount() {
this.getNodeStateListFromServer();
this.updateMapFromServer();
- this.interval = setInterval(this.timedEvents, 5000);
+ this.interval = setInterval(this.updateMapFromServer, 5000);
}
componentWillUnmount() {
@@ -55,11 +47,6 @@ class MapPageComponent extends AuthComponent {
});
};
- timedEvents = () => {
- this.updateMapFromServer();
- this.updateTelemetryFromServer();
- };
-
updateMapFromServer = () => {
this.authFetch('/api/netmap')
.then(res => res.json())
@@ -74,33 +61,6 @@ class MapPageComponent extends AuthComponent {
});
};
- updateTelemetryFromServer = () => {
- if (this.state.telemetryUpdateInProgress) {
- return
- }
- this.setState({telemetryUpdateInProgress: true});
- this.authFetch('/api/telemetry-feed?timestamp=' + this.state.telemetryLastTimestamp)
- .then(res => res.json())
- .then(res => {
- if ('telemetries' in res) {
- let newTelem = this.state.telemetry.concat(res['telemetries']);
- this.setState(
- {
- telemetry: newTelem,
- telemetryLastTimestamp: res['timestamp'],
- telemetryUpdateInProgress: false
- });
- this.props.onStatusChange();
-
- let telemConsoleRef = this.telemConsole.current;
- if (!this.state.isScrolledUp) {
- telemConsoleRef.scrollTop = telemConsoleRef.scrollHeight - telemConsoleRef.clientHeight;
- this.scrollTop = telemConsoleRef.scrollTop;
- }
- }
- });
- };
-
selectionChanged(event) {
if (event.nodes.length === 1) {
this.authFetch('/api/netmap/node?id=' + event.nodes[0])
@@ -157,46 +117,6 @@ class MapPageComponent extends AuthComponent {
)
};
- renderTelemetryEntry(telemetry) {
- return (
-
- {telemetry.timestamp}
- {telemetry.hostname}:
- {telemetry.brief}
-
- );
- }
-
- handleScroll(e) {
- let element = e.target;
-
- let telemetryStyle = window.getComputedStyle(element);
- let telemetryLineHeight = parseInt((telemetryStyle.lineHeight).replace('px', ''));
-
- this.setState({
- isScrolledUp: (element.scrollTop < this.scrollTop),
- telemetryCurrentLine: Math.trunc(element.scrollTop / telemetryLineHeight) + 1,
- telemetryLines: Math.trunc(element.scrollHeight / telemetryLineHeight)
- });
- }
-
- renderTelemetryConsole() {
- return (
-
- {
- this.state.telemetry.map(this.renderTelemetryEntry)
- }
-
- );
- }
-
- renderTelemetryLineCount() {
- return (
-
- [{this.state.telemetryCurrentLine}/{this.state.telemetryLines}]
-
- );
- }
render() {
return (
@@ -220,10 +140,9 @@ class MapPageComponent extends AuthComponent {
Island Communication
- {this.renderTelemetryLineCount()}
- {this.renderTelemetryConsole()}
+
diff --git a/monkey/monkey_island/cc/ui/src/styles/components/Map.scss b/monkey/monkey_island/cc/ui/src/styles/components/Map.scss
index ebeb7c687..adbfe8b05 100644
--- a/monkey/monkey_island/cc/ui/src/styles/components/Map.scss
+++ b/monkey/monkey_island/cc/ui/src/styles/components/Map.scss
@@ -46,3 +46,10 @@
width: 100%;
padding-bottom: 10px;
}
+
+.telemetry-log-section .loading-icon{
+ position: relative;
+ color: white;
+ z-index: 5;
+ margin-top: 30px;
+}