SigV4: Add support EC2 IAM role auth and possibility to toggle auth providers (#32444)

* wip: consume aws-sdk config in auth http settings

* fix typings

* use sdk regions

* use latest version of grafana/aws-sdk

* use newest sdk

* add comment

* bump sdk

* fix yarn lock file

* fix storybook issue

* upgrade aws-sdk. also use it in grafana ui

* remove not used config

* add aws-sdk as an external dep for grafana in the grafana ui rollup

Co-authored-by: Will Browne <will.browne@grafana.com>
This commit is contained in:
Erik Sundell
2021-03-30 12:30:34 +02:00
committed by GitHub
parent cfc7537632
commit f0d6f132ae
7 changed files with 77 additions and 364 deletions

2
go.mod
View File

@@ -41,7 +41,7 @@ require (
github.com/google/uuid v1.2.0
github.com/gosimple/slug v1.9.0
github.com/grafana/alerting-api v0.0.0-20210323194814-03a29a4c4c27
github.com/grafana/grafana-aws-sdk v0.2.0
github.com/grafana/grafana-aws-sdk v0.3.0
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
github.com/grafana/grafana-plugin-sdk-go v0.89.0
github.com/grafana/loki v1.6.2-0.20201026154740-6978ee5d7387

2
go.sum
View File

@@ -803,6 +803,8 @@ github.com/grafana/grafana v1.9.2-0.20210308201921-4ce0a49eac03/go.mod h1:AHRRvd
github.com/grafana/grafana-aws-sdk v0.1.0/go.mod h1:+pPo5U+pX0zWimR7YBc7ASeSQfbRkcTyQYqMiAj7G5U=
github.com/grafana/grafana-aws-sdk v0.2.0 h1:UTBBYwye+ad5YUIlwN7TGxLdz1wXN3Ezhl0pseDGRVA=
github.com/grafana/grafana-aws-sdk v0.2.0/go.mod h1:+pPo5U+pX0zWimR7YBc7ASeSQfbRkcTyQYqMiAj7G5U=
github.com/grafana/grafana-aws-sdk v0.3.0 h1:UT3rIXQFeAh0OaRJT7dUQojYaSjbI9RwOtMacaerv8I=
github.com/grafana/grafana-aws-sdk v0.3.0/go.mod h1:+pPo5U+pX0zWimR7YBc7ASeSQfbRkcTyQYqMiAj7G5U=
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 h1:SPdxCL9BChFTlyi0Khv64vdCW4TMna8+sxL7+Chx+Ag=
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4/go.mod h1:nc0XxBzjeGcrMltCDw269LoWF9S8ibhgxolCdA1R8To=
github.com/grafana/grafana-plugin-sdk-go v0.79.0/go.mod h1:NvxLzGkVhnoBKwzkst6CFfpMFKwAdIUZ1q8ssuLeF60=

View File

@@ -201,7 +201,7 @@
},
"dependencies": {
"@emotion/core": "10.0.27",
"@grafana/aws-sdk": "0.0.24",
"@grafana/aws-sdk": "0.0.3",
"@grafana/slate-react": "0.22.9-grafana",
"@popperjs/core": "2.5.4",
"@reduxjs/toolkit": "1.5.0",

View File

@@ -31,6 +31,7 @@
"@grafana/e2e-selectors": "7.5.0-pre.0",
"@grafana/slate-react": "0.22.9-grafana",
"@grafana/tsconfig": "^1.0.0-rc1",
"@grafana/aws-sdk": "0.0.3",
"@iconscout/react-unicons": "1.1.4",
"@popperjs/core": "2.5.4",
"@sentry/browser": "5.25.0",

View File

@@ -28,6 +28,7 @@ const buildCjsPackage = ({ env }) => {
external: [
'react',
'react-dom',
'@grafana/aws-sdk',
'@grafana/data',
'@grafana/e2e-selectors',
'moment',

View File

@@ -1,254 +1,70 @@
import React, { useEffect } from 'react';
import { HttpSettingsProps } from './types';
import { SelectableValue } from '@grafana/data';
import { Button, InlineFormLabel, Input } from '..';
import Select from '../Forms/Legacy/Select/Select';
import React from 'react';
import { HttpSettingsBaseProps } from './types';
import { DataSourceSettings } from '@grafana/data';
import {
AwsAuthDataSourceSecureJsonData,
AwsAuthDataSourceJsonData,
ConnectionConfig,
ConnectionConfigProps,
} from '@grafana/aws-sdk';
export const SigV4AuthSettings: React.FC<HttpSettingsProps> = (props) => {
export const SigV4AuthSettings: React.FC<HttpSettingsBaseProps> = (props) => {
const { dataSourceConfig, onChange } = props;
const authProviderOptions = [
{ label: 'AWS SDK Default', value: 'default' },
{ label: 'Access & secret key', value: 'keys' },
{ label: 'Credentials file', value: 'credentials' },
] as SelectableValue[];
const regions = [
{ value: 'af-south-1', label: 'af-south-1' },
{ value: 'ap-east-1', label: 'ap-east-1' },
{ value: 'ap-northeast-1', label: 'ap-northeast-1' },
{ value: 'ap-northeast-2', label: 'ap-northeast-2' },
{ value: 'ap-northeast-3', label: 'ap-northeast-3' },
{ value: 'ap-south-1', label: 'ap-south-1' },
{ value: 'ap-southeast-1', label: 'ap-southeast-1' },
{ value: 'ap-southeast-2', label: 'ap-southeast-2' },
{ value: 'ca-central-1', label: 'ca-central-1' },
{ value: 'cn-north-1', label: 'cn-north-1' },
{ value: 'cn-northwest-1', label: 'cn-northwest-1' },
{ value: 'eu-central-1', label: 'eu-central-1' },
{ value: 'eu-north-1', label: 'eu-north-1' },
{ value: 'eu-west-1', label: 'eu-west-1' },
{ value: 'eu-west-2', label: 'eu-west-2' },
{ value: 'eu-west-3', label: 'eu-west-3' },
{ value: 'me-south-1', label: 'me-south-1' },
{ value: 'sa-east-1', label: 'sa-east-1' },
{ value: 'us-east-1', label: 'us-east-1' },
{ value: 'us-east-2', label: 'us-east-2' },
{ value: 'us-gov-east-1', label: 'us-gov-east-1' },
{ value: 'us-gov-west-1', label: 'us-gov-west-1' },
{ value: 'us-iso-east-1', label: 'us-iso-east-1' },
{ value: 'us-isob-east-1', label: 'us-isob-east-1' },
{ value: 'us-west-1', label: 'us-west-1' },
{ value: 'us-west-2', label: 'us-west-2' },
] as SelectableValue[];
// Apply some defaults on initial render
useEffect(() => {
const sigV4AuthType = dataSourceConfig.jsonData.sigV4AuthType || 'default';
onJsonDataChange('sigV4AuthType', sigV4AuthType);
// We can't enforce the eslint rule here because we only want to run this once.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const onSecureJsonDataReset = (fieldName: string) => {
const state = {
...dataSourceConfig,
secureJsonData: {
...dataSourceConfig.secureJsonData,
[fieldName]: '',
},
secureJsonFields: {
...dataSourceConfig.secureJsonFields,
[fieldName]: false,
},
};
onChange(state);
};
const onSecureJsonDataChange = (fieldName: string, fieldValue: string) => {
const state = {
...dataSourceConfig,
secureJsonData: {
...dataSourceConfig.secureJsonData,
[fieldName]: fieldValue,
},
};
onChange(state);
};
const onJsonDataChange = (fieldName: string, fieldValue: string) => {
const state = {
// The @grafana/aws-sdk ConnectionConfig is designed to be rendered in a ConfigEditor,
// taking DataSourcePluginOptionsEditorProps as props. We therefore need to map the props accordingly.
const connectionConfigProps: ConnectionConfigProps<AwsAuthDataSourceJsonData, AwsAuthDataSourceSecureJsonData> = {
onOptionsChange: (awsDataSourceSettings) => {
const dataSourceSettings: DataSourceSettings<any, any> = {
...dataSourceConfig,
jsonData: {
...dataSourceConfig.jsonData,
sigV4AuthType: awsDataSourceSettings.jsonData.authType,
sigV4Profile: awsDataSourceSettings.jsonData.profile,
sigV4AssumeRoleArn: awsDataSourceSettings.jsonData.assumeRoleArn,
sigV4ExternalId: awsDataSourceSettings.jsonData.externalId,
sigV4Region: awsDataSourceSettings.jsonData.defaultRegion,
sigV4Endpoint: awsDataSourceSettings.jsonData.endpoint,
},
secureJsonFields: {
sigV4AccessKey: awsDataSourceSettings.secureJsonFields?.accessKey,
sigV4SecretKey: awsDataSourceSettings.secureJsonFields?.secretKey,
},
secureJsonData: {
sigV4AccessKey: awsDataSourceSettings.secureJsonData?.accessKey,
sigV4SecretKey: awsDataSourceSettings.secureJsonData?.secretKey,
},
};
onChange(dataSourceSettings);
},
options: {
...dataSourceConfig,
jsonData: {
...dataSourceConfig.jsonData,
[fieldName]: fieldValue,
authType: dataSourceConfig.jsonData.sigV4AuthType,
profile: dataSourceConfig.jsonData.sigV4Profile,
assumeRoleArn: dataSourceConfig.jsonData.sigV4AssumeRoleArn,
externalId: dataSourceConfig.jsonData.sigV4ExternalId,
defaultRegion: dataSourceConfig.jsonData.sigV4Region,
endpoint: dataSourceConfig.jsonData.sigV4Endpoint,
},
};
onChange(state);
secureJsonFields: {
accessKey: dataSourceConfig.secureJsonFields?.sigV4AccessKey,
secretKey: dataSourceConfig.secureJsonFields?.sigV4SecretKey,
},
secureJsonData: {
accessKey: dataSourceConfig.secureJsonData?.sigV4AccessKey,
secretKey: dataSourceConfig.secureJsonData?.sigV4SecretKey,
},
},
};
return (
<>
<h6>SigV4 Auth Details</h6>
<div className="gf-form-group">
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
className="width-14"
tooltip="Which AWS credentials chain to use. AWS SDK Default is the recommended option for EKS, ECS, or if you've attached an IAM role to your EC2 instance."
>
Authentication Provider
</InlineFormLabel>
<Select
className="width-30"
value={authProviderOptions.find(
(authProvider) => authProvider.value === dataSourceConfig.jsonData.sigV4AuthType
)}
options={authProviderOptions}
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || ''}
onChange={(option) => {
onJsonDataChange('sigV4AuthType', option.value);
}}
/>
</div>
</div>
{dataSourceConfig.jsonData.sigV4AuthType === 'credentials' && (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
className="width-14"
tooltip="Credentials profile name, as specified in ~/.aws/credentials, leave blank for default."
>
Credentials Profile Name
</InlineFormLabel>
<div className="width-30">
<Input
className="width-30"
placeholder="default"
value={dataSourceConfig.jsonData.sigV4Profile || ''}
onChange={(e) => onJsonDataChange('sigV4Profile', e.currentTarget.value)}
/>
</div>
</div>
</div>
)}
{dataSourceConfig.jsonData.sigV4AuthType === 'keys' && (
<div>
{dataSourceConfig.secureJsonFields?.sigV4AccessKey ? (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-14">Access Key ID</InlineFormLabel>
<Input className="width-25" placeholder="Configured" disabled={true} />
</div>
<div className="gf-form">
<div className="max-width-30 gf-form-inline">
<Button variant="secondary" type="button" onClick={(e) => onSecureJsonDataReset('sigV4AccessKey')}>
Reset
</Button>
</div>
</div>
</div>
) : (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-14">Access Key ID</InlineFormLabel>
<div className="width-30">
<Input
className="width-30"
value={dataSourceConfig.secureJsonData?.sigV4AccessKey || ''}
onChange={(e) => onSecureJsonDataChange('sigV4AccessKey', e.currentTarget.value)}
/>
</div>
</div>
</div>
)}
{dataSourceConfig.secureJsonFields?.sigV4SecretKey ? (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-14">Secret Access Key</InlineFormLabel>
<Input className="width-25" placeholder="Configured" disabled={true} />
</div>
<div className="gf-form">
<div className="max-width-30 gf-form-inline">
<Button variant="secondary" type="button" onClick={(e) => onSecureJsonDataReset('sigV4SecretKey')}>
Reset
</Button>
</div>
</div>
</div>
) : (
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel className="width-14">Secret Access Key</InlineFormLabel>
<div className="width-30">
<Input
className="width-30"
value={dataSourceConfig.secureJsonData?.sigV4SecretKey || ''}
onChange={(e) => onSecureJsonDataChange('sigV4SecretKey', e.currentTarget.value)}
/>
</div>
</div>
</div>
)}
</div>
)}
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
className="width-14"
tooltip="ARN of the role to assume. Specifying a role here ensures that the selected authentication provider is used to assume the role rather than using the credentials directly. Leave blank if you don't need to assume a role."
>
Assume Role ARN
</InlineFormLabel>
<div className="width-30">
<Input
className="width-30"
placeholder="arn:aws:iam:*"
value={dataSourceConfig.jsonData.sigV4AssumeRoleArn || ''}
onChange={(e) => onJsonDataChange('sigV4AssumeRoleArn', e.currentTarget.value)}
/>
</div>
</div>
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
className="width-14"
tooltip="If you are assuming a role in another account, that was created with an external ID, specify the external ID here."
>
External ID
</InlineFormLabel>
<div className="width-30">
<Input
className="width-30"
placeholder="External ID"
value={dataSourceConfig.jsonData.sigV4ExternalId || ''}
onChange={(e) => onJsonDataChange('sigV4ExternalId', e.currentTarget.value)}
/>
</div>
</div>
</div>
</div>
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
className="width-14"
tooltip="Specify the region, for example, use ` us-west-2 ` for US West (Oregon)."
>
Default Region
</InlineFormLabel>
<Select
className="width-30"
value={regions.find((region) => region.value === dataSourceConfig.jsonData.sigV4Region)}
options={regions}
defaultValue={dataSourceConfig.jsonData.sigV4Region || ''}
onChange={(option) => onJsonDataChange('sigV4Region', option.value)}
/>
</div>
</div>
<div className="gf-form">
<h6>SigV4 Auth Details</h6>
</div>
<ConnectionConfig {...connectionConfigProps} skipHeader skipEndpoint></ConnectionConfig>
</>
);
};

139
yarn.lock
View File

@@ -3136,18 +3136,6 @@
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
"@emotion/core@^10.0.27", "@emotion/core@^10.1.1":
version "10.1.1"
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3"
integrity sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==
dependencies:
"@babel/runtime" "^7.5.5"
"@emotion/cache" "^10.0.27"
"@emotion/css" "^10.0.27"
"@emotion/serialize" "^0.11.15"
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
"@emotion/core@^10.0.9":
version "10.0.21"
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.21.tgz#2e8398d2b92fd90d4ed6ac4d0b66214971de3458"
@@ -3160,6 +3148,18 @@
"@emotion/sheet" "0.9.3"
"@emotion/utils" "0.11.2"
"@emotion/core@^10.1.1":
version "10.1.1"
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3"
integrity sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==
dependencies:
"@babel/runtime" "^7.5.5"
"@emotion/cache" "^10.0.27"
"@emotion/css" "^10.0.27"
"@emotion/serialize" "^0.11.15"
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
"@emotion/css@^10.0.14", "@emotion/css@^10.0.9":
version "10.0.14"
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.14.tgz#95dacabdd0e22845d1a1b0b5968d9afa34011139"
@@ -3430,39 +3430,10 @@
source-map "~0.6.1"
typescript "~3.9.7"
"@grafana/aws-sdk@0.0.24":
version "0.0.24"
resolved "https://registry.yarnpkg.com/@grafana/aws-sdk/-/aws-sdk-0.0.24.tgz#30511706d5efaf52e81cadd8f933d1c21e8d1c6b"
integrity sha512-tKYZ5KgJn0/S+/z7KFUtcgihYC1vCpUPvDMONp8j1j02JWwUE3ivu9LDiPeWBLcicy0psUypC0mL3QG/e8prIw==
dependencies:
"@grafana/data" "7.4.0"
"@grafana/runtime" "7.4.0"
"@grafana/ui" "7.4.0"
"@grafana/data@7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@grafana/data/-/data-7.4.0.tgz#7e7ae998cc4d916ba7d004e27b38eab4b1199ee8"
integrity sha512-YlsA9uu3dO7hnbIUagjhAMXUU7puSSXxc42aOyuj/K9daPVMhOunI7lSM8PSm1uhKVifvd5DJOwlA0UQQRJqSw==
dependencies:
"@braintree/sanitize-url" "4.0.0"
"@types/d3-interpolate" "^1.3.1"
apache-arrow "0.16.0"
eventemitter3 "4.0.7"
lodash "4.17.20"
marked "1.2.2"
rxjs "6.6.3"
xss "1.0.6"
"@grafana/e2e-selectors@7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@grafana/e2e-selectors/-/e2e-selectors-7.4.0.tgz#f286dab307b26e6b0628a1d5e9907e3943c38ed3"
integrity sha512-RCAXLOTUlO8nWALo1Wnl0pTkZYzUGnBV6RuetwEDaRkfZEEKXn3F1OerzF5iJWFsdFJvVehCZ7sFYOPmnEl/gg==
dependencies:
"@grafana/tsconfig" "^1.0.0-rc1"
commander "5.0.0"
execa "4.0.0"
typescript "4.1.2"
yaml "^1.8.3"
"@grafana/aws-sdk@0.0.3":
version "0.0.3"
resolved "https://registry.yarnpkg.com/@grafana/aws-sdk/-/aws-sdk-0.0.3.tgz#bc632c6c77971a5474acbe45847420503679fc75"
integrity sha512-V0PJLk+oU1C33knurXp8BkT5N2ctDu9b21zpK16vkJAs5aOiH/OaOhQ/IP9ipxYhB+b9PFjr8RXagV/8XJ8xwg==
"@grafana/eslint-config@2.3.0":
version "2.3.0"
@@ -3480,16 +3451,6 @@
prettier "2.2.1"
typescript "4.1.3"
"@grafana/runtime@7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@grafana/runtime/-/runtime-7.4.0.tgz#c097adcdb151efd4211a4fc2bd91598c9cd7e627"
integrity sha512-ug2FPtzlEL4CykmrVp1Cnp6066ndzlMP8x/+rHNK7RR3HbS0ju2c+t6iq0vwU6605dwL71uOC+NOUuNIyZtYyg==
dependencies:
"@grafana/data" "7.4.0"
"@grafana/ui" "7.4.0"
systemjs "0.20.19"
systemjs-plugin-css "0.1.37"
"@grafana/slate-react@0.22.9-grafana":
version "0.22.9-grafana"
resolved "https://registry.yarnpkg.com/@grafana/slate-react/-/slate-react-0.22.9-grafana.tgz#07f35f0ffc018f616b9f82fa6e5ba65fae75c6a0"
@@ -3517,59 +3478,6 @@
resolved "https://registry.yarnpkg.com/@grafana/tsconfig/-/tsconfig-1.0.0-rc1.tgz#d07ea16755a50cae21000113f30546b61647a200"
integrity sha512-nucKPGyzlSKYSiJk5RA8GzMdVWhdYNdF+Hh65AXxjD9PlY69JKr5wANj8bVdQboag6dgg0BFKqgKPyY+YtV4Iw==
"@grafana/ui@7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@grafana/ui/-/ui-7.4.0.tgz#4c5eea4c5a00a342fb87bd9a462398e32a72bb84"
integrity sha512-jbpqWGqzdDmU5+lCOHY/DLTlpLD0rNgXJfQLWwPq2ilOdCq0ItIaehxM+e5XqzTy3J+q5nyNQZFH2wM+wqzZRw==
dependencies:
"@emotion/core" "^10.0.27"
"@grafana/data" "7.4.0"
"@grafana/e2e-selectors" "7.4.0"
"@grafana/slate-react" "0.22.9-grafana"
"@grafana/tsconfig" "^1.0.0-rc1"
"@iconscout/react-unicons" "1.1.4"
"@popperjs/core" "2.5.4"
"@sentry/browser" "5.25.0"
"@testing-library/jest-dom" "5.11.9"
"@torkelo/react-select" "3.0.8"
"@types/hoist-non-react-statics" "3.3.1"
"@types/react-beautiful-dnd" "12.1.2"
"@types/react-color" "3.0.1"
"@types/react-select" "3.0.8"
"@types/react-table" "7.0.12"
"@types/slate" "0.47.1"
"@types/slate-react" "0.22.5"
classnames "2.2.6"
d3 "5.15.0"
emotion "10.0.27"
hoist-non-react-statics "3.3.2"
immutable "3.8.2"
jquery "3.5.1"
lodash "4.17.20"
moment "2.24.0"
monaco-editor "0.20.0"
papaparse "5.3.0"
rc-cascader "1.0.1"
rc-drawer "3.1.3"
rc-slider "9.6.4"
rc-time-picker "^3.7.3"
react "17.0.1"
react-beautiful-dnd "13.0.0"
react-calendar "2.19.2"
react-color "2.18.0"
react-custom-scrollbars "4.2.1"
react-dom "17.0.1"
react-highlight-words "0.16.0"
react-hook-form "5.1.3"
react-monaco-editor "0.36.0"
react-popper "2.2.4"
react-storybook-addon-props-combinations "1.1.0"
react-table "7.0.0"
react-transition-group "4.4.1"
slate "0.47.8"
tinycolor2 "1.4.1"
uplot "1.6.4"
"@icons/material@^0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8"
@@ -17716,11 +17624,6 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@4.17.20:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
lodash@4.17.21, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.1.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@@ -17996,11 +17899,6 @@ markdown-to-jsx@^6.11.4:
prop-types "^15.6.2"
unquote "^1.1.0"
marked@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.2.tgz#5d77ffb789c4cb0ae828bfe76250f7140b123f70"
integrity sha512-5jjKHVl/FPo0Z6ocP3zYhKiJLzkwJAw4CZoLjv57FkvbUuwOX4LIBBGGcXjAY6ATcd1q9B8UTj5T9Umauj0QYQ==
marked@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.1.tgz#5e7ed7009bfa5c95182e4eb696f85e948cefcee3"
@@ -25878,11 +25776,6 @@ update-notifier@^2.5.0:
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"
uplot@1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.4.tgz#016e9f66796d78c187957e710743f7ca405dfb4d"
integrity sha512-4d6JixG54HQKFDLAegzwgwf87GtEbp6pt3YlHygyLt+mJ9RHneCXYlZxr1QOhLetoSSHeeDuWP5RFMv8mdltpg==
uplot@1.6.7:
version "1.6.7"
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.7.tgz#d3faaec899791ee3fdf5d2835b19a33d9117566a"