aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorEmiliano Ciavatta2020-09-30 20:57:25 +0000
committerEmiliano Ciavatta2020-09-30 20:57:25 +0000
commitd6e2aaad41f916c2080c59cf7b4e42bf87a1a03f (patch)
tree57545a722a62d2279bfcd2e36f1cbd1da5a5736a /frontend
parentd5ed31be3b6c97f92be4e94b70d32d1b89932ae9 (diff)
Complete setup page
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/components/Connection.js18
-rw-r--r--frontend/src/components/Connection.scss4
-rw-r--r--frontend/src/components/ConnectionContent.js2
-rw-r--r--frontend/src/components/ConnectionMatchedRules.js29
-rw-r--r--frontend/src/components/ConnectionMatchedRules.scss23
-rw-r--r--frontend/src/components/panels/ConfigurationPane.js162
-rw-r--r--frontend/src/components/panels/ConfigurationPane.scss19
-rw-r--r--frontend/src/components/panels/MainPane.js56
-rw-r--r--frontend/src/components/panels/MainPane.scss23
-rw-r--r--frontend/src/components/panels/PcapPane.js5
-rw-r--r--frontend/src/components/panels/RulePane.js9
-rw-r--r--frontend/src/components/panels/ServicePane.js2
-rw-r--r--frontend/src/index.scss7
-rw-r--r--frontend/src/views/App.js58
-rw-r--r--frontend/src/views/App.scss15
-rw-r--r--frontend/src/views/Config.js242
-rw-r--r--frontend/src/views/Config.scss55
-rw-r--r--frontend/src/views/Connections.js42
-rw-r--r--frontend/src/views/Filters.js1
-rw-r--r--frontend/src/views/Header.js7
-rw-r--r--frontend/src/views/MainPane.js59
-rw-r--r--frontend/src/views/MainPane.scss6
-rw-r--r--frontend/src/views/Services.js210
-rw-r--r--frontend/src/views/Services.scss32
-rw-r--r--frontend/yarn.lock184
25 files changed, 482 insertions, 788 deletions
diff --git a/frontend/src/components/Connection.js b/frontend/src/components/Connection.js
index 95b27ff..44f9f18 100644
--- a/frontend/src/components/Connection.js
+++ b/frontend/src/components/Connection.js
@@ -2,9 +2,11 @@ import React, {Component} from 'react';
import './Connection.scss';
import {Form, OverlayTrigger, Popover} from "react-bootstrap";
import backend from "../backend";
-import {durationBetween, formatSize} from "../utils";
+import {dateTimeToTime, durationBetween, formatSize} from "../utils";
import ButtonField from "./fields/ButtonField";
+const classNames = require('classnames');
+
class Connection extends Component {
constructor(props) {
@@ -60,14 +62,6 @@ class Connection extends Component {
<span>Closed at {closedAt.toLocaleDateString() + " " + closedAt.toLocaleTimeString()}</span>
</div>;
- let classes = "connection";
- if (this.props.selected) {
- classes += " connection-selected";
- }
- if (this.props.containsFlag) {
- classes += " contains-flag";
- }
-
const popoverFor = function (name, content) {
return <Popover id={`popover-${name}-${conn.id}`} className="connection-popover">
<Popover.Content>
@@ -88,7 +82,8 @@ class Connection extends Component {
</div>;
return (
- <tr className={classes}>
+ <tr className={classNames("connection", {"connection-selected": this.props.selected},
+ {"has-matched-rules": conn.matched_rules.length > 0})}>
<td>
<span className="connection-service">
<ButtonField small fullSpan color={serviceColor} name={serviceName}
@@ -99,6 +94,7 @@ class Connection extends Component {
<td className="clickable" onClick={this.props.onSelected}>{conn.port_src}</td>
<td className="clickable" onClick={this.props.onSelected}>{conn.ip_dst}</td>
<td className="clickable" onClick={this.props.onSelected}>{conn.port_dst}</td>
+ <td className="clickable" onClick={this.props.onSelected}>{dateTimeToTime(conn.started_at)}</td>
<td className="clickable" onClick={this.props.onSelected}>
<OverlayTrigger trigger={["focus", "hover"]} placement="right"
overlay={popoverFor("duration", timeInfo)}>
@@ -107,7 +103,7 @@ class Connection extends Component {
</td>
<td className="clickable" onClick={this.props.onSelected}>{formatSize(conn.client_bytes)}</td>
<td className="clickable" onClick={this.props.onSelected}>{formatSize(conn.server_bytes)}</td>
- <td className="contains-flag">
+ <td>
{/*<OverlayTrigger trigger={["focus", "hover"]} placement="right"*/}
{/* overlay={popoverFor("hide", <span>Hide this connection from the list</span>)}>*/}
{/* <span className={"connection-icon" + (conn.hidden ? " icon-enabled" : "")}*/}
diff --git a/frontend/src/components/Connection.scss b/frontend/src/components/Connection.scss
index cb9eb5f..97ef0a4 100644
--- a/frontend/src/components/Connection.scss
+++ b/frontend/src/components/Connection.scss
@@ -39,8 +39,8 @@
background-color: $color-primary-2;
}
- &.contains-flag {
- border-right: 3px solid $color-secondary-2;
+ &.has-matched-rules {
+ border-bottom: 0;
}
}
diff --git a/frontend/src/components/ConnectionContent.js b/frontend/src/components/ConnectionContent.js
index 6bc0c96..ccaec0b 100644
--- a/frontend/src/components/ConnectionContent.js
+++ b/frontend/src/components/ConnectionContent.js
@@ -63,7 +63,7 @@ class ConnectionContent extends Component {
}
let unrollMap = (obj) => obj == null ? null : Object.entries(obj).map(([key, value]) =>
- <p><strong>{key}</strong>: {value}</p>
+ <p key={key}><strong>{key}</strong>: {value}</p>
);
let m = connectionMessage.metadata;
diff --git a/frontend/src/components/ConnectionMatchedRules.js b/frontend/src/components/ConnectionMatchedRules.js
new file mode 100644
index 0000000..21f2a92
--- /dev/null
+++ b/frontend/src/components/ConnectionMatchedRules.js
@@ -0,0 +1,29 @@
+import React, {Component} from 'react';
+import './ConnectionMatchedRules.scss';
+import ButtonField from "./fields/ButtonField";
+
+class ConnectionMatchedRules extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ };
+ }
+
+ render() {
+ const matchedRules = this.props.matchedRules.map(mr => {
+ const rule = this.props.rules.find(r => r.id === mr);
+ return <ButtonField key={mr} onClick={() => this.props.addMatchedRulesFilter(rule.id)} name={rule.name}
+ color={rule.color} small />;
+ });
+
+ return (
+ <tr className="connection-matches">
+ <td className="row-label">matched_rules:</td>
+ <td className="rule-buttons" colSpan={9}>{matchedRules}</td>
+ </tr>
+ );
+ }
+}
+
+export default ConnectionMatchedRules;
diff --git a/frontend/src/components/ConnectionMatchedRules.scss b/frontend/src/components/ConnectionMatchedRules.scss
new file mode 100644
index 0000000..ed18f3c
--- /dev/null
+++ b/frontend/src/components/ConnectionMatchedRules.scss
@@ -0,0 +1,23 @@
+@import '../colors.scss';
+
+.connection-matches {
+ background-color: $color-primary-0;
+
+ .rule-buttons {
+ padding: 0;
+ }
+
+ .button-field {
+ display: inline-block;
+ margin-right: 5px;
+
+ button {
+ font-size: 0.8em;
+ padding: 2px 10px;
+ }
+ }
+
+ .row-label {
+ font-size: 0.8em;
+ }
+}
diff --git a/frontend/src/components/panels/ConfigurationPane.js b/frontend/src/components/panels/ConfigurationPane.js
new file mode 100644
index 0000000..10309f6
--- /dev/null
+++ b/frontend/src/components/panels/ConfigurationPane.js
@@ -0,0 +1,162 @@
+import React, {Component} from 'react';
+import './common.scss';
+import './ConfigurationPane.scss';
+import LinkPopover from "../objects/LinkPopover";
+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";
+
+class ConfigurationPane extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ settings: {
+ "config": {
+ "server_address": "",
+ "flag_regex": "",
+ "auth_required": false
+ },
+ "accounts": {
+ }
+ },
+ newUsername: "",
+ newPassword: ""
+ };
+ }
+
+ saveSettings = () => {
+ if (this.validateSettings(this.state.settings)) {
+ backend.post("/setup", this.state.settings).then(_ => {
+ this.props.onConfigured();
+ }).catch(res => {
+ this.setState({setupStatusCode: res.status, setupResponse: JSON.stringify(res.json)});
+ });
+ }
+ };
+
+ validateSettings = (settings) => {
+ let valid = true;
+ if (!validation.isValidAddress(settings.config.server_address, true)) {
+ this.setState({serverAddressError: "invalid ip_address"});
+ valid = false;
+ }
+ if (settings.config.flag_regex.length < 8) {
+ this.setState({flagRegexError: "flag_regex.length < 8"});
+ valid = false;
+ }
+
+ return valid;
+ };
+
+ updateParam = (callback) => {
+ callback(this.state.settings);
+ this.setState({settings: this.state.settings});
+ };
+
+ addAccount = () => {
+ if (this.state.newUsername.length !== 0 && this.state.newPassword.length !== 0) {
+ const settings = this.state.settings;
+ settings.accounts[this.state.newUsername] = this.state.newPassword;
+
+ this.setState({
+ newUsername: "",
+ newPassword: "",
+ settings: settings
+ });
+ } else {
+ this.setState({
+ newUsernameActive: this.state.newUsername.length === 0,
+ newPasswordActive: this.state.newPassword.length === 0
+ });
+ }
+ };
+
+ render() {
+ const settings = this.state.settings;
+ const curlCommand = createCurlCommand("/setup", "POST", settings);
+
+ const accounts = Object.entries(settings.accounts).map(([username, password]) =>
+ <tr key={username}>
+ <td>{username}</td>
+ <td><LinkPopover text="******" content={password} /></td>
+ <td><ButtonField variant="red" small rounded name="delete"
+ onClick={() => this.updateParam((s) => delete s.accounts[username]) }/></td>
+ </tr>).concat(<tr key={"new_account"}>
+ <td><InputField value={this.state.newUsername} small active={this.state.newUsernameActive}
+ onChange={(v) => this.setState({newUsername: v})} /></td>
+ <td><InputField value={this.state.newPassword} small active={this.state.newPasswordActive}
+ onChange={(v) => this.setState({newPassword: v})} /></td>
+ <td><ButtonField variant="green" small rounded name="add" onClick={this.addAccount}/></td>
+ </tr>);
+
+ return (
+ <div className="configuration-pane">
+ <div className="pane">
+ <div className="pane-container">
+ <div className="pane-section">
+ <div className="section-header">
+ <span className="api-request">POST /setup</span>
+ <span className="api-response"><LinkPopover text={this.state.setupStatusCode}
+ content={this.state.setupResponse}
+ placement="left" /></span>
+ </div>
+
+ <div className="section-content">
+ <Container className="p-0">
+ <Row>
+ <Col>
+ <InputField name="server_address" value={settings.config.server_address}
+ error={this.state.serverAddressError}
+ onChange={(v) => this.updateParam((s) => s.config.server_address = v)} />
+ <InputField name="flag_regex" value={settings.config.flag_regex}
+ onChange={(v) => this.updateParam((s) => s.config.flag_regex = v)}
+ error={this.state.flagRegexError} />
+ <div style={{"marginTop": "10px"}}>
+ <CheckField checked={settings.config.auth_required} name="auth_required"
+ onChange={(v) => this.updateParam((s) => s.config.auth_required = v)}/>
+ </div>
+
+ </Col>
+
+ <Col>
+ accounts:
+ <div className="section-table">
+ <Table borderless size="sm">
+ <thead>
+ <tr>
+ <th>username</th>
+ <th>password</th>
+ <th>actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ {accounts}
+ </tbody>
+ </Table>
+ </div>
+ </Col>
+ </Row>
+ </Container>
+
+ <TextField value={curlCommand} rows={4} readonly small={true}/>
+ </div>
+
+ <div className="section-footer">
+ <ButtonField variant="green" name="save" bordered onClick={this.saveSettings} />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
+export default ConfigurationPane;
diff --git a/frontend/src/components/panels/ConfigurationPane.scss b/frontend/src/components/panels/ConfigurationPane.scss
new file mode 100644
index 0000000..955d2bc
--- /dev/null
+++ b/frontend/src/components/panels/ConfigurationPane.scss
@@ -0,0 +1,19 @@
+@import '../../colors';
+
+.configuration-pane {
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: $color-primary-0;
+
+ .pane {
+ flex-basis: 900px;
+ margin-bottom: 200px;
+ }
+
+ .pane-container {
+ padding-bottom: 1px;
+ }
+
+}
diff --git a/frontend/src/components/panels/MainPane.js b/frontend/src/components/panels/MainPane.js
new file mode 100644
index 0000000..3202d6d
--- /dev/null
+++ b/frontend/src/components/panels/MainPane.js
@@ -0,0 +1,56 @@
+import React, {Component} from 'react';
+import './common.scss';
+import './MainPane.scss';
+import Connections from "../../views/Connections";
+import ConnectionContent from "../ConnectionContent";
+import {Route, Switch, withRouter} from "react-router-dom";
+import PcapPane from "./PcapPane";
+import backend from "../../backend";
+import RulePane from "./RulePane";
+import ServicePane from "./ServicePane";
+
+class MainPane extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedConnection: null,
+ loading: false
+ };
+ }
+
+ componentDidMount() {
+ const match = this.props.location.pathname.match(/^\/connections\/([a-f0-9]{24})$/);
+ if (match != null) {
+ this.setState({loading: true});
+ backend.get(`/api/connections/${match[1]}`)
+ .then(res => this.setState({selectedConnection: res.json, loading: false}))
+ .catch(error => console.log(error));
+ }
+ }
+
+ render() {
+ return (
+ <div className="main-pane">
+ <div className="pane connections-pane">
+ {
+ !this.state.loading &&
+ <Connections onSelected={(c) => this.setState({selectedConnection: c})}
+ initialConnection={this.state.selectedConnection} />
+ }
+ </div>
+ <div className="pane details-pane">
+ <Switch>
+ <Route path="/pcaps" children={<PcapPane />} />
+ <Route path="/rules" children={<RulePane />} />
+ <Route path="/services" children={<ServicePane />} />
+ <Route exact path="/connections/:id" children={<ConnectionContent connection={this.state.selectedConnection} />} />
+ <Route children={<ConnectionContent />} />
+ </Switch>
+ </div>
+ </div>
+ );
+ }
+}
+
+export default withRouter(MainPane);
diff --git a/frontend/src/components/panels/MainPane.scss b/frontend/src/components/panels/MainPane.scss
new file mode 100644
index 0000000..04be347
--- /dev/null
+++ b/frontend/src/components/panels/MainPane.scss
@@ -0,0 +1,23 @@
+@import '../../colors';
+
+.main-pane {
+ height: 100%;
+ display: flex;
+ padding: 0 15px;
+ background-color: $color-primary-2;
+
+ .pane {
+ flex: 1;
+ }
+
+ .connections-pane {
+ flex: 1 0;
+ margin-right: 7.5px;
+ }
+
+ .details-pane {
+ flex: 1 1;
+ margin-left: 7.5px;
+ }
+
+}
diff --git a/frontend/src/components/panels/PcapPane.js b/frontend/src/components/panels/PcapPane.js
index e83e3da..7b3fde6 100644
--- a/frontend/src/components/panels/PcapPane.js
+++ b/frontend/src/components/panels/PcapPane.js
@@ -109,7 +109,7 @@ class PcapPane extends Component {
render() {
let sessions = this.state.sessions.map(s =>
- <tr className="table-row">
+ <tr key={s.id} className="table-row">
<td>{s["id"].substring(0, 8)}</td>
<td>{dateTimeToTime(s["started_at"])}</td>
<td>{durationBetween(s["started_at"], s["completed_at"])}</td>
@@ -119,8 +119,7 @@ class PcapPane extends Component {
<td><LinkPopover text={Object.keys(s["packets_per_service"]).length + " services"}
content={JSON.stringify(s["packets_per_service"])}
placement="left"/></td>
- <td className="table-cell-action"><a target="_blank" rel="noopener noreferrer"
- href={"/api/pcap/sessions/" + s["id"] + "/download"}>download</a>
+ <td className="table-cell-action"><a href={"/api/pcap/sessions/" + s["id"] + "/download"}>download</a>
</td>
</tr>
);
diff --git a/frontend/src/components/panels/RulePane.js b/frontend/src/components/panels/RulePane.js
index 7cb849c..49364d2 100644
--- a/frontend/src/components/panels/RulePane.js
+++ b/frontend/src/components/panels/RulePane.js
@@ -13,6 +13,7 @@ import ChoiceField from "../fields/ChoiceField";
import ButtonField from "../fields/ButtonField";
import validation from "../../validation";
import LinkPopover from "../objects/LinkPopover";
+import {randomClassName} from "../../utils";
const classNames = require('classnames');
const _ = require('lodash');
@@ -220,7 +221,7 @@ class RulePane extends Component {
const pattern = this.state.selectedPattern || this.state.newPattern;
let rules = this.state.rules.map(r =>
- <tr onClick={() => {
+ <tr key={r.id} onClick={() => {
this.reset();
this.setState({selectedRule: _.cloneDeep(r)});
}} className={classNames("row-small", "row-clickable", {"row-selected": rule.id === r.id })}>
@@ -235,7 +236,7 @@ class RulePane extends Component {
rule.patterns.concat(this.state.newPattern) :
rule.patterns
).map(p => p === pattern ?
- <tr>
+ <tr key={randomClassName()}>
<td style={{"width": "500px"}}>
<InputField small active={this.state.patternRegexFocused} value={pattern.regex}
onChange={(v) => {
@@ -272,7 +273,7 @@ class RulePane extends Component {
</td>
</tr>
:
- <tr className="row-small">
+ <tr key={"new_pattern"} className="row-small">
<td>{p.regex}</td>
<td>{p.flags.caseless ? "yes": "no"}</td>
<td>{p.flags.dot_all ? "yes": "no"}</td>
@@ -377,7 +378,7 @@ class RulePane extends Component {
<thead>
<tr>
<th>regex</th>
- <th>Aa</th>
+ <th>!Aa</th>
<th>.*</th>
<th>\n+</th>
<th>UTF8</th>
diff --git a/frontend/src/components/panels/ServicePane.js b/frontend/src/components/panels/ServicePane.js
index b21ad6c..eaefa64 100644
--- a/frontend/src/components/panels/ServicePane.js
+++ b/frontend/src/components/panels/ServicePane.js
@@ -100,7 +100,7 @@ class ServicePane extends Component {
const service = this.state.currentService;
let services = this.state.services.map(s =>
- <tr onClick={() => {
+ <tr key={s.port} onClick={() => {
this.reset();
this.setState({isUpdate: true, currentService: _.cloneDeep(s)});
}} className={classNames("row-small", "row-clickable", {"row-selected": service.port === s.port })}>
diff --git a/frontend/src/index.scss b/frontend/src/index.scss
index 9dcc692..9ba23de 100644
--- a/frontend/src/index.scss
+++ b/frontend/src/index.scss
@@ -11,8 +11,7 @@ body {
-moz-osx-font-smoothing: grayscale;
background-color: $color-primary-2;
color: $color-primary-4;
- height: 100%;
- max-height: 100%;
+ height: 100vh;
font-size: 100%;
}
@@ -67,10 +66,6 @@ a {
}
}
-textarea.form-control {
- resize: none;
-}
-
.table {
color: $color-primary-4;
}
diff --git a/frontend/src/views/App.js b/frontend/src/views/App.js
index 8bd5e46..fb4454c 100644
--- a/frontend/src/views/App.js
+++ b/frontend/src/views/App.js
@@ -1,62 +1,46 @@
import React, {Component} from 'react';
+import './App.scss';
import Header from "./Header";
-import MainPane from "./MainPane";
+import MainPane from "../components/panels/MainPane";
import Footer from "./Footer";
import {BrowserRouter as Router} from "react-router-dom";
-import Services from "./Services";
import Filters from "./Filters";
-import Config from "./Config";
+import backend from "../backend";
+import ConfigurationPane from "../components/panels/ConfigurationPane";
class App extends Component {
constructor(props) {
super(props);
- this.state = {
- servicesWindowOpen: false,
- filterWindowOpen: false,
- configWindowOpen: false,
- configDone: false
- };
-
- fetch('/api/services')
- .then(response => {
- if( response.status === 503){
- this.setState({configWindowOpen: true});
- } else if (response.status === 200){
- this.setState({configDone: true});
- }
- });
+ this.state = {};
+ }
+ componentDidMount() {
+ backend.get("/api/services").then(_ => this.setState({configured: true}));
}
render() {
let modal;
- if (this.state.servicesWindowOpen) {
- modal = <Services onHide={() => this.setState({servicesWindowOpen: false})}/>;
- }
- if (this.state.filterWindowOpen) {
+ if (this.state.filterWindowOpen && this.state.configured) {
modal = <Filters onHide={() => this.setState({filterWindowOpen: false})}/>;
}
- if (this.state.configWindowOpen) {
- modal = <Config onHide={() => this.setState({configWindowOpen: false})}
- onDone={() => this.setState({configDone: true})}/>;
- }
return (
- <div className="app">
+ <div className="main">
<Router>
- <Header onOpenServices={() => this.setState({servicesWindowOpen: true})}
- onOpenFilters={() => this.setState({filterWindowOpen: true})}
- onOpenConfig={() => this.setState({configWindowOpen: true})}
- onOpenUpload={() => this.setState({uploadWindowOpen: true})}
- onConfigDone={this.state.configDone}
- />
- <MainPane />
- {modal}
- <Footer/>
+ <div className="main-header">
+ <Header onOpenFilters={() => this.setState({filterWindowOpen: true})} />
+ </div>
+ <div className="main-content">
+ {this.state.configured ? <MainPane /> :
+ <ConfigurationPane onConfigured={() => this.setState({configured: true})} />}
+ {modal}
+ </div>
+ <div className="main-footer">
+ {this.state.configured && <Footer/>}
+ </div>
</Router>
-
</div>
);
}
diff --git a/frontend/src/views/App.scss b/frontend/src/views/App.scss
new file mode 100644
index 0000000..b25d4c9
--- /dev/null
+++ b/frontend/src/views/App.scss
@@ -0,0 +1,15 @@
+
+.main {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+
+ .main-content {
+ flex: 1 1;
+ overflow: hidden;
+ }
+
+ .main-header, .main-footer {
+ flex: 0 0;
+ }
+}
diff --git a/frontend/src/views/Config.js b/frontend/src/views/Config.js
deleted file mode 100644
index b11f827..0000000
--- a/frontend/src/views/Config.js
+++ /dev/null
@@ -1,242 +0,0 @@
-import React, {Component} from 'react';
-import './Config.scss';
-import {Button, ButtonGroup, Col, Container, Form, Modal, Row, Table, ToggleButton} from "react-bootstrap";
-
-class Config extends Component {
-
- constructor(props) {
- super(props);
-
- this.state = {
- server_address: "",
- flag_regex: "",
- auth_required: false,
- accounts: {},
- showSignup: false,
- showConfig: true,
- tmpUser:"",
- tmpPass:"",
- tmpConf:"",
- errors:""
- };
-
- this.serverIpChanged = this.serverIpChanged.bind(this);
- this.flagRegexChanged = this.flagRegexChanged.bind(this);
- this.authRequiredChanged = this.authRequiredChanged.bind(this);
- this.userChanged = this.userChanged.bind(this);
- this.passwdChanged = this.passwdChanged.bind(this);
- this.confirmChanged = this.confirmChanged.bind(this);
- }
-
- serverIpChanged(event) {
- this.setState({server_address: event.target.value});
- }
-
- flagRegexChanged(event) {
- this.setState({flag_regex: event.target.value});
- }
-
- authRequiredChanged() {
- this.setState({auth_required: !this.value});
- this.checked = !this.checked;
- this.value = !this.value;
- }
-
- userChanged(event) {
- this.setState({tmpUser: event.target.value});
- }
-
- passwdChanged(event) {
- this.setState({tmpPass: event.target.value});
- }
-
- confirmChanged(event) {
- this.setState({tmpConf: event.target.value});
- }
-
- setup() {
- const requestOptions = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- config: {
- server_address: this.state.server_address,
- flag_regex: this.state.flag_regex,
- auth_required: this.state.auth_required,
- },
- accounts: this.state.accounts
- })
- };
-
- fetch('/setup', requestOptions)
- .then(response => {
- if (response.status === 202 ){
- //this.setState({showConfig:false});
- this.props.onHide();
- this.props.onDone();
- } else {
- response.json().then(data => {
- this.setState(
- {errors : data.error.toString()}
- );
- });
- }
- }
- );
-
- }
-
- signup(){
- if (this.state.tmpPass === this.state.tmpConf){
- const accounts = {...this.state.accounts};
- accounts[this.state.tmpUser] = this.state.tmpPass;
- this.setState({accounts});
- console.log(this.state);
- this.setState({showSignup:false,showConfig:true})
- }
- this.setState({tmpUser : ""});
- this.setState({tmpPass : ""});
- this.setState({tmpConf : ""});
- }
-
- render() {
- let rows = Object.keys(this.state.accounts).map(u =>
- <tr>
- <td>{u}</td>
- </tr>
- );
-
-
-
- return (
- <>
- <Modal show={this.state.showSignup} size="lg" aria-labelledby="services-dialog" centered >
- <Modal.Header>
- <Modal.Title id="services-dialog">
- # passwd
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <Container>
- <Row>
- <Form id="passwd-form">
- <Form.Group controlId="username">
- <Form.Label>username:</Form.Label>
- <Form.Control type="text" onChange={this.userChanged} value={this.state.tmpUser}/>
- </Form.Group>
-
- <Form.Group controlId="password">
- <Form.Label>password:</Form.Label>
- <Form.Control type="password" onChange={this.passwdChanged} value={this.state.tmpPass}/>
- </Form.Group>
-
- <Form.Group controlId="confirmPassword">
- <Form.Label>confirm password:</Form.Label>
- <Form.Control type="password" onChange={this.confirmChanged} value={this.state.tmpConf}/>
- </Form.Group>
-
-
- </Form>
-
- </Row>
-
- </Container>
- </Modal.Body>
- <Modal.Footer className="dialog-footer">
- <Button variant="green" onClick={() => this.signup()}>signup</Button>
- <Button variant="red" onClick={() => this.setState({showSignup:false,showConfig:true})}>close</Button>
- </Modal.Footer>
-
- </Modal>
- <Modal
- {...this.props}
- show="true"
- size="lg"
- aria-labelledby="services-dialog"
- centered
- >
- <Modal.Header>
- <Modal.Title id="services-dialog">
- ~/.config
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <div class="blink"><span><b>Warning:</b></span> once the configuration is completed, it cannot be changed unless you reset caronte :(</div>
- <hr/>
- <Container>
- <Row>
- <Col md={5}>
-
- <ButtonGroup toggle className="mb-2">
- <ToggleButton
- type="checkbox"
- variant="secondary"
- checked={this.state.auth_required}
- value={this.state.auth_required}
- onChange={() => this.authRequiredChanged()}
- >
- Authentication
- </ToggleButton>
- </ButtonGroup>
-
- <Table borderless size="sm" className="users-list">
-
- <thead>
- <tr>
- <th>users</th>
- </tr>
- </thead>
- <tbody>
- {rows}
- <tr> <td>
- <Button size="sm" onClick={() => this.setState({showSignup:true,showConfig:false})}>new</Button>
- </td> </tr>
- </tbody>
- </Table>
-
-
-
- </Col>
-
- <Col md={7}>
-
- <Form>
- <Form.Group controlId="server_address">
- <Form.Label>server_address:</Form.Label>
- <Form.Control type="text" onChange={this.serverIpChanged} value={this.state.server_address}/>
- </Form.Group>
-
- <Form.Group controlId="flag_regex">
- <Form.Label>flag_regex:</Form.Label>
- <Form.Control type="text" onChange={this.flagRegexChanged} value={this.state.flag_regex}/>
- </Form.Group>
-
- </Form>
-
- </Col>
-
- </Row>
- <Row>
- <div class="error">
- <b>
- {this.state.errors
- .split('\n').map((item, key) => {
- return <span key={key}>{item}<br/></span>})
- }
- </b>
- </div>
- </Row>
-
- </Container>
- </Modal.Body>
- <Modal.Footer className="dialog-footer">
- <Button variant="green" onClick={() => this.setup()}>set</Button>
- <Button variant="red" onClick={this.props.onHide}>close</Button>
- </Modal.Footer>
- </Modal>
- </>
- );
- }
-}
-
-export default Config;
diff --git a/frontend/src/views/Config.scss b/frontend/src/views/Config.scss
deleted file mode 100644
index 331d7a7..0000000
--- a/frontend/src/views/Config.scss
+++ /dev/null
@@ -1,55 +0,0 @@
-@import '../colors.scss';
-
-.curl-output {
- width: 100%;
- font-size: 13px;
-}
-
-#passwd-form {
- margin:auto;
-}
-
-.users-list {
- .btn {
- width: 70px;
- }
-
- td {
- background-color: $color-primary-2;
- border-top: 2px solid $color-primary-0;
- vertical-align: middle;
- text-align: center;
- }
-
- th {
- background-color: $color-primary-1;
- text-align: center;
- }
-}
-
-.btn-color {
- border: 3px solid #fff;
-}
-
-.dialog-footer {
- .btn {
- width: 80px;
- }
-}
-
-.blink{
-
- span{
- animation: blink 1s linear infinite;
- }
- @keyframes blink{
- 0%{opacity: 0;}
- 50%{opacity: .5;}
- 100%{opacity: 1;}
- }
-
-}
-
-.error{
- color: red;
-}
diff --git a/frontend/src/views/Connections.js b/frontend/src/views/Connections.js
index 9dca7e9..73979c4 100644
--- a/frontend/src/views/Connections.js
+++ b/frontend/src/views/Connections.js
@@ -5,6 +5,7 @@ import Table from 'react-bootstrap/Table';
import {Redirect} from 'react-router';
import {withRouter} from "react-router-dom";
import backend from "../backend";
+import ConnectionMatchedRules from "../components/ConnectionMatchedRules";
class Connections extends Component {
@@ -62,11 +63,21 @@ class Connections extends Component {
};
addServicePortFilter = (port) => {
- let urlParams = new URLSearchParams(this.props.location.search);
+ const urlParams = new URLSearchParams(this.props.location.search);
urlParams.set("service_port", port);
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.setState({queryString: "?" + urlParams});
+ }
+ };
+
async loadConnections(params) {
let url = "/api/connections";
const urlParams = new URLSearchParams(this.props.location.search);
@@ -112,20 +123,15 @@ class Connections extends Component {
}
}
- let flagRule = this.state.flagRule;
let rules = this.state.rules;
- if (flagRule === null) {
+ if (rules === null) {
rules = (await backend.get("/api/rules")).json;
- flagRule = rules.filter(rule => {
- return rule.name === "flag";
- })[0];
}
this.setState({
loading: false,
connections: connections,
- rules: res,
- flagRule: flagRule,
+ rules: rules,
firstConnection: firstConnection,
lastConnection: lastConnection
});
@@ -158,6 +164,7 @@ class Connections extends Component {
<th>srcport</th>
<th>dstip</th>
<th>dstport</th>
+ <th>started_at</th>
<th>duration</th>
<th>up</th>
<th>down</th>
@@ -166,13 +173,18 @@ class Connections extends Component {
</thead>
<tbody>
{
- this.state.connections.map(c =>
- <Connection key={c.id} data={c} onSelected={() => this.connectionSelected(c)}
- selected={this.state.selected === c.id} onMarked={marked => c.marked = marked}
- onEnabled={enabled => c.hidden = !enabled}
- containsFlag={this.state.flagRule && c.matched_rules.includes(this.state.flagRule.id)}
- addServicePortFilter={this.addServicePortFilter}/>
- )
+ this.state.connections.flatMap(c => {
+ return [<Connection key={c.id} data={c} onSelected={() => this.connectionSelected(c)}
+ selected={this.state.selected === c.id}
+ onMarked={marked => c.marked = marked}
+ onEnabled={enabled => c.hidden = !enabled}
+ addServicePortFilter={this.addServicePortFilter} />,
+ c.matched_rules.length > 0 &&
+ <ConnectionMatchedRules key={c.id + "_m"} matchedRules={c.matched_rules}
+ rules={this.state.rules}
+ addMatchedRulesFilter={this.addMatchedRulesFilter} />
+ ];
+ })
}
{loading}
</tbody>
diff --git a/frontend/src/views/Filters.js b/frontend/src/views/Filters.js
index b62e7eb..ba7d467 100644
--- a/frontend/src/views/Filters.js
+++ b/frontend/src/views/Filters.js
@@ -1,5 +1,4 @@
import React, {Component} from 'react';
-import './Services.scss';
import {Col, Container, Modal, Row, Table} from "react-bootstrap";
import {filtersDefinitions, filtersNames} from "../components/filters/FiltersDefinitions";
import ButtonField from "../components/fields/ButtonField";
diff --git a/frontend/src/views/Header.js b/frontend/src/views/Header.js
index c38c22d..944f1d5 100644
--- a/frontend/src/views/Header.js
+++ b/frontend/src/views/Header.js
@@ -46,7 +46,7 @@ class Header extends Component {
render() {
let quickFilters = filtersNames.filter(name => this.state[`${name}_active`])
- .map(name => filtersDefinitions[name])
+ .map(name => <React.Fragment key={name} >{filtersDefinitions[name]}</React.Fragment>)
.slice(0, 5);
return (
@@ -78,8 +78,9 @@ class Header extends Component {
<Link to="/services">
<ButtonField variant="indigo" name="services" bordered />
</Link>
- <ButtonField variant="blue" onClick={this.props.onOpenConfig}
- disabled={false} name="config" bordered />
+ <Link to="/config">
+ <ButtonField variant="blue" name="config" bordered />
+ </Link>
</div>
</div>
</div>
diff --git a/frontend/src/views/MainPane.js b/frontend/src/views/MainPane.js
deleted file mode 100644
index d2950ab..0000000
--- a/frontend/src/views/MainPane.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import React, {Component} from 'react';
-import './MainPane.scss';
-import Connections from "./Connections";
-import ConnectionContent from "../components/ConnectionContent";
-import {Route, Switch, withRouter} from "react-router-dom";
-import PcapPane from "../components/panels/PcapPane";
-import backend from "../backend";
-import RulePane from "../components/panels/RulePane";
-import ServicePane from "../components/panels/ServicePane";
-
-class MainPane extends Component {
-
- constructor(props) {
- super(props);
- this.state = {
- selectedConnection: null,
- loading: false
- };
- }
-
- componentDidMount() {
- const match = this.props.location.pathname.match(/^\/connections\/([a-f0-9]{24})$/);
- if (match != null) {
- this.setState({loading: true});
- backend.get(`/api/connections/${match[1]}`)
- .then(res => this.setState({selectedConnection: res.json, loading: false}))
- .catch(error => console.log(error));
- }
- }
-
- render() {
- return (
- <div className="main-pane">
- <div className="container-fluid">
- <div className="row">
- <div className="col-md-6 pane">
- {
- !this.state.loading &&
- <Connections onSelected={(c) => this.setState({selectedConnection: c})}
- initialConnection={this.state.selectedConnection} />
- }
- </div>
- <div className="col-md-6 pl-0 pane">
- <Switch>
- <Route path="/pcaps" children={<PcapPane />} />
- <Route path="/rules" children={<RulePane />} />
- <Route path="/services" children={<ServicePane />} />
- <Route exact path="/connections/:id" children={<ConnectionContent connection={this.state.selectedConnection} />} />
- <Route children={<ConnectionContent />} />
- </Switch>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-export default withRouter(MainPane);
diff --git a/frontend/src/views/MainPane.scss b/frontend/src/views/MainPane.scss
deleted file mode 100644
index 20720ba..0000000
--- a/frontend/src/views/MainPane.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.main-pane {
- .pane {
- height: calc(100vh - 210px);
- position: relative;
- }
-}
diff --git a/frontend/src/views/Services.js b/frontend/src/views/Services.js
deleted file mode 100644
index 97368dc..0000000
--- a/frontend/src/views/Services.js
+++ /dev/null
@@ -1,210 +0,0 @@
-import React, {Component} from 'react';
-import './Services.scss';
-import {Button, ButtonGroup, Col, Container, Form, FormControl, InputGroup, Modal, Row, Table} from "react-bootstrap";
-import {createCurlCommand} from '../utils';
-import backend from "../backend";
-
-class Services extends Component {
-
- constructor(props) {
- super(props);
- this.alphabet = 'abcdefghijklmnopqrstuvwxyz';
- this.colors = ["#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB", "#1E88E5", "#039BE5", "#00ACC1",
- "#00897B", "#43A047", "#7CB342", "#9E9D24", "#F9A825", "#FB8C00", "#F4511E", "#6D4C41"];
-
- this.state = {
- services: {},
- port: 0,
- portValid: false,
- name: "",
- nameValid: false,
- color: this.colors[0],
- colorValid: false,
- notes: ""
- };
-
- this.portChanged = this.portChanged.bind(this);
- this.nameChanged = this.nameChanged.bind(this);
- this.notesChanged = this.notesChanged.bind(this);
- this.newService = this.newService.bind(this);
- this.editService = this.editService.bind(this);
- this.saveService = this.saveService.bind(this);
- this.loadServices = this.loadServices.bind(this);
- }
-
- componentDidMount() {
- this.loadServices();
- }
-
- portChanged(event) {
- let value = event.target.value.replace(/[^\d]/gi, '');
- let port = 0;
- if (value !== "") {
- port = parseInt(value);
- }
- this.setState({port: port});
- }
-
- nameChanged(event) {
- let value = event.target.value.replace(/[\s]/gi, '_').replace(/[^\w]/gi, '').toLowerCase();
- this.setState({name: value});
- }
-
- notesChanged(event) {
- this.setState({notes: event.target.value});
- }
-
- newService() {
- this.setState({name: "", port: 0, notes: ""});
- }
-
- editService(service) {
- this.setState({name: service.name, port: service.port, color: service.color, notes: service.notes});
- }
-
- saveService() {
- if (this.state.portValid && this.state.nameValid) {
- backend.put("/api/services", {
- color: this.state.color,
- name: this.state.name,
- notes: this.state.notes,
- port: this.state.port,
- }).then(_ => {
- this.newService();
- this.loadServices();
- });
- }
- }
-
- loadServices() {
- backend.get("/api/services").then(res => this.setState({services: res}));
- }
-
- componentDidUpdate(prevProps, prevState, snapshot) {
- if (this.state.name != null && prevState.name !== this.state.name) {
- this.setState({
- nameValid: this.state.name.length >= 3
- });
- }
- if (prevState.port !== this.state.port) {
- this.setState({
- portValid: this.state.port > 0 && this.state.port <= 65565
- });
- }
- }
-
- render() {
- let output = "";
- if (!this.state.portValid) {
- output += "assert(1 <= port <= 65565)\n";
- }
- if (!this.state.nameValid) {
- output += "assert(len(name) >= 3)\n";
- }
- if (output === "") {
- output = createCurlCommand("/services", "PUT", {
- "port": this.state.port,
- "name": this.state.name,
- "color": this.state.color,
- "notes": this.state.notes
- });
- }
- let rows = Object.values(this.state.services).map(s =>
- <tr>
- <td><Button variant="btn-edit" size="sm"
- onClick={() => this.editService(s)} style={{"backgroundColor": s.color}}>edit</Button></td>
- <td>{s.port}</td>
- <td>{s.name}</td>
- </tr>
- );
-
- let colorButtons = this.colors.map((color, i) =>
- <Button size="sm" className="btn-color" key={"button" + this.alphabet[i]}
- style={{"backgroundColor": color, "borderColor": this.state.color === color ? "#fff" : color}}
- onClick={() => this.setState({color: color})}>{this.alphabet[i]}</Button>);
-
- return (
- <Modal
- {...this.props}
- show="true"
- size="lg"
- aria-labelledby="services-dialog"
- centered
- >
- <Modal.Header>
- <Modal.Title id="services-dialog">
- ~/services
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <Container>
- <Row>
- <Col md={7}>
- <Table borderless size="sm" className="services-list">
- <thead>
- <tr>
- <th><Button size="sm" onClick={this.newService}>new</Button></th>
- <th>port</th>
- <th>name</th>
- </tr>
- </thead>
- <tbody>
- {rows}
- </tbody>
- </Table>
- </Col>
- <Col md={5}>
- <Form>
- <Form.Group controlId="servicePort">
- <Form.Label>port:</Form.Label>
- <Form.Control type="text" onChange={this.portChanged} value={this.state.port}/>
- </Form.Group>
-
- <Form.Group controlId="serviceName">
- <Form.Label>name:</Form.Label>
- <Form.Control type="text" onChange={this.nameChanged} value={this.state.name}/>
- </Form.Group>
-
- <Form.Group controlId="serviceColor">
- <Form.Label>color:</Form.Label>
- <ButtonGroup aria-label="Basic example">
- {colorButtons.slice(0, 8)}
- </ButtonGroup>
- <ButtonGroup aria-label="Basic example">
- {colorButtons.slice(8, 18)}
- </ButtonGroup>
- </Form.Group>
-
- <Form.Group controlId="serviceNotes">
- <Form.Label>notes:</Form.Label>
- <Form.Control as="textarea" rows={3} onChange={this.notesChanged}
- value={this.state.notes}/>
- </Form.Group>
- </Form>
-
-
- </Col>
-
- </Row>
-
- <Row>
- <Col md={12}>
- <InputGroup>
- <FormControl as="textarea" rows={4} className="curl-output" readOnly={true}
- value={output}/>
- </InputGroup>
-
- </Col>
- </Row>
- </Container>
- </Modal.Body>
- <Modal.Footer className="dialog-footer">
- <Button variant="green" onClick={this.saveService}>save</Button>
- <Button variant="red" onClick={this.props.onHide}>close</Button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-export default Services;
diff --git a/frontend/src/views/Services.scss b/frontend/src/views/Services.scss
deleted file mode 100644
index 2abb55e..0000000
--- a/frontend/src/views/Services.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-@import '../colors.scss';
-
-.curl-output {
- width: 100%;
- font-size: 13px;
-}
-
-.services-list {
- .btn {
- width: 70px;
- }
-
- td {
- background-color: $color-primary-2;
- border-top: 2px solid $color-primary-0;
- vertical-align: middle;
- }
-
- th {
- background-color: $color-primary-1;
- }
-}
-
-.btn-color {
- border: 3px solid #fff;
-}
-
-.dialog-footer {
- .btn {
- width: 80px;
- }
-}
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index c5f59cb..6d111a3 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1166,24 +1166,24 @@
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
-"@fortawesome/fontawesome-common-types@^0.2.30":
- version "0.2.30"
- resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.30.tgz#2f1cc5b46bd76723be41d0013a8450c9ba92b777"
- integrity sha512-TsRwpTuKwFNiPhk1UfKgw7zNPeV5RhNp2Uw3pws+9gDAkPGKrtjR1y2lI3SYn7+YzyfuNknflpBA1LRKjt7hMg==
+"@fortawesome/fontawesome-common-types@^0.2.31":
+ version "0.2.31"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.31.tgz#f15a39e5ab4e5dfda0717733bcacb9580e666ad9"
+ integrity sha512-xfnPyH6NN5r/h1/qDYoGB0BlHSID902UkQzxR8QsoKDh55KAPr8ruAoie12WQEEQT8lRE2wtV7LoUllJ1HqCag==
"@fortawesome/fontawesome-svg-core@^1.2.28":
- version "1.2.30"
- resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.30.tgz#f56dc6791861fe5d1af04fb8abddb94658c576db"
- integrity sha512-E3sAXATKCSVnT17HYmZjjbcmwihrNOCkoU7dVMlasrcwiJAHxSKeZ+4WN5O+ElgO/FaYgJmASl8p9N7/B/RttA==
+ version "1.2.31"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.31.tgz#23fff9c521f1b57e79e4c2fd6cce8aa794e9d99e"
+ integrity sha512-lqUWRK+ylHQJG5Kiez4XrAZAfc7snxCc+X59quL3xPfMnxzfyf1lt+/hD7X1ZL4KlzAH2KFzMuEVrolo/rAkog==
dependencies:
- "@fortawesome/fontawesome-common-types" "^0.2.30"
+ "@fortawesome/fontawesome-common-types" "^0.2.31"
"@fortawesome/free-solid-svg-icons@^5.13.0":
- version "5.14.0"
- resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.14.0.tgz#970453f5e8c4915ad57856c3a0252ac63f6fec18"
- integrity sha512-M933RDM8cecaKMWDSk3FRYdnzWGW7kBBlGNGfvqLVwcwhUPNj9gcw+xZMrqBdRqxnSXdl3zWzTCNNGEtFUq67Q==
+ version "5.15.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.0.tgz#6f553f25cdebd7d5ae778598c2ddd3321dd63450"
+ integrity sha512-4dGRsOnGBPM7c0fd3LuiU6LgRSLn01rw1LJ370yC2iFMLUcLCLLynZhQbMhsiJmMwQM/YmPQblAdyHKVCgsIAA==
dependencies:
- "@fortawesome/fontawesome-common-types" "^0.2.30"
+ "@fortawesome/fontawesome-common-types" "^0.2.31"
"@fortawesome/react-fontawesome@^0.1.9":
version "0.1.11"
@@ -1407,9 +1407,9 @@
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
"@popperjs/core@^2.0.0":
- version "2.4.4"
- resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.4.tgz#11d5db19bd178936ec89cd84519c4de439574398"
- integrity sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg==
+ version "2.5.3"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.5.3.tgz#4982b0b66b7a4cf949b86f5d25a8cf757d3cfd9d"
+ integrity sha512-RFwCobxsvZ6j7twS7dHIZQZituMIDJJNHS/qY6iuthVebxS3zhRY+jaC2roEKiAYaVuTcGmX6Luc6YBcf6zJVg==
"@restart/context@^2.1.4":
version "2.1.4"
@@ -1533,9 +1533,9 @@
loader-utils "^1.2.3"
"@testing-library/dom@*":
- version "7.24.2"
- resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.24.2.tgz#6d2b7dd21efbd5358b98c2777fc47c252f3ae55e"
- integrity sha512-ERxcZSoHx0EcN4HfshySEWmEf5Kkmgi+J7O79yCJ3xggzVlBJ2w/QjJUC+EBkJJ2OeSw48i3IoePN4w8JlVUIA==
+ version "7.24.3"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.24.3.tgz#dae3071463cf28dc7755b43d9cf2202e34cbb85d"
+ integrity sha512-6eW9fUhEbR423FZvoHRwbWm9RUUByLWGayYFNVvqTnQLYvsNpBS4uEuKH9aqr3trhxFwGVneJUonehL3B1sHJw==
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.10.3"
@@ -1593,9 +1593,9 @@
integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==
"@types/babel__core@^7.1.0":
- version "7.1.9"
- resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d"
- integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==
+ version "7.1.10"
+ resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40"
+ integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw==
dependencies:
"@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0"
@@ -1604,24 +1604,24 @@
"@types/babel__traverse" "*"
"@types/babel__generator@*":
- version "7.6.1"
- resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.1.tgz#4901767b397e8711aeb99df8d396d7ba7b7f0e04"
- integrity sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==
+ version "7.6.2"
+ resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8"
+ integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==
dependencies:
"@babel/types" "^7.0.0"
"@types/babel__template@*":
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307"
- integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.3.tgz#b8aaeba0a45caca7b56a5de9459872dde3727214"
+ integrity sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==
dependencies:
"@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0"
"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
- version "7.0.14"
- resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.14.tgz#e99da8c075d4fb098c774ba65dabf7dc9954bd13"
- integrity sha512-8w9szzKs14ZtBVuP6Wn7nMLRJ0D6dfB0VEBEyRgxrZ/Ln49aNMykrghM2FaNn4FJRzNppCSa0Rv9pBRM5Xc3wg==
+ version "7.0.15"
+ resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03"
+ integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==
dependencies:
"@babel/types" "^7.3.0"
@@ -1691,9 +1691,9 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*":
- version "14.11.1"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.1.tgz#56af902ad157e763f9ba63d671c39cda3193c835"
- integrity sha512-oTQgnd0hblfLsJ6BvJzzSL+Inogp3lq9fGgqRkMB/ziKMgEUaFl801OncOzUmalfzt14N0oPHMK47ipl+wbTIw==
+ version "14.11.2"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256"
+ integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==
"@types/parse-json@^4.0.0":
version "4.0.0"
@@ -1771,16 +1771,16 @@
integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
"@types/yargs@^13.0.0":
- version "13.0.10"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.10.tgz#e77bf3fc73c781d48c2eb541f87c453e321e5f4b"
- integrity sha512-MU10TSgzNABgdzKvQVW1nuuT+sgBMWeXNc3XOs5YXV5SDAK+PPja2eUuBNB9iqElu03xyEDqlnGw0jgl4nbqGQ==
+ version "13.0.11"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.11.tgz#def2f0c93e4bdf2c61d7e34899b17e34be28d3b1"
+ integrity sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==
dependencies:
"@types/yargs-parser" "*"
"@types/yargs@^15.0.0":
- version "15.0.5"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.5.tgz#947e9a6561483bdee9adffc983e91a6902af8b79"
- integrity sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==
+ version "15.0.7"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.7.tgz#dad50a7a234a35ef9460737a56024287a3de1d2b"
+ integrity sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA==
dependencies:
"@types/yargs-parser" "*"
@@ -2393,13 +2393,6 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
-axios@^0.19.2:
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
- integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
- dependencies:
- follow-redirects "1.5.10"
-
axobject-query@^2.0.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
@@ -2792,12 +2785,12 @@ browserslist@4.10.0:
pkg-up "^3.1.0"
browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.6.2, browserslist@^4.6.4, browserslist@^4.8.5, browserslist@^4.9.1:
- version "4.14.3"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.3.tgz#381f9e7f13794b2eb17e1761b4f118e8ae665a53"
- integrity sha512-GcZPC5+YqyPO4SFnz48/B0YaCwS47Q9iPChRGi6t7HhflKBcINzFrJvRfC+jp30sRMKxF+d4EHGs27Z0XP1NaQ==
+ version "4.14.5"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.5.tgz#1c751461a102ddc60e40993639b709be7f2c4015"
+ integrity sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==
dependencies:
- caniuse-lite "^1.0.30001131"
- electron-to-chromium "^1.3.570"
+ caniuse-lite "^1.0.30001135"
+ electron-to-chromium "^1.3.571"
escalade "^3.1.0"
node-releases "^1.1.61"
@@ -2982,10 +2975,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001131:
- version "1.0.30001133"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001133.tgz#ec564c5495311299eb05245e252d589a84acd95e"
- integrity sha512-s3XAUFaC/ntDb1O3lcw9K8MPeOW7KO3z9+GzAoBxfz1B0VdacXPMKgFUtG4KIsgmnbexmi013s9miVu4h+qMHw==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135:
+ version "1.0.30001140"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001140.tgz#30dae27599f6ede2603a0962c82e468bca894232"
+ integrity sha512-xFtvBtfGrpjTOxTpjP5F2LmN04/ZGfYV8EQzUIC/RmKpdrmzJrjqlJ4ho7sGuAMPko2/Jl08h7x9uObCfBFaAA==
capture-exit@^2.0.0:
version "2.0.0"
@@ -3812,13 +3805,6 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
dependencies:
ms "2.0.0"
-debug@=3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
- integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
- dependencies:
- ms "2.0.0"
-
debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -4026,9 +4012,9 @@ dom-accessibility-api@^0.3.0:
integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==
dom-accessibility-api@^0.5.1:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.2.tgz#ef3cdb5d3f0d599d8f9c8b18df2fb63c9793739d"
- integrity sha512-k7hRNKAiPJXD2aBqfahSo4/01cTsKWXf+LqJgglnkN2Nz8TsxXKQBXHhKe0Ye9fEfHEZY49uSA5Sr3AqP/sWKA==
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.3.tgz#0ea493c924d4070dfbf531c4aaca3d7a2c601aab"
+ integrity sha512-yfqzAi1GFxK6EoJIZKgxqJyK6j/OjEFEUi2qkNThD/kUhoCFSG1izq31B5xuxzbJBGw9/67uPtkPMYAzWL7L7Q==
dom-converter@^0.2:
version "0.2.0"
@@ -4151,10 +4137,10 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
-electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.570:
- version "1.3.570"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.570.tgz#3f5141cc39b4e3892a276b4889980dabf1d29c7f"
- integrity sha512-Y6OCoVQgFQBP5py6A/06+yWxUZHDlNr/gNDGatjH8AZqXl8X0tE4LfjLJsXGz/JmWJz8a6K7bR1k+QzZ+k//fg==
+electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.571:
+ version "1.3.576"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.576.tgz#2e70234484e03d7c7e90310d7d79fd3775379c34"
+ integrity sha512-uSEI0XZ//5ic+0NdOqlxp0liCD44ck20OAGyLMSymIWTEAtHKVJi6JM18acOnRgUgX7Q65QqnI+sNncNvIy8ew==
elliptic@^6.5.3:
version "6.5.3"
@@ -4252,20 +4238,20 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstrac
string.prototype.trimstart "^1.0.1"
es-abstract@^1.18.0-next.0:
- version "1.18.0-next.0"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc"
- integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==
+ version "1.18.0-next.1"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
+ integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
- is-callable "^1.2.0"
+ is-callable "^1.2.2"
is-negative-zero "^2.0.0"
is-regex "^1.1.1"
object-inspect "^1.8.0"
object-keys "^1.1.1"
- object.assign "^4.1.0"
+ object.assign "^4.1.1"
string.prototype.trimend "^1.0.1"
string.prototype.trimstart "^1.0.1"
@@ -4927,13 +4913,6 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
-follow-redirects@1.5.10:
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
- integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
- dependencies:
- debug "=3.1.0"
-
follow-redirects@^1.0.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
@@ -5858,10 +5837,10 @@ is-buffer@^1.0.2, is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
-is-callable@^1.1.4, is-callable@^1.2.0:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.1.tgz#4d1e21a4f437509d25ce55f8184350771421c96d"
- integrity sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==
+is-callable@^1.1.4, is-callable@^1.2.0, is-callable@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
+ integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==
is-ci@^2.0.0:
version "2.0.0"
@@ -6991,7 +6970,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.5, lodash@~4.17.10:
+"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@~4.17.10:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@@ -7215,11 +7194,16 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
-mime-db@1.44.0, "mime-db@>= 1.43.0 < 2":
+mime-db@1.44.0:
version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
+"mime-db@>= 1.43.0 < 2":
+ version "1.45.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea"
+ integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==
+
mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
@@ -7697,7 +7681,7 @@ object-visit@^1.0.0:
dependencies:
isobject "^3.0.0"
-object.assign@^4.1.0:
+object.assign@^4.1.0, object.assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd"
integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==
@@ -7783,9 +7767,9 @@ onetime@^5.1.0:
mimic-fn "^2.1.0"
open@^7.0.2:
- version "7.2.1"
- resolved "https://registry.yarnpkg.com/open/-/open-7.2.1.tgz#07b0ade11a43f2a8ce718480bdf3d7563a095195"
- integrity sha512-xbYCJib4spUdmcs0g/2mK1nKo/jO2T7INClWd/beL7PFkXRWgr8B23ssDHX/USPn2M2IjDR5UdpYs6I67SnTSA==
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69"
+ integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==
dependencies:
is-docker "^2.0.0"
is-wsl "^2.1.1"
@@ -8469,9 +8453,9 @@ postcss-lab-function@^2.0.1:
postcss-values-parser "^2.0.0"
postcss-load-config@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.1.tgz#0a684bb8beb05e55baf922f7ab44c3edb17cf78e"
- integrity sha512-D2ENobdoZsW0+BHy4x1CAkXtbXtYWYRIxL/JbtRBqrRGOPtJ2zoga/bEZWhV/ShWB5saVxJMzbMdSyA/vv4tXw==
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a"
+ integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==
dependencies:
cosmiconfig "^5.0.0"
import-cwd "^2.0.0"
@@ -8845,9 +8829,9 @@ postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4:
uniq "^1.0.1"
postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.3.tgz#766d77728728817cc140fa1ac6da5e77f9fada98"
- integrity sha512-0ClFaY4X1ra21LRqbW6y3rUbWcxnSVkDFG57R7Nxus9J9myPFlv+jYDMohzpkBx0RrjjiqjtycpchQ+PLGmZ9w==
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3"
+ integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==
dependencies:
cssesc "^3.0.0"
indexes-of "^1.0.1"
@@ -8902,9 +8886,9 @@ postcss@7.0.21:
supports-color "^6.1.0"
postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.23, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
- version "7.0.34"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.34.tgz#f2baf57c36010df7de4009940f21532c16d65c20"
- integrity sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw==
+ version "7.0.35"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24"
+ integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"