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


![image](https://user-images.githubusercontent.com/16198204/207160791-73e11e05-953b-42ba-b4dd-a4904bd95451.png)

### 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:
Karl Godard 2022-12-19 16:37:39 -08:00 committed by GitHub
parent 7a6eac8d1b
commit 0b19cfafa3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 918 additions and 6 deletions

View file

@ -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.

View file

@ -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",

View file

@ -47,6 +47,7 @@ RUNTIME_DEPS = [
"@npm//antlr4ts",
"@npm//babel-loader",
"@npm//monaco-editor",
"@npm//monaco-yaml",
"@npm//raw-loader",
"@npm//rxjs",
]

View file

@ -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 };

View file

@ -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
*/

View file

@ -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);

View file

@ -12,6 +12,7 @@ pageLoadAssetSize:
cloud: 21076
cloudChat: 19894
cloudDataMigration: 19170
cloudDefend: 18697
cloudExperiments: 59358
cloudFullStory: 18493
cloudGainsight: 18710

View file

@ -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(?![\\/\\\\](byte-size|monaco-editor|react-monaco-editor|d3-interpolate|d3-color))[/\\\\].+\\.js$',
'[/\\\\]node_modules(?![\\/\\\\](byte-size|monaco-editor|monaco-yaml|vscode-languageserver-types|react-monaco-editor|d3-interpolate|d3-color))[/\\\\].+\\.js$',
'packages/kbn-pm/dist/index.js',
],

View file

@ -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"],

View file

@ -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"],

View file

@ -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",

View file

@ -0,0 +1,7 @@
{
"prefix": "cloudDefend",
"paths": {
"cloudDefend": "."
},
"translations": ["translations/ja-JP.json"]
}

View 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.

View 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';

View 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}',
],
};

View 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": []
}

View 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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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]);
};

View file

@ -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();
});
});

View file

@ -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>
);
};

View file

@ -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',
},
});

View file

@ -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 };
}, []);
};

View file

@ -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>',
});

View 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 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 };

View 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 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 };

View 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';

View 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() {}
}

View 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>
);
};

View 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();
});
};

View 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;
}

View 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" }
]
}

View file

@ -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"