mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Custom fleet policy UX for new integration (cloud defend v1) (#147300)
## Summary New Kibana plugin created for an integration called "Cloud defend for containers" which will have a corresponding agent service which can proactively block and alert on executable creation or modification in a running container. This plugin is purely in place to configure the fleet policy UX around this new integration. For now we have added a yaml editor as a custom input to our integration. The monaco-yaml libary was added to allow support for JSON schema validation support for yaml. Integration PR is up, and a work in progress: (waiting on some content for the doc page) https://github.com/elastic/integrations/pull/4680 ### Screenshot  ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) Co-authored-by: Karl Godard <karlgodard@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
7a6eac8d1b
commit
0b19cfafa3
34 changed files with 918 additions and 6 deletions
|
@ -449,6 +449,10 @@ The plugin exposes the static DefaultEditorController class to consume.
|
|||
|Static migration page where self-managed users can see text/copy about migrating to Elastic Cloud
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/cloud_defend/README.md[cloudDefend]
|
||||
|This plugin currently only exists to provide custom fleet policy UX for a set of new BPF LSM features. The first feature being container "drift prevention".
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/cloud_integrations/cloud_experiments/README.mdx[cloudExperiments]
|
||||
|The Cloud Experiments Service provides the necessary APIs to implement A/B testing scenarios, fetching the variations in configuration and reporting back metrics to track conversion rates of the experiments.
|
||||
|
||||
|
|
|
@ -578,6 +578,7 @@
|
|||
"moment-duration-format": "^2.3.2",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"monaco-editor": "^0.24.0",
|
||||
"monaco-yaml": "3.2.1",
|
||||
"mustache": "^2.3.2",
|
||||
"node-fetch": "^2.6.7",
|
||||
"node-forge": "^1.3.1",
|
||||
|
|
|
@ -47,6 +47,7 @@ RUNTIME_DEPS = [
|
|||
"@npm//antlr4ts",
|
||||
"@npm//babel-loader",
|
||||
"@npm//monaco-editor",
|
||||
"@npm//monaco-yaml",
|
||||
"@npm//raw-loader",
|
||||
"@npm//rxjs",
|
||||
]
|
||||
|
|
|
@ -26,5 +26,6 @@ import 'monaco-editor/esm/vs/editor/contrib/bracketMatching/bracketMatching.js';
|
|||
import 'monaco-editor/esm/vs/language/json/monaco.contribution.js';
|
||||
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js'; // Needed for basic javascript support
|
||||
import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution.js'; // Needed for basic xml support
|
||||
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'; // Needed for yaml support
|
||||
|
||||
export { monaco };
|
||||
|
|
|
@ -11,12 +11,13 @@ import { PainlessLang } from './painless';
|
|||
import { SQLLang } from './sql';
|
||||
import { monaco } from './monaco_imports';
|
||||
import { ESQL_THEME_ID, ESQLLang, buildESQlTheme } from './esql';
|
||||
|
||||
import { registerLanguage, registerTheme } from './helpers';
|
||||
import { createWorkersRegistry } from './workers_registry';
|
||||
|
||||
export const DEFAULT_WORKER_ID = 'default';
|
||||
|
||||
const Yaml = 'yaml';
|
||||
|
||||
const workerRegistry = createWorkersRegistry(DEFAULT_WORKER_ID);
|
||||
|
||||
workerRegistry.register(
|
||||
|
@ -44,6 +45,11 @@ workerRegistry.register(
|
|||
async () => await import('!!raw-loader!../../target_workers/json.editor.worker.js')
|
||||
);
|
||||
|
||||
workerRegistry.register(
|
||||
Yaml,
|
||||
async () => await import('!!raw-loader!../../target_workers/yaml.editor.worker.js')
|
||||
);
|
||||
|
||||
/**
|
||||
* Register languages and lexer rules
|
||||
*/
|
||||
|
|
|
@ -14,6 +14,8 @@ const getWorkerEntry = (language) => {
|
|||
return 'monaco-editor/esm/vs/editor/editor.worker.js';
|
||||
case 'json':
|
||||
return 'monaco-editor/esm/vs/language/json/json.worker.js';
|
||||
case 'yaml':
|
||||
return 'monaco-yaml/lib/esm/yaml.worker.js';
|
||||
default:
|
||||
return path.resolve(__dirname, 'src', language, 'worker', `${language}.worker.ts`);
|
||||
}
|
||||
|
@ -47,4 +49,4 @@ const getWorkerConfig = (language) => ({
|
|||
},
|
||||
});
|
||||
|
||||
module.exports = ['default', 'json', 'painless', 'xjson', 'esql'].map(getWorkerConfig);
|
||||
module.exports = ['default', 'json', 'painless', 'xjson', 'esql', 'yaml'].map(getWorkerConfig);
|
||||
|
|
|
@ -12,6 +12,7 @@ pageLoadAssetSize:
|
|||
cloud: 21076
|
||||
cloudChat: 19894
|
||||
cloudDataMigration: 19170
|
||||
cloudDefend: 18697
|
||||
cloudExperiments: 59358
|
||||
cloudFullStory: 18493
|
||||
cloudGainsight: 18710
|
||||
|
|
|
@ -132,7 +132,7 @@ module.exports = {
|
|||
transformIgnorePatterns: [
|
||||
// ignore all node_modules except monaco-editor and react-monaco-editor which requires babel transforms to handle dynamic import()
|
||||
// since ESM modules are not natively supported in Jest yet (https://github.com/facebook/jest/issues/4842)
|
||||
'[/\\\\]node_modules(?)[/\\\\].+\\.js$',
|
||||
'[/\\\\]node_modules(?)[/\\\\].+\\.js$',
|
||||
'packages/kbn-pm/dist/index.js',
|
||||
],
|
||||
|
||||
|
|
|
@ -259,6 +259,15 @@
|
|||
"enabled": true,
|
||||
"prCreation": "immediate"
|
||||
},
|
||||
{
|
||||
"groupName": "Cloud Defend",
|
||||
"matchPackageNames": ["monaco-yaml"],
|
||||
"reviewers": ["team:sec-cloudnative-integrations"],
|
||||
"matchBaseBranches": ["main"],
|
||||
"labels": ["Team: Cloud Native Integrations", "release_note:skip", "backport:skip"],
|
||||
"enabled": true,
|
||||
"prCreation": "immediate"
|
||||
},
|
||||
{
|
||||
"groupName": "XState",
|
||||
"matchPackageNames": ["xstate"],
|
||||
|
|
|
@ -1072,6 +1072,8 @@
|
|||
"@kbn/canvas-plugin/*": ["x-pack/plugins/canvas/*"],
|
||||
"@kbn/cases-plugin": ["x-pack/plugins/cases"],
|
||||
"@kbn/cases-plugin/*": ["x-pack/plugins/cases/*"],
|
||||
"@kbn/cloud-defend-plugin": ["x-pack/plugins/cloud_defend"],
|
||||
"@kbn/cloud-defend-plugin/*": ["x-pack/plugins/cloud_defend/*"],
|
||||
"@kbn/cloud-chat-plugin": ["x-pack/plugins/cloud_integrations/cloud_chat"],
|
||||
"@kbn/cloud-chat-plugin/*": ["x-pack/plugins/cloud_integrations/cloud_chat/*"],
|
||||
"@kbn/cloud-data-migration-plugin": ["x-pack/plugins/cloud_integrations/cloud_data_migration"],
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"xpack.cases": "plugins/cases",
|
||||
"xpack.cloud": "plugins/cloud",
|
||||
"xpack.cloudChat": "plugins/cloud_integrations/cloud_chat",
|
||||
"xpack.cloudDefend": "plugins/cloud_defend",
|
||||
"xpack.cloudLinks": "plugins/cloud_integrations/cloud_links",
|
||||
"xpack.cloudDataMigration": "plugins/cloud_integrations/cloud_data_migration",
|
||||
"xpack.csp": "plugins/cloud_security_posture",
|
||||
|
|
7
x-pack/plugins/cloud_defend/.i18nrc.json
Executable file
7
x-pack/plugins/cloud_defend/.i18nrc.json
Executable file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"prefix": "cloudDefend",
|
||||
"paths": {
|
||||
"cloudDefend": "."
|
||||
},
|
||||
"translations": ["translations/ja-JP.json"]
|
||||
}
|
48
x-pack/plugins/cloud_defend/README.md
Executable file
48
x-pack/plugins/cloud_defend/README.md
Executable file
|
@ -0,0 +1,48 @@
|
|||
# Cloud Defend (for containers)
|
||||
|
||||
This plugin currently only exists to provide custom fleet policy UX for a set of new BPF LSM features. The first feature being container "drift prevention".
|
||||
|
||||
Drift prevention is a way to block when executables are created or modified. Our agent service detects these events, and applies a set of selectors and responses configured to either block, alert or both.
|
||||
|
||||
## Example configuration
|
||||
```
|
||||
selectors:
|
||||
# default selector (user can modify or remove if they want)
|
||||
- name: default
|
||||
operation: [createExecutable, modifyExecutable, execMemFd]
|
||||
|
||||
# example custom selector
|
||||
- name: nginxOnly
|
||||
containerImageName:
|
||||
- nginx
|
||||
|
||||
# example selector used for exclude
|
||||
- name: excludeCustomNginxBuild
|
||||
containerImageTag:
|
||||
- staging
|
||||
|
||||
# responses are evaluated from top to bottom
|
||||
# only the first response with a match will run its actions
|
||||
responses:
|
||||
- match: [nginxOnly]
|
||||
exclude: [excludeCustomNginxBuild]
|
||||
actions: [alert, block]
|
||||
|
||||
# default response
|
||||
# delete this if no default response needed
|
||||
- match: [default]
|
||||
actions: [alert]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
## pre commit checks
|
||||
|
||||
```
|
||||
node scripts/type_check.js --project x-pack/plugins/cloud_defend/tsconfig.json
|
||||
yarn test:jest x-pack/plugins/cloud_defend
|
||||
```
|
||||
|
||||
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment.
|
12
x-pack/plugins/cloud_defend/common/constants.ts
Executable file
12
x-pack/plugins/cloud_defend/common/constants.ts
Executable file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const PLUGIN_ID = 'cloudDefend';
|
||||
export const PLUGIN_NAME = 'cloudDefend';
|
||||
export const INTEGRATION_PACKAGE_NAME = 'cloud_defend';
|
||||
export const INPUT_CONTROL = 'cloud_defend/control';
|
||||
export const ALERTS_DATASET = 'cloud_defend.alerts';
|
18
x-pack/plugins/cloud_defend/jest.config.js
Normal file
18
x-pack/plugins/cloud_defend/jest.config.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/** @type {import('@jest/types').Config.InitialOptions} */
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/x-pack/plugins/cloud_defend'],
|
||||
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/cloud_defend',
|
||||
coverageReporters: ['text', 'html'],
|
||||
collectCoverageFrom: [
|
||||
'<rootDir>/x-pack/plugins/cloud_defend/{common,public,server}/**/*.{ts,tsx}',
|
||||
],
|
||||
};
|
14
x-pack/plugins/cloud_defend/kibana.json
Executable file
14
x-pack/plugins/cloud_defend/kibana.json
Executable file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"id": "cloudDefend",
|
||||
"version": "1.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"owner": {
|
||||
"name": "Cloud Native Integrations",
|
||||
"githubTeam": "sec-cloudnative-integrations"
|
||||
},
|
||||
"description": "Defend for Containers",
|
||||
"server": false,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["fleet", "kibanaReact"],
|
||||
"optionalPlugins": []
|
||||
}
|
12
x-pack/plugins/cloud_defend/public/common/utils.ts
Normal file
12
x-pack/plugins/cloud_defend/public/common/utils.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { NewPackagePolicy } from '@kbn/fleet-plugin/public';
|
||||
|
||||
export function getInputFromPolicy(policy: NewPackagePolicy, inputId: string) {
|
||||
return policy.inputs.find((input) => input.type === inputId);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
class ResizeObserver {
|
||||
observe() {
|
||||
// do nothing
|
||||
}
|
||||
unobserve() {
|
||||
// do nothing
|
||||
}
|
||||
disconnect() {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
window.ResizeObserver = ResizeObserver;
|
||||
export default ResizeObserver;
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
class Worker {
|
||||
constructor(stringUrl) {
|
||||
this.url = stringUrl;
|
||||
this.onmessage = () => {};
|
||||
}
|
||||
|
||||
postMessage(msg) {
|
||||
this.onmessage(msg);
|
||||
}
|
||||
|
||||
terminate() {}
|
||||
}
|
||||
|
||||
window.Worker = Worker;
|
||||
export default Worker;
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { useMemo } from 'react';
|
||||
import yaml from 'js-yaml';
|
||||
import { setDiagnosticsOptions } from 'monaco-yaml';
|
||||
import { monaco } from '@kbn/monaco';
|
||||
|
||||
const { Uri, editor } = monaco;
|
||||
|
||||
const SCHEMA_URI = 'http://elastic.co/cloud_defend.yaml';
|
||||
const modelUri = Uri.parse(SCHEMA_URI);
|
||||
|
||||
export const useConfigModel = (configuration: string) => {
|
||||
const json = useMemo(() => {
|
||||
try {
|
||||
return yaml.load(configuration);
|
||||
} catch {
|
||||
return { selectors: [] };
|
||||
}
|
||||
}, [configuration]);
|
||||
|
||||
return useMemo(() => {
|
||||
const selectorNames = json?.selectors?.map((selector: any) => selector.name) || [];
|
||||
|
||||
setDiagnosticsOptions({
|
||||
validate: true,
|
||||
completion: true,
|
||||
hover: true,
|
||||
schemas: [
|
||||
{
|
||||
uri: SCHEMA_URI,
|
||||
fileMatch: [String(modelUri)],
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['selectors', 'responses'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
selectors: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { $ref: '#/$defs/selector' },
|
||||
},
|
||||
responses: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { $ref: '#/$defs/response' },
|
||||
},
|
||||
},
|
||||
$defs: {
|
||||
selector: {
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
additionalProperties: false,
|
||||
anyOf: [
|
||||
{ required: ['operation'] },
|
||||
{ required: ['containerImageName'] },
|
||||
{ required: ['containerImageTag'] },
|
||||
{ required: ['targetFilePath'] },
|
||||
{ required: ['orchestratorClusterId'] },
|
||||
{ required: ['orchestratorClusterName'] },
|
||||
{ required: ['orchestratorNamespace'] },
|
||||
{ required: ['orchestratorResourceLabel'] },
|
||||
{ required: ['orchestratorResourceName'] },
|
||||
{ required: ['orchestratorType'] },
|
||||
],
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
operation: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { enum: ['createExecutable', 'modifyExecutable', 'execMemFd'] },
|
||||
},
|
||||
containerImageName: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
containerImageTag: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
targetFilePath: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
orchestratorClusterId: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
orchestratorClusterName: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
orchestratorNamespace: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
orchestratorResourceLabel: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
orchestratorResourceName: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string' },
|
||||
},
|
||||
orchestratorType: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { enum: ['kubernetes'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
response: {
|
||||
type: 'object',
|
||||
required: ['match', 'actions'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
match: { type: 'array', minItems: 1, items: { enum: selectorNames } },
|
||||
exclude: { type: 'array', items: { enum: selectorNames } },
|
||||
actions: { type: 'array', minItems: 1, items: { enum: ['alert', 'block'] } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let model = editor.getModel(modelUri);
|
||||
|
||||
if (model === null) {
|
||||
model = editor.createModel(configuration, 'yaml', modelUri);
|
||||
}
|
||||
|
||||
return model;
|
||||
}, [configuration, json.selectors]);
|
||||
};
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import '@kbn/kibana-react-plugin/public/code_editor/code_editor.test.helpers';
|
||||
import { TestProvider } from '../../test/test_provider';
|
||||
import { getCloudDefendNewPolicyMock } from './mocks';
|
||||
import { ConfigYamlView } from '.';
|
||||
import './__mocks__/worker';
|
||||
import './__mocks__/resizeobserver';
|
||||
|
||||
// @ts-ignore-next
|
||||
window.Worker = Worker;
|
||||
|
||||
describe('<CloudDefendCreatePolicyExtension />', () => {
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // Deprecated
|
||||
removeListener: jest.fn(), // Deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
const onChange = jest.fn();
|
||||
|
||||
const WrappedComponent = ({ policy = getCloudDefendNewPolicyMock() }) => {
|
||||
return (
|
||||
<TestProvider>
|
||||
<ConfigYamlView policy={policy} onChange={onChange} />;
|
||||
</TestProvider>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
onChange.mockClear();
|
||||
});
|
||||
|
||||
it('renders a checkbox to toggle BPF/LSM control mechanism', () => {
|
||||
const { getByTestId } = render(<WrappedComponent />);
|
||||
const input = getByTestId('cloud-defend-control-toggle') as HTMLInputElement;
|
||||
expect(input).toBeInTheDocument();
|
||||
expect(input).toBeEnabled();
|
||||
});
|
||||
|
||||
it('renders a yaml editor', () => {
|
||||
const { getByTestId } = render(<WrappedComponent />);
|
||||
const el = getByTestId('monacoEditorTextarea') as HTMLTextAreaElement;
|
||||
expect(el).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { EuiSwitch, EuiSpacer, EuiText, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||
import { CodeEditor, YamlLang } from '@kbn/kibana-react-plugin/public';
|
||||
import { NewPackagePolicy } from '@kbn/fleet-plugin/public';
|
||||
import { monaco } from '@kbn/monaco';
|
||||
import { INPUT_CONTROL } from '../../../common/constants';
|
||||
import { useStyles } from './styles';
|
||||
import { useConfigModel } from './hooks/use_config_model';
|
||||
import { getInputFromPolicy } from '../../common/utils';
|
||||
import * as i18n from './translations';
|
||||
|
||||
const { editor } = monaco;
|
||||
|
||||
interface OnChangeDeps {
|
||||
isValid: boolean;
|
||||
updatedPolicy: NewPackagePolicy;
|
||||
}
|
||||
|
||||
interface ConfigYamlViewDeps {
|
||||
policy: NewPackagePolicy;
|
||||
onChange(opts: OnChangeDeps): void;
|
||||
}
|
||||
|
||||
interface ConfigError {
|
||||
line: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export const ConfigYamlView = ({ policy, onChange }: ConfigYamlViewDeps) => {
|
||||
const styles = useStyles();
|
||||
const [errors, setErrors] = useState<ConfigError[]>([]);
|
||||
const input = getInputFromPolicy(policy, INPUT_CONTROL);
|
||||
const configuration = input?.vars?.configuration?.value || '';
|
||||
const currentModel = useConfigModel(configuration);
|
||||
const controlEnabled = !!input?.enabled;
|
||||
|
||||
useEffect(() => {
|
||||
const listener = editor.onDidChangeMarkers(([resource]) => {
|
||||
const markers = editor.getModelMarkers({ resource });
|
||||
const errs = markers.map((marker) => {
|
||||
const error: ConfigError = {
|
||||
line: marker.startLineNumber,
|
||||
message: marker.message,
|
||||
};
|
||||
|
||||
return error;
|
||||
});
|
||||
|
||||
onChange({ isValid: errs.length === 0, updatedPolicy: policy });
|
||||
setErrors(errs);
|
||||
});
|
||||
|
||||
return () => {
|
||||
listener.dispose();
|
||||
};
|
||||
}, [onChange, policy]);
|
||||
|
||||
const onYamlChange = useCallback(
|
||||
(value) => {
|
||||
if (input?.vars) {
|
||||
input.vars.configuration.value = value;
|
||||
onChange({ isValid: errors.length === 0, updatedPolicy: policy });
|
||||
}
|
||||
},
|
||||
[errors.length, input, onChange, policy]
|
||||
);
|
||||
|
||||
const onToggleEnabled = useCallback(
|
||||
(e) => {
|
||||
if (input) {
|
||||
input.enabled = e.target.checked;
|
||||
onChange({ isValid: errors.length === 0, updatedPolicy: policy });
|
||||
}
|
||||
},
|
||||
[errors.length, input, onChange, policy]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<EuiSwitch
|
||||
data-test-subj="cloud-defend-control-toggle"
|
||||
label={i18n.enableControl}
|
||||
checked={controlEnabled}
|
||||
onChange={onToggleEnabled}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.enableControlHelp}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
{controlEnabled && (
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
<h4>{i18n.controlYaml}</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText color="subdued" size="s">
|
||||
{i18n.controlYamlHelp}
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
<div css={styles.yamlEditor}>
|
||||
<CodeEditor
|
||||
languageId={YamlLang}
|
||||
options={{
|
||||
wordWrap: 'off',
|
||||
model: currentModel,
|
||||
}}
|
||||
onChange={onYamlChange}
|
||||
value={configuration}
|
||||
/>
|
||||
</div>
|
||||
<EuiSpacer size="s" />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { NewPackagePolicy } from '@kbn/fleet-plugin/public';
|
||||
import type { PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { INTEGRATION_PACKAGE_NAME, INPUT_CONTROL, ALERTS_DATASET } from '../../../common/constants';
|
||||
|
||||
const MOCK_YAML_CONFIGURATION = `
|
||||
selectors:
|
||||
# default selector (user can modify or remove if they want)
|
||||
- name: default
|
||||
operation: [createExecutable, modifyExecutable, execMemFd]
|
||||
|
||||
# example custom selector
|
||||
- name: nginxOnly
|
||||
containerImageName:
|
||||
- nginx
|
||||
|
||||
# example selector used for exclude
|
||||
- name: excludeCustomNginxBuild
|
||||
containerImageTag:
|
||||
- staging
|
||||
|
||||
# responses are evaluated from top to bottom
|
||||
# only the first response with a match will run its actions
|
||||
responses:
|
||||
- match: [nginxOnly]
|
||||
exclude: [excludeCustomNginxBuild]
|
||||
actions: [alert, block]
|
||||
|
||||
# default response
|
||||
# delete this if no default response needed
|
||||
- match: [default]
|
||||
actions: [alert]
|
||||
`;
|
||||
|
||||
export const getCloudDefendNewPolicyMock = (): NewPackagePolicy => ({
|
||||
name: 'some-cloud_defend-policy',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_id: '',
|
||||
enabled: true,
|
||||
inputs: [
|
||||
{
|
||||
type: INPUT_CONTROL,
|
||||
policy_template: INTEGRATION_PACKAGE_NAME,
|
||||
enabled: true,
|
||||
vars: {
|
||||
configuration: {
|
||||
type: 'yaml',
|
||||
value: MOCK_YAML_CONFIGURATION,
|
||||
},
|
||||
},
|
||||
streams: [
|
||||
{
|
||||
enabled: true,
|
||||
data_stream: {
|
||||
type: 'logs',
|
||||
dataset: ALERTS_DATASET,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
package: {
|
||||
name: 'cloud_defend',
|
||||
title: 'Kubernetes Security Posture Management',
|
||||
version: '0.0.21',
|
||||
},
|
||||
});
|
||||
|
||||
export const getCloudDefendPolicyMock = (): PackagePolicy => ({
|
||||
id: 'c6d16e42-c32d-4dce-8a88-113cfe276ad1',
|
||||
version: 'abcd',
|
||||
revision: 1,
|
||||
updated_at: '2020-06-25T16:03:38.159292',
|
||||
updated_by: 'kibana',
|
||||
created_at: '2020-06-25T16:03:38.159292',
|
||||
created_by: 'kibana',
|
||||
name: 'some-cloud_defend-policy',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_id: '',
|
||||
enabled: true,
|
||||
inputs: [
|
||||
{
|
||||
type: INPUT_CONTROL,
|
||||
policy_template: INTEGRATION_PACKAGE_NAME,
|
||||
enabled: true,
|
||||
vars: {
|
||||
configuration: {
|
||||
type: 'yaml',
|
||||
value: MOCK_YAML_CONFIGURATION,
|
||||
},
|
||||
},
|
||||
streams: [
|
||||
{
|
||||
id: '1234',
|
||||
enabled: true,
|
||||
data_stream: {
|
||||
type: 'logs',
|
||||
dataset: ALERTS_DATASET,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
package: {
|
||||
name: 'cloud_defend',
|
||||
title: 'Kubernetes Security Posture Management',
|
||||
version: '0.0.21',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { CSSObject } from '@emotion/react';
|
||||
|
||||
export const useStyles = () => {
|
||||
return useMemo(() => {
|
||||
const yamlEditor: CSSObject = {
|
||||
height: '400px',
|
||||
};
|
||||
|
||||
return { yamlEditor };
|
||||
}, []);
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const enableControl = i18n.translate('xpack.cloudDefend.enableControl', {
|
||||
defaultMessage: 'Enable BPF/LSM controls',
|
||||
});
|
||||
|
||||
export const enableControlHelp = i18n.translate('xpack.cloudDefend.enableControlHelp', {
|
||||
defaultMessage:
|
||||
'Enables BPF/LSM control mechanism, for use with FIM and container drift prevention.',
|
||||
});
|
||||
|
||||
export const controlYaml = i18n.translate('xpack.cloudDefend.controlYaml', {
|
||||
defaultMessage: 'Configuration yaml',
|
||||
});
|
||||
|
||||
export const controlYamlHelp = i18n.translate('xpack.cloudDefend.controlYamlHelp', {
|
||||
defaultMessage:
|
||||
'Configure BPF/LSM controls by creating selectors, and responses below. To learn more click <here>',
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { memo } from 'react';
|
||||
import type { PackagePolicyCreateExtensionComponentProps } from '@kbn/fleet-plugin/public';
|
||||
import { ConfigYamlView } from '../config_yaml_view';
|
||||
|
||||
export const CloudDefendCreatePolicyExtension = memo<PackagePolicyCreateExtensionComponentProps>(
|
||||
({ newPolicy, onChange }) => {
|
||||
return <ConfigYamlView policy={newPolicy} onChange={onChange} />;
|
||||
}
|
||||
);
|
||||
|
||||
CloudDefendCreatePolicyExtension.displayName = 'CloudDefendCreatePolicyExtension';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { CloudDefendCreatePolicyExtension as default };
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { memo } from 'react';
|
||||
import type { PackagePolicyEditExtensionComponentProps } from '@kbn/fleet-plugin/public';
|
||||
import { ConfigYamlView } from '../config_yaml_view';
|
||||
|
||||
export const CloudDefendEditPolicyExtension = memo<PackagePolicyEditExtensionComponentProps>(
|
||||
({ newPolicy, onChange }) => {
|
||||
return <ConfigYamlView policy={newPolicy} onChange={onChange} />;
|
||||
}
|
||||
);
|
||||
|
||||
CloudDefendEditPolicyExtension.displayName = 'CloudDefendEditPolicyExtension';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { CloudDefendEditPolicyExtension as default };
|
14
x-pack/plugins/cloud_defend/public/index.ts
Executable file
14
x-pack/plugins/cloud_defend/public/index.ts
Executable file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { CloudDefendPlugin } from './plugin';
|
||||
|
||||
// This exports static code and TypeScript types,
|
||||
// as well as, Kibana Platform `plugin()` initializer.
|
||||
export function plugin() {
|
||||
return new CloudDefendPlugin();
|
||||
}
|
||||
export type { CloudDefendPluginSetup, CloudDefendPluginStart } from './types';
|
44
x-pack/plugins/cloud_defend/public/plugin.ts
Executable file
44
x-pack/plugins/cloud_defend/public/plugin.ts
Executable file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { lazy } from 'react';
|
||||
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
|
||||
import {
|
||||
CloudDefendPluginSetup,
|
||||
CloudDefendPluginStart,
|
||||
CloudDefendPluginStartDeps,
|
||||
} from './types';
|
||||
import { INTEGRATION_PACKAGE_NAME } from '../common/constants';
|
||||
|
||||
const LazyEditPolicy = lazy(() => import('./components/fleet_extensions/policy_extension_edit'));
|
||||
const LazyCreatePolicy = lazy(
|
||||
() => import('./components/fleet_extensions/policy_extension_create')
|
||||
);
|
||||
|
||||
export class CloudDefendPlugin implements Plugin<CloudDefendPluginSetup, CloudDefendPluginStart> {
|
||||
public setup(core: CoreSetup): CloudDefendPluginSetup {
|
||||
// Return methods that should be available to other plugins
|
||||
return {};
|
||||
}
|
||||
|
||||
public start(core: CoreStart, plugins: CloudDefendPluginStartDeps): CloudDefendPluginStart {
|
||||
plugins.fleet.registerExtension({
|
||||
package: INTEGRATION_PACKAGE_NAME,
|
||||
view: 'package-policy-create',
|
||||
Component: LazyCreatePolicy,
|
||||
});
|
||||
|
||||
plugins.fleet.registerExtension({
|
||||
package: INTEGRATION_PACKAGE_NAME,
|
||||
view: 'package-policy-edit',
|
||||
Component: LazyEditPolicy,
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
}
|
48
x-pack/plugins/cloud_defend/public/test/test_provider.tsx
Executable file
48
x-pack/plugins/cloud_defend/public/test/test_provider.tsx
Executable file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||
import React, { useMemo } from 'react';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { Router, Switch, Route } from 'react-router-dom';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { fleetMock } from '@kbn/fleet-plugin/public/mocks';
|
||||
import type { CloudDefendPluginStartDeps } from '../types';
|
||||
|
||||
interface CspAppDeps {
|
||||
core: CoreStart;
|
||||
deps: CloudDefendPluginStartDeps;
|
||||
params: AppMountParameters;
|
||||
}
|
||||
|
||||
export const TestProvider: React.FC<Partial<CspAppDeps>> = ({
|
||||
core = coreMock.createStart(),
|
||||
deps = {
|
||||
data: dataPluginMock.createStartContract(),
|
||||
fleet: fleetMock.createStartMock(),
|
||||
},
|
||||
params = coreMock.createAppMountParameters(),
|
||||
children,
|
||||
} = {}) => {
|
||||
const queryClient = useMemo(() => new QueryClient(), []);
|
||||
return (
|
||||
<KibanaContextProvider services={{ ...core, ...deps }}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Router history={params.history}>
|
||||
<I18nProvider>
|
||||
<Switch>
|
||||
<Route path="*" render={() => <>{children}</>} />
|
||||
</Switch>
|
||||
</I18nProvider>
|
||||
</Router>
|
||||
</QueryClientProvider>
|
||||
</KibanaContextProvider>
|
||||
);
|
||||
};
|
17
x-pack/plugins/cloud_defend/public/test/utils.ts
Normal file
17
x-pack/plugins/cloud_defend/public/test/utils.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
export const expectIdsInDoc = ({ be = [], notToBe = [] }: { be: string[]; notToBe?: string[] }) => {
|
||||
be.forEach((testId) => {
|
||||
expect(screen.getByTestId(testId)).toBeInTheDocument();
|
||||
});
|
||||
notToBe.forEach((testId) => {
|
||||
expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
|
||||
});
|
||||
};
|
20
x-pack/plugins/cloud_defend/public/types.ts
Executable file
20
x-pack/plugins/cloud_defend/public/types.ts
Executable file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { FleetSetup, FleetStart } from '@kbn/fleet-plugin/public';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface CloudDefendPluginSetup {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface CloudDefendPluginStart {}
|
||||
|
||||
export interface CloudDefendPluginSetupDeps {
|
||||
fleet: FleetSetup;
|
||||
}
|
||||
export interface CloudDefendPluginStartDeps {
|
||||
fleet: FleetStart;
|
||||
}
|
17
x-pack/plugins/cloud_defend/tsconfig.json
Executable file
17
x-pack/plugins/cloud_defend/tsconfig.json
Executable file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target/types",
|
||||
"emitDeclarationOnly": true,
|
||||
"declaration": true
|
||||
},
|
||||
"include": [
|
||||
"common/**/*",
|
||||
"public/**/*",
|
||||
"../../../typings/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
{ "path": "../../../src/core/tsconfig.json" },
|
||||
{ "path": "../../../x-pack/plugins/fleet/tsconfig.json" }
|
||||
]
|
||||
}
|
39
yarn.lock
39
yarn.lock
|
@ -7018,7 +7018,7 @@
|
|||
"@types/tough-cookie" "*"
|
||||
parse5 "^7.0.0"
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
"@types/json-schema@*", "@types/json-schema@^7", "@types/json-schema@^7.0.0", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
|
||||
|
@ -17962,7 +17962,7 @@ js-yaml@3.14.1, js-yaml@^3.13.1, js-yaml@^3.14.1:
|
|||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@4.1.0, js-yaml@^4.1.0:
|
||||
js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||
|
@ -19911,6 +19911,19 @@ monaco-editor@*, monaco-editor@^0.24.0:
|
|||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.24.0.tgz#990b55096bcc95d08d8d28e55264c6eb17707269"
|
||||
integrity sha512-o1f0Lz6ABFNTtnEqqqvlY9qzNx24rQZx1RgYNQ8SkWkE+Ka63keHH/RqxQ4QhN4fs/UYOnvAtEUZsPrzccH++A==
|
||||
|
||||
monaco-yaml@3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/monaco-yaml/-/monaco-yaml-3.2.1.tgz#45ce9f7f8140dc26249ac99eb0a9e5a9ab9beb87"
|
||||
integrity sha512-2geAd5I7H1SMgwTHBuyPAPK9WTAzxbl9XwDl5h6NY6n9j4qnlLLQKK1i0P9cAmUiV2uaiViz69RLNWqVU5BVsg==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.0"
|
||||
js-yaml "^4.0.0"
|
||||
path-browserify "^1.0.0"
|
||||
prettier "2.0.5"
|
||||
vscode-languageserver-textdocument "^1.0.0"
|
||||
vscode-languageserver-types "^3.0.0"
|
||||
yaml-language-server-parser "^0.1.0"
|
||||
|
||||
monitor-event-loop-delay@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/monitor-event-loop-delay/-/monitor-event-loop-delay-1.0.0.tgz#b5ab78165a3bb93f2b275c50d01430c7f155d1f7"
|
||||
|
@ -21217,7 +21230,7 @@ path-browserify@0.0.1:
|
|||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
|
||||
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
|
||||
|
||||
path-browserify@^1.0.1:
|
||||
path-browserify@^1.0.0, path-browserify@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
|
||||
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
|
||||
|
@ -21952,6 +21965,11 @@ prettier-linter-helpers@^1.0.0:
|
|||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
|
||||
integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
|
||||
|
||||
"prettier@>=2.2.1 <=2.3.0":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
|
||||
|
@ -27544,6 +27562,16 @@ vm-browserify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
|
||||
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
|
||||
|
||||
vscode-languageserver-textdocument@^1.0.0:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz#16df468d5c2606103c90554ae05f9f3d335b771b"
|
||||
integrity sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==
|
||||
|
||||
vscode-languageserver-types@^3.0.0:
|
||||
version "3.17.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz#b2c2e7de405ad3d73a883e91989b850170ffc4f2"
|
||||
integrity sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==
|
||||
|
||||
vt-pbf@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac"
|
||||
|
@ -28318,6 +28346,11 @@ yallist@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
||||
|
||||
yaml-language-server-parser@^0.1.0:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/yaml-language-server-parser/-/yaml-language-server-parser-0.1.3.tgz#f0e9082068291c7c330eefa1f3c9f1b4c3c54183"
|
||||
integrity sha512-xD2I+6M/vqQvcy4ded8JpXUaDHXmZMdhIO3OpuiFxstutwnW4whrfDzNcrsfXVdgMWqOUpdv3747Q081PFN1+g==
|
||||
|
||||
yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue