diff options
author | Emiliano Ciavatta | 2020-10-08 20:17:04 +0000 |
---|---|---|
committer | Emiliano Ciavatta | 2020-10-08 20:17:04 +0000 |
commit | d203f3c7e3bcaa20895c0f32f348cd1513ae9876 (patch) | |
tree | bc5beea659f6d1717a0e31b0ee10cde6699da2ad /frontend/src/components/ConnectionContent.js | |
parent | e1198433a63eec2c900ac8986dbf0ae7db16b777 (diff) |
Frontend folder structure refactor
Diffstat (limited to 'frontend/src/components/ConnectionContent.js')
-rw-r--r-- | frontend/src/components/ConnectionContent.js | 243 |
1 files changed, 0 insertions, 243 deletions
diff --git a/frontend/src/components/ConnectionContent.js b/frontend/src/components/ConnectionContent.js deleted file mode 100644 index b468277..0000000 --- a/frontend/src/components/ConnectionContent.js +++ /dev/null @@ -1,243 +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 <http://www.gnu.org/licenses/>. - */ - -import React, {Component} from 'react'; -import './ConnectionContent.scss'; -import {Row} from 'react-bootstrap'; -import MessageAction from "./MessageAction"; -import backend from "../backend"; -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"; - -const classNames = require('classnames'); - -class ConnectionContent extends Component { - - constructor(props) { - super(props); - this.state = { - loading: false, - connectionContent: null, - format: "default", - tryParse: true, - messageActionDialog: null - }; - - this.validFormats = ["default", "hex", "hexdump", "base32", "base64", "ascii", "binary", "decimal", "octal"]; - } - - componentDidMount() { - if (this.props.connection != null) { - this.loadStream(); - } - - document.title = "caronte:~/$"; - } - - componentDidUpdate(prevProps, prevState, snapshot) { - if (this.props.connection != null && ( - this.props.connection !== prevProps.connection || this.state.format !== prevState.format)) { - this.closeRenderWindow(); - this.loadStream(); - } - } - - componentWillUnmount() { - this.closeRenderWindow(); - } - - loadStream = () => { - this.setState({loading: true}); - // TODO: limit workaround. - backend.get(`/api/streams/${this.props.connection.id}?format=${this.state.format}&limit=999999`).then(res => { - this.setState({ - connectionContent: res.json, - loading: false - }); - }); - }; - - setFormat = (format) => { - if (this.validFormats.includes(format)) { - this.setState({format: format}); - } - }; - - tryParseConnectionMessage = (connectionMessage) => { - if (connectionMessage.metadata == null) { - return connectionMessage.content; - } - if (connectionMessage["is_metadata_continuation"]) { - return <span style={{"fontSize": "12px"}}>**already parsed in previous messages**</span>; - } - - let unrollMap = (obj) => obj == null ? null : Object.entries(obj).map(([key, value]) => - <p key={key}><strong>{key}</strong>: {value}</p> - ); - - let m = connectionMessage.metadata; - switch (m.type) { - case "http-request": - let url = <i><u><a href={"http://" + m.host + m.url} target="_blank" - rel="noopener noreferrer">{m.host}{m.url}</a></u></i>; - return <span className="type-http-request"> - <p style={{"marginBottom": "7px"}}><strong>{m.method}</strong> {url} {m.protocol}</p> - {unrollMap(m.headers)} - <div style={{"margin": "20px 0"}}>{m.body}</div> - {unrollMap(m.trailers)} - </span>; - case "http-response": - const contentType = getHeaderValue(m, "Content-Type"); - let body = m.body; - if (contentType && contentType.includes("application/json")) { - try { - const json = JSON.parse(m.body); - body = <ReactJson src={json} theme="grayscale" collapsed={false} displayDataTypes={false}/>; - } catch (e) { - console.log(e); - } - } - - return <span className="type-http-response"> - <p style={{"marginBottom": "7px"}}>{m.protocol} <strong>{m.status}</strong></p> - {unrollMap(m.headers)} - <div style={{"margin": "20px 0"}}>{body}</div> - {unrollMap(m.trailers)} - </span>; - default: - return connectionMessage.content; - } - }; - - connectionsActions = (connectionMessage) => { - if (connectionMessage.metadata == null) { //} || !connectionMessage.metadata["reproducers"]) { - return null; - } - - const m = connectionMessage.metadata; - switch (m.type) { - case "http-request" : - if (!connectionMessage.metadata["reproducers"]) { - return; - } - return Object.entries(connectionMessage.metadata["reproducers"]).map(([actionName, actionValue]) => - <ButtonField small key={actionName + "_button"} name={actionName} onClick={() => { - this.setState({ - messageActionDialog: <MessageAction actionName={actionName} actionValue={actionValue} - onHide={() => this.setState({messageActionDialog: null})}/> - }); - }}/> - ); - case "http-response": - const contentType = getHeaderValue(m, "Content-Type"); - - if (contentType && contentType.includes("text/html")) { - return <ButtonField small name="render_html" onClick={() => { - let w; - if (this.state.renderWindow && !this.state.renderWindow.closed) { - w = this.state.renderWindow; - } else { - w = window.open("", "", "width=900, height=600, scrollbars=yes"); - this.setState({renderWindow: w}); - } - w.document.body.innerHTML = DOMPurify.sanitize(m.body); - w.focus(); - }}/>; - } - break; - default: - return null; - } - }; - - downloadStreamRaw = (value) => { - backend.download(`/api/streams/${this.props.connection.id}/download?format=${this.state.format}&type=${value}`) - .then(res => downloadBlob(res.blob, `${this.props.connection.id}-${value}-${this.state.format}.txt`)) - .catch(_ => log.error("Failed to download stream messages")); - }; - - closeRenderWindow = () => { - if (this.state.renderWindow) { - this.state.renderWindow.close(); - } - }; - - render() { - const conn = this.props.connection; - const content = this.state.connectionContent; - - if (content == null) { - return <div>select a connection to view</div>; - } - - let payload = content.map((c, i) => - <div key={`content-${i}`} - className={classNames("connection-message", c["from_client"] ? "from-client" : "from-server")}> - <div className="connection-message-header container-fluid"> - <div className="row"> - <div className="connection-message-info col"> - <span><strong>offset</strong>: {c.index}</span> | <span><strong>timestamp</strong>: {c.timestamp} - </span> | <span><strong>retransmitted</strong>: {c["is_retransmitted"] ? "yes" : "no"}</span> - </div> - <div className="connection-message-actions col-auto">{this.connectionsActions(c)}</div> - </div> - </div> - <div className="connection-message-label">{c["from_client"] ? "client" : "server"}</div> - <div - className="message-content"> - {this.state.tryParse && this.state.format === "default" ? this.tryParseConnectionMessage(c) : c.content} - </div> - </div> - ); - - return ( - <div className="connection-content"> - <div className="connection-content-header container-fluid"> - <Row> - <div className="header-info col"> - <span><strong>flow</strong>: {conn["ip_src"]}:{conn["port_src"]} -> {conn["ip_dst"]}:{conn["port_dst"]}</span> - <span> | <strong>timestamp</strong>: {conn["started_at"]}</span> - </div> - <div className="header-actions col-auto"> - <ChoiceField name="format" inline small onlyName - keys={["default", "hex", "hexdump", "base32", "base64", "ascii", "binary", "decimal", "octal"]} - values={["plain", "hex", "hexdump", "base32", "base64", "ascii", "binary", "decimal", "octal"]} - onChange={this.setFormat} value={this.state.value}/> - - <ChoiceField name="view_as" inline small onlyName keys={["default"]} values={["default"]}/> - - <ChoiceField name="download_as" inline small onlyName onChange={this.downloadStreamRaw} - keys={["nl_separated", "only_client", "only_server", "pwntools"]} - values={["nl_separated", "only_client", "only_server", "pwntools"]}/> - </div> - </Row> - </div> - - <pre>{payload}</pre> - {this.state.messageActionDialog} - </div> - ); - } - -} - - -export default ConnectionContent; |