kibana/.eslintrc.js
Kibana Machine 342b05045f
[Security Solutions] Adds back the legacy actions and notification system in a limited fashion (#112869) (#113202)
## Summary

Fixes https://github.com/elastic/security-team/issues/1759

Related earlier PR, https://github.com/elastic/kibana/pull/109722, where these were removed to where they could no longer function. This PR adds them back to where they will function for existing users. The end goal is to have users naturally migrate as they update, enable/disable, or create new rules. 

What this PR does:
* Adds back the legacy side car actions `siem-detection-engine-rule-actions`
* Adds back the legacy hidden alert of `siem.notifications`
* Adds back unit tests where they existed. Both of these systems did not have existing e2e tests.
* Re-adds the find feature and functionality which should show the rules with legacy and non-legacy notifications/side car actions during a REST find operation.
* Updates the logic for when to show a legacy vs. non-legacy notification/side car action.
* Adds a new route called `/internal/api/detection/legacy/notifications` which is only for developer and tests for us to maintain this system for the foreseeable future.
* Adds script to exercise creating old notifications `detection_engine/scripts/post_legacy_notification.sh`
* Adds a data file for the script to use as an example for ad-hoc testing, `scripts/legacy_notifications/one_action.json`
* Adds within `security_solution/server/types.ts` `ActionsApiRequestHandlerContext` so that if we need to directly access actions within plugins we can. I do not use it here, but it should have been existing there and is good to have it in case we need it at this point within REST routes.
* When adding back the files and changes, I use the kibana-core approach of prefixing files, functions, types, etc... with the words `legacyFoo`. The files are named `legacy_foo.ts`. Everything has `@deprecation` above them as well. The intent here is all of this should hopefully make it unambiguously clear which parts of the notification system are for the new system/existing API and which ones are only for the deprecated legacy system. There exists some parts of the system that are used within _both_ and the hope is that we can keep the legacy pieces separate from the non-legacy pieces for strangling the legacy pieces.   
* This adds a new linter rule to prevent users from easily importing files named `legacy_foo.ts` or `foo_legacy.ts` we are using here and can also use for other similar legacy parts of the system we have.  This seems to be the established pattern that kibana-core does as well looking through the linters and code base.
* Removes some dead import/export code and types instead of maintaining them since they are no longer used.

What this PR does not do (but are planned on follow ups):
* This PR does not add migration logic in most conditions such as a user enabling/disabling a rule, editing a rule unless the user is explicitly changing the actions by turning off the notification and then re-adding the notification.
* This PR does not log any information indicating to the user that they are running legacy rules or indicates they have that.
* This PR does not allow the executors or any UI/UX, backend to re-add a legacy notification. Instead only the hidden REST route of `/internal/api/detection/legacy/notifications` allows us to do this for testing purposes.
* This PR does not migrate the data structure of actions legacy notification system `siem-detection-engine-rule-actions` to use saved object references.
* If you delete an alert this will not delete the side car if it detects one is present on it.
* If you update an alert notification with a new notification this will not remove the side car on the update.

**Ad-hoc testing instructions**
How to do ad-hoc testing for various situations such as having a legacy notification system such as a user's or if you want to mimic a malfunction and result of a "split-brain" to where you have both notification systems running at the same time due to a bug or regression:

Create a rule and activate it normally within security_solution:
<img width="1046" alt="Screen Shot 2021-09-22 at 2 09 14 PM" src="https://user-images.githubusercontent.com/1151048/134416564-e4e001a7-1086-46a1-aa8d-79880f70cc35.png">

Do not add actions to the rule at this point as we will first exercise the older legacy system. However, you want at least one action configured such as a slack notification:
<img width="575" alt="Screen Shot 2021-09-22 at 2 28 16 PM" src="https://user-images.githubusercontent.com/1151048/134417012-58e63709-5447-4832-8866-f82be1b9596b.png">

Within dev tools do a query for all your actions and grab one of the `_id` of them without their prefix:
```json
# See all your actions
GET .kibana/_search
{
  "query": {
    "term": {
      "type": "action"
    }
  }
}
```

Mine was `"_id" : "action:879e8ff0-1be1-11ec-a722-83da1c22a481",` so I will be copying the ID of `879e8ff0-1be1-11ec-a722-83da1c22a481`

Go to the file `detection_engine/scripts/legacy_notifications/one_action.json` and add this id to the file. Something like this:
```json
{
  "name": "Legacy notification with one action",
  "interval": "1m",  <--- You can use whatever you want. Real values are "1h", "1d", "1w". I use "1m" for testing purposes.
  "actions": [
    {
      "id": "879e8ff0-1be1-11ec-a722-83da1c22a481", <--- My action id
      "group": "default",
      "params": {
        "message": "Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts"
      },
      "actionTypeId": ".slack" <--- I am a slack action id type.
    }
  ]
}
```

Query for an alert you want to add manually add back a legacy notification to it. Such as:
```json
# See all your siem.signals alert types and choose one
GET .kibana/_search
{
  "query": {
    "term": {
      "alert.alertTypeId": "siem.signals"
    }
  }
}
```

Grab the `_id` without the `alert` prefix. For mine this was `933ca720-1be1-11ec-a722-83da1c22a481`

Within the directory of `detection_engine/scripts` execute the script
```bash
./post_legacy_notification.sh 933ca720-1be1-11ec-a722-83da1c22a481
{
  "ok": "acknowledged"
}
```

which is going to do a few things. See the file `detection_engine/routes/rules/legacy_create_legacy_notification.ts` for the definition of the route and what it does in full, but we should notice that we have now:

Created a legacy side car action object of type `siem-detection-engine-rule-actions` you can see in dev tools:
```json
# See the actions "side car" which are part of the legacy notification system.
GET .kibana/_search
{
  "query": {
    "term": {
      "type": {
        "value": "siem-detection-engine-rule-actions"
      }
    }
  }
}
```

Note in the response:
```json
          "siem-detection-engine-rule-actions" : {
            "ruleAlertId" : "933ca720-1be1-11ec-a722-83da1c22a481", <--- NOTE, not migrated to references yet
            "actions" : [
              {
                "action_type_id" : ".slack",
                "id" : "879e8ff0-1be1-11ec-a722-83da1c22a481", <--- NOTE, not migrated to references yet
                "params" : {
                  "message" : "Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts"
                },
                "group" : "default"
              }
            ],
            "ruleThrottle" : "1m", <--- Should be the same as the interval in "one_action.json" config
            "alertThrottle" : "1m" <--- Should be the same as the interval in "one_action.json" config
          },
          "type" : "siem-detection-engine-rule-actions",
          "references" : [ ],
```

Created a `siem.notification` rule instance which you can see in dev tools as well:
```json
# Get the alert type of "siem-notifications" which is part of the legacy system.
GET .kibana/_search
{
  "query": {
    "term": {
      "alert.alertTypeId": "siem.notifications"
    }
  }
}
```

Take note from the `siem.notifications` these values which determine how/when it fires and if your actions are set up correctly:
```json
            "name" : "Legacy notification with one action" <--- Our name from one_action.json 
            "schedule" : {
              "interval" : "1m" <--- Interval should match interval in one_action.json
            },
            "enabled" : true, <--- We should be enabled
            "actions" : [
              {
                "group" : "default",
                "params" : {
                  "message" : "Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts"
                },
                "actionTypeId" : ".slack", <--- Our actionID
                "actionRef" : "action_0"
              }
            ],
```


And that now there exists a task within task manager that will be executing this:
```json
# Get the tasks of siem notifications to ensure and see it is running
GET .task-manager/_search
{
  "query": {
    "term": {
      "task.taskType": "alerting:siem.notifications"
    }
  }
}
```

You can double check the interval from the result of the query to ensure it runs as the configuration test file shows it should be:
```json
            "schedule" : {
              "interval" : "1m"
            },
```

Within time you should see your action execute like the legacy notification system:
<img width="876" alt="Screen Shot 2021-09-22 at 2 55 28 PM" src="https://user-images.githubusercontent.com/1151048/134422639-80523abb-f43c-4f7c-abef-a60062bef139.png">

If you go to edit the rule you should notice that the rule now has the side car attached to it within the UI:
<img width="1050" alt="Screen Shot 2021-09-22 at 8 08 54 PM" src="https://user-images.githubusercontent.com/1151048/134445265-fa0a330b-3238-48e2-aef3-6042c7e9aa69.png">

You can also look at your log messages in debug mode to verify the behaviors of the legacy system and the normal rules running.

Compare these data structures to a 7.14.x system in cloud to ensure the data looks the same and the ad-hoc testing functions as expected.

Check the scripts of `./find_rules.sh`, `./read_rules.sh` to ensure that the find REST route returns the legacy actions when they are there.

### Checklist


- [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

Co-authored-by: Frank Hassanabad <frank.hassanabad@elastic.co>
2021-09-27 22:10:54 -04:00

1710 lines
54 KiB
JavaScript

/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
const APACHE_2_0_LICENSE_HEADER = `
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
`;
const OLD_DUAL_LICENSE_HEADER = `
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* and the Server Side Public License, v 1; you may not use this file except in
* compliance with, at your election, the Elastic License or the Server Side
* Public License, v 1.
*/
`;
const DUAL_LICENSE_HEADER = `
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
`;
const OLD_ELASTIC_LICENSE_HEADER = `
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
`;
const ELASTIC_LICENSE_HEADER = `
/*
* 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.
*/
`;
const SAFER_LODASH_SET_HEADER = `
/*
* Elasticsearch B.V licenses this file to you under the MIT License.
* See \`packages/elastic-safer-lodash-set/LICENSE\` for more information.
*/
`;
const SAFER_LODASH_SET_LODASH_HEADER = `
/*
* This file is forked from the lodash project (https://lodash.com/),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See \`packages/elastic-safer-lodash-set/LICENSE\` for more information.
*/
`;
const SAFER_LODASH_SET_DEFINITELYTYPED_HEADER = `
/*
* This file is forked from the DefinitelyTyped project (https://github.com/DefinitelyTyped/DefinitelyTyped),
* and may include modifications made by Elasticsearch B.V.
* Elasticsearch B.V. licenses this file to you under the MIT License.
* See \`packages/elastic-safer-lodash-set/LICENSE\` for more information.
*/
`;
/** Packages which should not be included within production code. */
const DEV_PACKAGES = [
'kbn-babel-code-parser',
'kbn-dev-utils',
'kbn-cli-dev-mode',
'kbn-docs-utils',
'kbn-es*',
'kbn-eslint*',
'kbn-optimizer',
'kbn-plugin-generator',
'kbn-plugin-helpers',
'kbn-pm',
'kbn-storybook',
'kbn-telemetry-tools',
'kbn-test',
];
/** Directories (at any depth) which include dev-only code. */
const DEV_DIRECTORIES = [
'.storybook',
'__tests__',
'__test__',
'__jest__',
'__fixtures__',
'__mocks__',
'__stories__',
'e2e',
'fixtures',
'ftr_e2e',
'integration_tests',
'manual_tests',
'mock',
'storybook',
'scripts',
'test',
'test-d',
'test_utils',
'test_utilities',
'test_helpers',
'tests_client_integration',
];
/** File patterns for dev-only code. */
const DEV_FILE_PATTERNS = [
'*.mock.{js,ts,tsx}',
'*.test.{js,ts,tsx}',
'*.test.helpers.{js,ts,tsx}',
'*.stories.{js,ts,tsx}',
'*.story.{js,ts,tsx}',
'*.stub.{js,ts,tsx}',
'mock.{js,ts,tsx}',
'_stubs.{js,ts,tsx}',
'{testHelpers,test_helper,test_utils}.{js,ts,tsx}',
'{postcss,webpack}.config.js',
];
/** Glob patterns which describe dev-only code. */
const DEV_PATTERNS = [
...DEV_PACKAGES.map((pkg) => `packages/${pkg}/**/*`),
...DEV_DIRECTORIES.map((dir) => `{packages,src,x-pack}/**/${dir}/**/*`),
...DEV_FILE_PATTERNS.map((file) => `{packages,src,x-pack}/**/${file}`),
'packages/kbn-interpreter/tasks/**/*',
'src/dev/**/*',
'x-pack/{dev-tools,tasks,scripts,test,build_chromium}/**/*',
'x-pack/plugins/*/server/scripts/**/*',
];
module.exports = {
root: true,
extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'],
overrides: [
/**
* Temporarily disable some react rules for specific plugins, remove in separate PRs
*/
{
files: ['src/plugins/eui_utils/**/*.{js,mjs,ts,tsx}'],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: ['src/plugins/kibana_react/**/*.{js,mjs,ts,tsx}'],
rules: {
'react-hooks/rules-of-hooks': 'off',
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: ['src/plugins/kibana_utils/**/*.{js,mjs,ts,tsx}'],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: ['x-pack/plugins/canvas/**/*.{js,mjs,ts,tsx}'],
rules: {
'jsx-a11y/click-events-have-key-events': 'off',
},
},
{
files: ['x-pack/plugins/cross_cluster_replication/**/*.{js,mjs,ts,tsx}'],
rules: {
'jsx-a11y/click-events-have-key-events': 'off',
},
},
{
files: ['x-pack/plugins/ml/**/*.{js,mjs,ts,tsx}'],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
/**
* Files that require dual-license headers, settings
* are overridden below for files that require Elastic
* Licence headers
*/
{
files: [
'**/*.{js,mjs,ts,tsx}',
'!plugins/**/*',
'!packages/elastic-datemath/**/*',
'!packages/elastic-eslint-config-kibana/**/*',
],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: DUAL_LICENSE_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
APACHE_2_0_LICENSE_HEADER,
ELASTIC_LICENSE_HEADER,
OLD_DUAL_LICENSE_HEADER,
OLD_ELASTIC_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
/**
* Files that require Apache headers
*/
{
files: [
'packages/elastic-datemath/**/*.{js,mjs,ts,tsx}',
'packages/elastic-eslint-config-kibana/**/*.{js,mjs,ts,tsx}',
],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: APACHE_2_0_LICENSE_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
DUAL_LICENSE_HEADER,
ELASTIC_LICENSE_HEADER,
OLD_DUAL_LICENSE_HEADER,
OLD_ELASTIC_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
/**
* New Platform client-side
*/
{
files: ['{src,x-pack}/plugins/*/public/**/*.{js,mjs,ts,tsx}'],
rules: {
'import/no-commonjs': 'error',
},
},
/**
* Files that require Elastic license headers instead of dual-license header
*/
{
files: ['x-pack/**/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: ELASTIC_LICENSE_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
APACHE_2_0_LICENSE_HEADER,
DUAL_LICENSE_HEADER,
OLD_DUAL_LICENSE_HEADER,
OLD_ELASTIC_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
/**
* safer-lodash-set package requires special license headers
*/
{
files: ['packages/elastic-safer-lodash-set/**/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: SAFER_LODASH_SET_LODASH_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
APACHE_2_0_LICENSE_HEADER,
DUAL_LICENSE_HEADER,
ELASTIC_LICENSE_HEADER,
OLD_DUAL_LICENSE_HEADER,
OLD_ELASTIC_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
{
files: ['packages/elastic-safer-lodash-set/test/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: SAFER_LODASH_SET_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
APACHE_2_0_LICENSE_HEADER,
DUAL_LICENSE_HEADER,
ELASTIC_LICENSE_HEADER,
OLD_DUAL_LICENSE_HEADER,
OLD_ELASTIC_LICENSE_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
],
},
],
},
},
{
files: ['packages/elastic-safer-lodash-set/**/*.d.ts'],
rules: {
'@kbn/eslint/require-license-header': [
'error',
{
license: SAFER_LODASH_SET_DEFINITELYTYPED_HEADER,
},
],
'@kbn/eslint/disallow-license-headers': [
'error',
{
licenses: [
APACHE_2_0_LICENSE_HEADER,
DUAL_LICENSE_HEADER,
ELASTIC_LICENSE_HEADER,
OLD_ELASTIC_LICENSE_HEADER,
OLD_DUAL_LICENSE_HEADER,
SAFER_LODASH_SET_HEADER,
SAFER_LODASH_SET_LODASH_HEADER,
],
},
],
},
},
/**
* Restricted paths
*/
{
files: ['**/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/no-restricted-paths': [
'error',
{
basePath: __dirname,
zones: [
{
target: ['(src|x-pack)/**/*', '!src/core/**/*'],
from: ['src/core/utils/**/*'],
errorMessage: `Plugins may only import from src/core/server and src/core/public.`,
},
{
target: ['(src|x-pack)/plugins/*/server/**/*'],
from: ['(src|x-pack)/plugins/*/public/**/*'],
errorMessage: `Server code can not import from public, use a common directory.`,
},
{
target: ['(src|x-pack)/plugins/*/common/**/*'],
from: ['(src|x-pack)/plugins/*/(server|public)/**/*'],
errorMessage: `Common code can not import from server or public, use a common directory.`,
},
{
target: ['(src|x-pack)/plugins/**/(public|server)/**/*', 'examples/**/*'],
from: [
'src/core/public/**/*',
'!src/core/public/index.ts', // relative import
'!src/core/public/mocks{,.ts}',
'!src/core/server/types{,.ts}',
'!src/core/public/utils/**/*',
'!src/core/public/*.test.mocks{,.ts}',
'src/core/server/**/*',
'!src/core/server/index.ts', // relative import
'!src/core/server/mocks{,.ts}',
'!src/core/server/types{,.ts}',
'!src/core/server/test_utils{,.ts}',
// for absolute imports until fixed in
// https://github.com/elastic/kibana/issues/36096
'!src/core/server/*.test.mocks{,.ts}',
'target/types/**',
],
allowSameFolder: true,
errorMessage:
'Plugins may only import from top-level public and server modules in core.',
},
{
target: [
'(src|x-pack)/plugins/**/(public|server)/**/*',
'examples/**/*',
'!(src|x-pack)/**/*.test.*',
'!(x-pack/)?test/**/*',
],
from: [
'(src|x-pack)/plugins/**/(public|server)/**/*',
'!(src|x-pack)/plugins/**/(public|server)/mocks/index.{js,mjs,ts}',
'!(src|x-pack)/plugins/**/(public|server)/(index|mocks).{js,mjs,ts,tsx}',
'!(src|x-pack)/plugins/**/__stories__/index.{js,mjs,ts,tsx}',
'!(src|x-pack)/plugins/**/__fixtures__/index.{js,mjs,ts,tsx}',
],
allowSameFolder: true,
errorMessage: 'Plugins may only import from top-level public and server modules.',
},
{
target: [
'(src|x-pack)/plugins/**/*',
'!(src|x-pack)/plugins/**/server/**/*',
'examples/**/*',
'!examples/**/server/**/*',
],
from: [
'src/core/server',
'src/core/server/**/*',
'(src|x-pack)/plugins/*/server/**/*',
'examples/**/server/**/*',
],
errorMessage:
'Server modules cannot be imported into client modules or shared modules.',
},
{
target: ['src/core/**/*'],
from: ['plugins/**/*', 'src/plugins/**/*'],
errorMessage: 'The core cannot depend on any plugins.',
},
{
target: ['(src|x-pack)/plugins/*/public/**/*'],
from: ['ui/**/*'],
errorMessage: 'Plugins cannot import legacy UI code.',
},
],
},
],
},
},
/**
* Allow default exports
*/
{
files: [
'**/*.stories.tsx',
'x-pack/test/apm_api_integration/**/*.ts',
'x-pack/test/functional/apps/**/*.js',
'x-pack/plugins/apm/**/*.js',
'test/*/config.ts',
'test/*/config_open.ts',
'test/*/{tests,test_suites,apis,apps}/**/*',
'test/visual_regression/tests/**/*',
'x-pack/test/*/{tests,test_suites,apis,apps}/**/*',
'x-pack/test/*/*config.*ts',
'x-pack/test/saved_object_api_integration/*/apis/**/*',
'x-pack/test/ui_capabilities/*/tests/**/*',
],
rules: {
'import/no-default-export': 'off',
'import/no-named-as-default': 'off',
},
},
/**
* Files that are allowed to import webpack-specific stuff
*/
{
files: [
'**/public/**/*.js',
'src/fixtures/**/*.js', // TODO: this directory needs to be more obviously "public" (or go away)
],
settings: {
// instructs import/no-extraneous-dependencies to treat certain modules
// as core modules, even if they aren't listed in package.json
'import/core-modules': ['plugins'],
'import/resolver': {
'@kbn/eslint-import-resolver-kibana': {
forceNode: false,
rootPackageName: 'kibana',
kibanaPath: '.',
pluginMap: {},
},
},
},
},
/**
* Single package.json rules, it tells eslint to ignore the child package.json files
* and look for dependencies declarations in the single and root level package.json
*/
{
files: ['{src,x-pack,packages}/**/*.{js,mjs,ts,tsx}'],
rules: {
'import/no-extraneous-dependencies': [
'error',
{
/* Files that ARE allowed to use devDependencies */
devDependencies: [...DEV_PATTERNS],
peerDependencies: true,
packageDir: '.',
},
],
},
},
/**
* Files that run BEFORE node version check
*/
{
files: ['scripts/**/*.js', 'src/setup_node_env/**/*.js'],
rules: {
'import/no-commonjs': 'off',
'prefer-object-spread/prefer-object-spread': 'off',
'no-var': 'off',
'prefer-const': 'off',
'prefer-destructuring': 'off',
'no-restricted-syntax': [
'error',
'ImportDeclaration',
'ExportNamedDeclaration',
'ExportDefaultDeclaration',
'ExportAllDeclaration',
'ArrowFunctionExpression',
'AwaitExpression',
'ClassDeclaration',
'RestElement',
'SpreadElement',
'YieldExpression',
'VariableDeclaration[kind="const"]',
'VariableDeclaration[kind="let"]',
'VariableDeclarator[id.type="ArrayPattern"]',
'VariableDeclarator[id.type="ObjectPattern"]',
],
},
},
/**
* Files that run in the browser with only node-level transpilation
*/
{
files: [
'test/functional/services/lib/web_element_wrapper/scroll_into_view_if_necessary.js',
'**/browser_exec_scripts/**/*.js',
],
rules: {
'prefer-object-spread/prefer-object-spread': 'off',
'no-var': 'off',
'prefer-const': 'off',
'prefer-destructuring': 'off',
'no-restricted-syntax': [
'error',
'ArrowFunctionExpression',
'AwaitExpression',
'ClassDeclaration',
'ImportDeclaration',
'RestElement',
'SpreadElement',
'YieldExpression',
'VariableDeclaration[kind="const"]',
'VariableDeclaration[kind="let"]',
'VariableDeclarator[id.type="ArrayPattern"]',
'VariableDeclarator[id.type="ObjectPattern"]',
],
},
},
/**
* Files that run AFTER node version check
* and are not also transpiled with babel
*/
{
files: [
'.eslintrc.js',
'packages/kbn-eslint-import-resolver-kibana/**/*.js',
'packages/kbn-eslint-plugin-eslint/**/*',
'x-pack/gulpfile.js',
'x-pack/scripts/*.js',
],
excludedFiles: ['**/integration_tests/**/*'],
rules: {
'import/no-commonjs': 'off',
'prefer-object-spread/prefer-object-spread': 'off',
'no-restricted-syntax': [
'error',
'ImportDeclaration',
'ExportNamedDeclaration',
'ExportDefaultDeclaration',
'ExportAllDeclaration',
],
},
},
/**
* Jest specific rules
*/
{
files: ['**/*.test.{js,mjs,ts,tsx}'],
rules: {
'jest/valid-describe': 'error',
},
},
/**
* Harden specific rules
*/
{
files: ['test/harden/*.js', 'packages/elastic-safer-lodash-set/test/*.js'],
rules: {
'mocha/handle-done-callback': 'off',
},
},
{
files: ['**/*.{js,mjs,ts,tsx}'],
rules: {
'no-restricted-imports': [
2,
{
paths: [
{
name: 'lodash',
importNames: ['set', 'setWith'],
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash.set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash.setwith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp',
importNames: ['set', 'setWith', 'assoc', 'assocPath'],
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/assoc',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/fp/assocPath',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash',
importNames: ['template'],
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
name: 'lodash.template',
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
name: 'lodash/template',
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
name: 'lodash/fp',
importNames: ['template'],
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
name: 'lodash/fp/template',
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
name: 'react-use',
message: 'Please use react-use/lib/{method} instead.',
},
],
},
],
'no-restricted-modules': [
2,
{
paths: [
{
name: 'lodash.set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash.setwith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash.template',
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
name: 'lodash/set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
name: 'lodash/template',
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
],
},
],
'no-restricted-properties': [
2,
{
object: 'lodash',
property: 'set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'set',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: 'lodash',
property: 'template',
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
object: '_',
property: 'template',
message:
'lodash.template is unsafe, and not compatible with our content security policy.',
},
{
object: 'lodash',
property: 'setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'setWith',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: 'lodash',
property: 'assoc',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'assoc',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: 'lodash',
property: 'assocPath',
message: 'Please use @elastic/safer-lodash-set instead',
},
{
object: '_',
property: 'assocPath',
message: 'Please use @elastic/safer-lodash-set instead',
},
],
},
},
/**
* APM and Observability overrides
*/
{
files: [
'x-pack/plugins/apm/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/observability/**/*.{js,mjs,ts,tsx}',
],
rules: {
'no-console': ['warn', { allow: ['error'] }],
'react/function-component-definition': [
'warn',
{
namedComponents: 'function-declaration',
unnamedComponents: 'arrow-function',
},
],
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useFetcher$' }],
},
},
{
files: ['x-pack/plugins/apm/**/*.stories.*', 'x-pack/plugins/observability/**/*.stories.*'],
rules: {
'react/function-component-definition': [
'off',
{
namedComponents: 'function-declaration',
unnamedComponents: 'arrow-function',
},
],
},
},
/**
* Fleet overrides
*/
{
files: ['x-pack/plugins/fleet/**/*.{js,mjs,ts,tsx}'],
rules: {
'@typescript-eslint/consistent-type-imports': 'error',
'import/order': [
'warn',
{
groups: ['builtin', 'external', 'internal', 'parent'],
'newlines-between': 'always-and-inside-groups',
},
],
},
},
/**
* Cases overrides
*/
{
files: ['x-pack/plugins/cases/**/*.{js,mjs,ts,tsx}'],
rules: {
'no-duplicate-imports': 'off',
'@typescript-eslint/no-duplicate-imports': ['error'],
},
},
/**
* Security Solution overrides
*/
{
// front end and common typescript and javascript files only
files: [
'x-pack/plugins/security_solution/public/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/security_solution/common/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/timelines/public/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/timelines/common/**/*.{js,mjs,ts,tsx}',
],
rules: {
'import/no-nodejs-modules': 'error',
'no-duplicate-imports': 'off',
'@typescript-eslint/no-duplicate-imports': ['error'],
'no-restricted-imports': [
'error',
{
// prevents UI code from importing server side code and then webpack including it when doing builds
patterns: ['**/server/*'],
},
],
},
},
{
// typescript only for front and back end
files: [
'x-pack/plugins/security_solution/**/*.{ts,tsx}',
'x-pack/plugins/timelines/**/*.{ts,tsx}',
],
rules: {
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/unified-signatures': 'error',
'no-restricted-imports': [
'error',
{
// prevents code from importing files that contain the name "legacy" within their name. This is a mechanism
// to help deprecation and prevent accidental re-use/continued use of code we plan on removing. If you are
// finding yourself turning this off a lot for "new code" consider renaming the file and functions if it is has valid uses.
patterns: ['*legacy*'],
},
],
},
},
{
// typescript and javascript for front and back end
files: [
'x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/timelines/**/*.{js,mjs,ts,tsx}',
],
plugins: ['eslint-plugin-node', 'react'],
env: {
jest: true,
},
rules: {
'accessor-pairs': 'error',
'array-callback-return': 'error',
'no-array-constructor': 'error',
complexity: 'warn',
'node/no-deprecated-api': 'error',
'no-bitwise': 'error',
'no-continue': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-duplicate-imports': 'off',
'no-empty-character-class': 'error',
'no-empty-pattern': 'error',
'no-ex-assign': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-extra-boolean-cast': 'error',
'no-extra-label': 'error',
'no-func-assign': 'error',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-invalid-regexp': 'error',
'no-inner-declarations': 'error',
'no-lone-blocks': 'error',
'no-multi-assign': 'error',
'no-misleading-character-class': 'error',
'no-new-symbol': 'error',
'no-obj-calls': 'error',
'no-param-reassign': 'error',
'no-process-exit': 'error',
'no-prototype-builtins': 'error',
'no-return-await': 'error',
'no-self-compare': 'error',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-this-before-super': 'error',
// rely on typescript
'no-undef': 'off',
'no-unreachable': 'error',
'no-unsafe-finally': 'error',
'no-useless-call': 'error',
'no-useless-catch': 'error',
'no-useless-concat': 'error',
'no-useless-computed-key': 'error',
'no-useless-rename': 'error',
'no-useless-return': 'error',
'one-var-declaration-per-line': 'error',
'prefer-object-spread': 'error',
'prefer-promise-reject-errors': 'error',
'prefer-rest-params': 'error',
'prefer-spread': 'error',
'prefer-template': 'error',
'react/boolean-prop-naming': 'error',
'react/button-has-type': 'error',
'react/display-name': 'error',
'react/forbid-dom-props': 'error',
'react/no-access-state-in-setstate': 'error',
'react/no-children-prop': 'error',
'react/no-danger-with-children': 'error',
'react/no-deprecated': 'error',
'react/no-did-mount-set-state': 'error',
'react/no-direct-mutation-state': 'error',
'react/no-find-dom-node': 'error',
'react/no-redundant-should-component-update': 'error',
'react/no-render-return-value': 'error',
'react/no-typos': 'error',
'react/no-string-refs': 'error',
'react/no-this-in-sfc': 'error',
'react/no-unescaped-entities': 'error',
'react/no-unsafe': 'error',
'react/no-unused-prop-types': 'error',
'react/no-unused-state': 'error',
'react/void-dom-elements-no-children': 'error',
'react/jsx-no-comment-textnodes': 'error',
'react/jsx-no-literals': 'error',
'react/jsx-no-target-blank': 'error',
'react/jsx-fragments': 'error',
'react/jsx-sort-default-props': 'error',
'require-atomic-updates': 'error',
'symbol-description': 'error',
'vars-on-top': 'error',
'@typescript-eslint/no-duplicate-imports': ['error'],
},
},
/**
* Lists overrides
*/
{
// front end and common typescript and javascript files only
files: [
'x-pack/plugins/lists/public/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/lists/common/**/*.{js,mjs,ts,tsx}',
],
rules: {
'import/no-nodejs-modules': 'error',
'no-restricted-imports': [
'error',
{
// prevents UI code from importing server side code and then webpack including it when doing builds
patterns: ['**/server/*'],
},
],
},
},
{
// typescript for /public and /common
files: ['x-pack/plugins/lists/public/*.{ts,tsx}', 'x-pack/plugins/lists/common/*.{ts,tsx}'],
rules: {
'@typescript-eslint/no-for-in-array': 'error',
},
},
{
// typescript for /public and /common
files: ['x-pack/plugins/lists/public/*.{ts,tsx}', 'x-pack/plugins/lists/common/*.{ts,tsx}'],
plugins: ['react'],
env: {
jest: true,
},
rules: {
'react/boolean-prop-naming': 'error',
'react/button-has-type': 'error',
'react/display-name': 'error',
'react/forbid-dom-props': 'error',
'react/no-access-state-in-setstate': 'error',
'react/no-children-prop': 'error',
'react/no-danger-with-children': 'error',
'react/no-deprecated': 'error',
'react/no-did-mount-set-state': 'error',
'react/no-did-update-set-state': 'error',
'react/no-direct-mutation-state': 'error',
'react/no-find-dom-node': 'error',
'react/no-redundant-should-component-update': 'error',
'react/no-render-return-value': 'error',
'react/no-typos': 'error',
'react/no-string-refs': 'error',
'react/no-this-in-sfc': 'error',
'react/no-unescaped-entities': 'error',
'react/no-unsafe': 'error',
'react/no-unused-prop-types': 'error',
'react/no-unused-state': 'error',
'react/sort-comp': 'error',
'react/void-dom-elements-no-children': 'error',
'react/jsx-no-comment-textnodes': 'error',
'react/jsx-no-literals': 'error',
'react/jsx-no-target-blank': 'error',
'react/jsx-fragments': 'error',
'react/jsx-sort-default-props': 'error',
},
},
{
files: ['x-pack/plugins/lists/public/**/!(*.test).{js,mjs,ts,tsx}'],
plugins: ['react-perf'],
rules: {
'react-perf/jsx-no-new-object-as-prop': 'error',
'react-perf/jsx-no-new-array-as-prop': 'error',
'react-perf/jsx-no-new-function-as-prop': 'error',
'react/jsx-no-bind': 'error',
},
},
{
// typescript and javascript for front and back
files: ['x-pack/plugins/lists/**/*.{js,mjs,ts,tsx}'],
plugins: ['eslint-plugin-node'],
env: {
jest: true,
},
rules: {
'accessor-pairs': 'error',
'array-callback-return': 'error',
'no-array-constructor': 'error',
complexity: 'error',
'consistent-return': 'error',
'func-style': ['error', 'expression'],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
},
],
'sort-imports': [
'error',
{
ignoreDeclarationSort: true,
},
],
'node/no-deprecated-api': 'error',
'no-bitwise': 'error',
'no-continue': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-duplicate-imports': 'error',
'no-empty-character-class': 'error',
'no-empty-pattern': 'error',
'no-ex-assign': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-extra-boolean-cast': 'error',
'no-extra-label': 'error',
'no-func-assign': 'error',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-invalid-regexp': 'error',
'no-inner-declarations': 'error',
'no-lone-blocks': 'error',
'no-multi-assign': 'error',
'no-misleading-character-class': 'error',
'no-new-symbol': 'error',
'no-obj-calls': 'error',
'no-param-reassign': ['error', { props: true }],
'no-process-exit': 'error',
'no-prototype-builtins': 'error',
'no-return-await': 'error',
'no-self-compare': 'error',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-this-before-super': 'error',
// rely on typescript
'no-undef': 'off',
'no-unreachable': 'error',
'no-unsafe-finally': 'error',
'no-useless-call': 'error',
'no-useless-catch': 'error',
'no-useless-concat': 'error',
'no-useless-computed-key': 'error',
'no-useless-escape': 'error',
'no-useless-rename': 'error',
'no-useless-return': 'error',
'no-void': 'error',
'one-var-declaration-per-line': 'error',
'prefer-object-spread': 'error',
'prefer-promise-reject-errors': 'error',
'prefer-rest-params': 'error',
'prefer-spread': 'error',
'prefer-template': 'error',
'require-atomic-updates': 'error',
'symbol-description': 'error',
'vars-on-top': 'error',
'@typescript-eslint/explicit-member-accessibility': 'error',
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/unified-signatures': 'error',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'no-template-curly-in-string': 'error',
'sort-keys': 'error',
'prefer-destructuring': 'error',
'no-restricted-imports': [
'error',
{
// prevents code from importing files that contain the name "legacy" within their name. This is a mechanism
// to help deprecation and prevent accidental re-use/continued use of code we plan on removing. If you are
// finding yourself turning this off a lot for "new code" consider renaming the file and functions if it has valid uses.
patterns: ['*legacy*'],
},
],
},
},
/**
* Metrics entities overrides
*/
{
// front end and common typescript and javascript files only
files: [
'x-pack/plugins/metrics_entities/public/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/metrics_entities/common/**/*.{js,mjs,ts,tsx}',
],
rules: {
'import/no-nodejs-modules': 'error',
'no-restricted-imports': [
'error',
{
// prevents UI code from importing server side code and then webpack including it when doing builds
patterns: ['**/server/*'],
},
],
},
},
{
// typescript and javascript for front and back end
files: ['x-pack/plugins/metrics_entities/**/*.{js,mjs,ts,tsx}'],
plugins: ['eslint-plugin-node'],
env: {
jest: true,
},
rules: {
'accessor-pairs': 'error',
'array-callback-return': 'error',
'no-array-constructor': 'error',
complexity: 'error',
'consistent-return': 'error',
'func-style': ['error', 'expression'],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
},
],
'sort-imports': [
'error',
{
ignoreDeclarationSort: true,
},
],
'node/no-deprecated-api': 'error',
'no-bitwise': 'error',
'no-continue': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-duplicate-imports': 'error',
'no-empty-character-class': 'error',
'no-empty-pattern': 'error',
'no-ex-assign': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-extra-boolean-cast': 'error',
'no-extra-label': 'error',
'no-func-assign': 'error',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-invalid-regexp': 'error',
'no-inner-declarations': 'error',
'no-lone-blocks': 'error',
'no-multi-assign': 'error',
'no-misleading-character-class': 'error',
'no-new-symbol': 'error',
'no-obj-calls': 'error',
'no-param-reassign': ['error', { props: true }],
'no-process-exit': 'error',
'no-prototype-builtins': 'error',
'no-return-await': 'error',
'no-self-compare': 'error',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-this-before-super': 'error',
// rely on typescript
'no-undef': 'off',
'no-unreachable': 'error',
'no-unsafe-finally': 'error',
'no-useless-call': 'error',
'no-useless-catch': 'error',
'no-useless-concat': 'error',
'no-useless-computed-key': 'error',
'no-useless-escape': 'error',
'no-useless-rename': 'error',
'no-useless-return': 'error',
'no-void': 'error',
'one-var-declaration-per-line': 'error',
'prefer-object-spread': 'error',
'prefer-promise-reject-errors': 'error',
'prefer-rest-params': 'error',
'prefer-spread': 'error',
'prefer-template': 'error',
'require-atomic-updates': 'error',
'symbol-description': 'error',
'vars-on-top': 'error',
'@typescript-eslint/explicit-member-accessibility': 'error',
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/unified-signatures': 'error',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'no-template-curly-in-string': 'error',
'sort-keys': 'error',
'prefer-destructuring': 'error',
'no-restricted-imports': [
'error',
{
// prevents code from importing files that contain the name "legacy" within their name. This is a mechanism
// to help deprecation and prevent accidental re-use/continued use of code we plan on removing. If you are
// finding yourself turning this off a lot for "new code" consider renaming the file and functions if it has valid uses.
patterns: ['*legacy*'],
},
],
},
},
/**
* Alerting Services overrides
*/
{
// typescript for front and back end
files: [
'x-pack/plugins/{alerting,stack_alerts,actions,task_manager,event_log}/**/*.{ts,tsx}',
],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
},
},
{
// typescript only for back end
files: ['x-pack/plugins/triggers_actions_ui/server/**/*.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
},
},
/**
* Lens overrides
*/
{
files: ['x-pack/plugins/lens/**/*.{ts,tsx}'],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
},
},
/**
* Discover overrides
*/
{
files: ['src/plugins/discover/**/*.{ts,tsx}'],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-expect-error': false,
},
],
},
},
/**
* Enterprise Search overrides
* NOTE: We also have a single rule at the bottom of the file that
* overrides Prettier's default of not linting unnecessary backticks
*/
{
// All files
files: ['x-pack/plugins/enterprise_search/**/*.{ts,tsx}'],
rules: {
'import/order': [
'error',
{
groups: ['unknown', ['builtin', 'external'], 'internal', 'parent', 'sibling', 'index'],
pathGroups: [
{
pattern:
'{../../../../../../,../../../../../,../../../../,../../../,../../,../,./}{common/,*}__mocks__{*,/**}',
group: 'unknown',
},
{
pattern: '{**,.}/*.mock',
group: 'unknown',
},
{
pattern: 'react*',
group: 'external',
position: 'before',
},
{
pattern: '{@elastic/**,@kbn/**,src/**}',
group: 'internal',
},
],
pathGroupsExcludedImportTypes: [],
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
'newlines-between': 'always-and-inside-groups',
},
],
'import/newline-after-import': 'error',
'react-hooks/exhaustive-deps': 'off',
'react/jsx-boolean-value': ['error', 'never'],
},
},
{
// Source files only - allow `any` in test/mock files
files: ['x-pack/plugins/enterprise_search/**/*.{ts,tsx}'],
excludedFiles: ['x-pack/plugins/enterprise_search/**/*.{test,mock,test_helper}.{ts,tsx}'],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
},
},
/**
* Canvas overrides
*/
{
files: ['x-pack/plugins/canvas/**/*.js'],
rules: {
radix: 'error',
// module importing
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
},
],
'import/extensions': ['error', 'never', { json: 'always', less: 'always', svg: 'always' }],
// react
'react/no-did-mount-set-state': 'error',
'react/no-did-update-set-state': 'error',
'react/no-multi-comp': ['error', { ignoreStateless: true }],
'react/self-closing-comp': 'error',
'react/sort-comp': 'error',
'react/jsx-boolean-value': 'error',
'react/no-unescaped-entities': ['error', { forbid: ['>', '}'] }],
'react/forbid-elements': [
'error',
{
forbid: [
{
element: 'EuiConfirmModal',
message: 'Use <ConfirmModal> instead',
},
{
element: 'EuiPopover',
message: 'Use <Popover> instead',
},
{
element: 'EuiIconTip',
message: 'Use <TooltipIcon> instead',
},
],
},
],
},
},
{
files: [
'x-pack/plugins/canvas/gulpfile.js',
'x-pack/plugins/canvas/scripts/*.js',
'x-pack/plugins/canvas/tasks/*.js',
'x-pack/plugins/canvas/tasks/**/*.js',
'x-pack/plugins/canvas/__tests__/**/*.js',
'x-pack/plugins/canvas/**/{__tests__,__test__,__jest__,__fixtures__,__mocks__}/**/*.js',
],
rules: {
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: true,
peerDependencies: true,
packageDir: '.',
},
],
},
},
{
files: ['x-pack/plugins/canvas/canvas_plugin_src/**/*.js'],
globals: { canvas: true, $: true },
rules: {
'import/no-unresolved': [
'error',
{
ignore: ['!!raw-loader.+.svg$'],
},
],
},
},
{
files: ['x-pack/plugins/canvas/public/**/*.js'],
env: {
browser: true,
},
},
{
files: ['packages/kbn-ui-shared-deps-src/src/flot_charts/**/*.js'],
env: {
jquery: true,
},
},
/**
* TSVB overrides
*/
{
files: ['src/plugins/vis_types/timeseries/**/*.{js,mjs,ts,tsx}'],
rules: {
'import/no-default-export': 'error',
},
},
/**
* Osquery overrides
*/
{
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
],
plugins: ['react', '@typescript-eslint'],
files: ['x-pack/plugins/osquery/**/*.{js,mjs,ts,tsx}'],
rules: {
'arrow-body-style': ['error', 'as-needed'],
'prefer-arrow-callback': 'error',
'no-unused-vars': 'off',
'react/prop-types': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
},
},
{
// typescript and javascript for front end react performance
files: ['x-pack/plugins/osquery/public/**/!(*.test).{js,mjs,ts,tsx}'],
plugins: ['react', 'react-perf'],
rules: {
'react-perf/jsx-no-new-object-as-prop': 'error',
'react-perf/jsx-no-new-array-as-prop': 'error',
'react-perf/jsx-no-new-function-as-prop': 'error',
'react/jsx-no-bind': 'error',
},
},
/**
* Prettier disables all conflicting rules, listing as last override so it takes precedence
*/
{
files: ['**/*'],
rules: {
...require('eslint-config-prettier').rules,
...require('eslint-config-prettier/react').rules,
...require('eslint-config-prettier/@typescript-eslint').rules,
},
},
/**
* Enterprise Search Prettier override
* Lints unnecessary backticks - @see https://github.com/prettier/eslint-config-prettier/blob/main/README.md#forbid-unnecessary-backticks
*/
{
files: ['x-pack/plugins/enterprise_search/**/*.{ts,tsx}'],
rules: {
quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: false }],
},
},
/**
* Platform Security Team overrides
*/
{
files: [
'src/plugins/security_oss/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/encrypted_saved_objects/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/security/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/spaces/**/*.{js,mjs,ts,tsx}',
],
rules: {
'@typescript-eslint/consistent-type-imports': 1,
'import/order': [
// This rule sorts import declarations
'error',
{
groups: [
'unknown',
['builtin', 'external'],
'internal',
['parent', 'sibling', 'index'],
],
pathGroups: [
{
pattern: '{**,.}/*.test.mocks',
group: 'unknown',
},
{
pattern: '{@kbn/**,src/**,kibana{,/**}}',
group: 'internal',
},
],
pathGroupsExcludedImportTypes: [],
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
'newlines-between': 'always',
},
],
'import/no-duplicates': ['error'],
'sort-imports': [
// This rule sorts imports of multiple members (destructured imports)
'error',
{
ignoreCase: true,
ignoreDeclarationSort: true,
},
],
},
},
/**
* Do not allow `any`
*/
{
files: [
'packages/kbn-analytics/**',
// 'packages/kbn-telemetry-tools/**',
'src/plugins/kibana_usage_collection/**',
'src/plugins/usage_collection/**',
'src/plugins/telemetry/**',
'src/plugins/telemetry_collection_manager/**',
'src/plugins/telemetry_management_section/**',
'x-pack/plugins/telemetry_collection_xpack/**',
],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
},
},
{
files: [
// core-team owned code
'src/core/**',
'x-pack/plugins/features/**',
'x-pack/plugins/licensing/**',
'x-pack/plugins/global_search/**',
'x-pack/plugins/cloud/**',
'packages/kbn-config-schema',
'src/plugins/status_page/**',
'src/plugins/saved_objects_management/**',
'packages/kbn-analytics/**',
'packages/kbn-telemetry-tools/**',
'src/plugins/kibana_usage_collection/**',
'src/plugins/usage_collection/**',
'src/plugins/telemetry/**',
'src/plugins/telemetry_collection_manager/**',
'src/plugins/telemetry_management_section/**',
'x-pack/plugins/telemetry_collection_xpack/**',
],
rules: {
'@typescript-eslint/prefer-ts-expect-error': 'error',
},
},
/**
* Disallow `export *` syntax in plugin/core public/server/common index files and instead
* require that plugins/core explicitly export the APIs that should be accessible outside the plugin.
*
* To add your plugin to this list just update the relevant glob with the name of your plugin
*/
{
files: [
'src/core/{server,public,common}/index.ts',
'src/plugins/*/{server,public,common}/index.ts',
'src/plugins/*/*/{server,public,common}/index.ts',
'x-pack/plugins/*/{server,public,common}/index.ts',
'x-pack/plugins/*/*/{server,public,common}/index.ts',
],
rules: {
'@kbn/eslint/no_export_all': 'error',
},
},
],
};