From 5e8cde7daefcbedc3ca70d68b55f32e216a0917f Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 22 Mar 2019 11:29:49 +0100 Subject: [PATCH 1/6] restrict import from core&plugin internals --- .eslintrc.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index 8bfa6c59596689..1f2da99d0ab470 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,10 @@ const ELASTIC_LICENSE_HEADER = ` */ `; +const corePublicPattern = './src/core/public/(?!utils|index).*'; +const coreServerPattern = './src/core/server/(?!index).*'; +const pluginsPublicPattern = './src/plugins/.*/public/(?!index).*'; +const pluginsServerPattern = './src/plugins/.*/server/(?!index).*'; module.exports = { extends: ['@elastic/eslint-config-kibana', '@elastic/eslint-config-kibana/jest'], plugins: ['@kbn/eslint-plugin-eslint'], @@ -47,6 +51,51 @@ module.exports = { rules: { 'no-restricted-imports': [2, restrictedModules], 'no-restricted-modules': [2, restrictedModules], + 'import/no-restricted-paths': [ + 2, + { + zones: [ + { + target: './src/legacy/.*js$', + from: [ + corePublicPattern, + coreServerPattern, + pluginsPublicPattern, + pluginsServerPattern, + ], + }, + { + target: './x-pack/(?!.*test).*js$', + from: [ + corePublicPattern, + coreServerPattern, + pluginsPublicPattern, + pluginsServerPattern, + ], + }, + { + target: './src/core/public/.*js$', + from: [coreServerPattern, pluginsPublicPattern, pluginsServerPattern], + }, + { + target: './src/core/server/.*js$', + from: [corePublicPattern, pluginsPublicPattern, pluginsServerPattern], + }, + { + target: './src/plugins/.*/public/.*js$', + from: [corePublicPattern, coreServerPattern, pluginsServerPattern], + }, + { + target: './src/plugins/.*/server/.*js$', + from: [corePublicPattern, coreServerPattern, pluginsPublicPattern], + }, + // the rule doesn't support 'from' as an array, so we flatten it + ].reduce( + (acc, zone) => acc.concat(zone.from.map(from => ({ target: zone.target, from }))), + [] + ), + }, + ], }, overrides: [ From 5e1510a51059cb54522bd6c19c6172c70cc897af Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Mon, 25 Mar 2019 11:10:28 +0100 Subject: [PATCH 2/6] Fork import/no-restricted-paths and add allowSameFolder option Our use case requires to restrict imports from plugin folders, which names are unknown for us yet. We cannot use 'import/no-restricted-paths' in the current state, because if we define 'from: plugins/*/server/' the rule will report all relative imports in the same folder as well. To fix this problem we added another option 'allowSameFolder' that makes the rule to ignore imports in the same folder. --- .eslintrc.js | 16 +- packages/kbn-eslint-plugin-eslint/index.js | 1 + .../kbn-eslint-plugin-eslint/package.json | 7 +- .../files/no_restricted_paths/client/a.js | 1 + .../files/no_restricted_paths/server/b.js | 1 + .../files/no_restricted_paths/server/c.js | 1 + .../no_restricted_paths/server/deep/d.js | 1 + .../rules/__tests__/no_restricted_paths.js | 228 ++++++++++++++++++ .../rules/no_restricted_paths.js | 149 ++++++++++++ .../plugin_pack/package_json_at_path.js | 1 + .../ui/public/test_harness/test_harness.js | 1 + yarn.lock | 16 ++ 12 files changed, 415 insertions(+), 8 deletions(-) create mode 100644 packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/client/a.js create mode 100644 packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/b.js create mode 100644 packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/c.js create mode 100644 packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/deep/d.js create mode 100644 packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js create mode 100644 packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js diff --git a/.eslintrc.js b/.eslintrc.js index 1f2da99d0ab470..4525b3d189bb25 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -51,11 +51,12 @@ module.exports = { rules: { 'no-restricted-imports': [2, restrictedModules], 'no-restricted-modules': [2, restrictedModules], - 'import/no-restricted-paths': [ - 2, + '@kbn/eslint/no-restricted-paths': [ + 'error', { zones: [ { + // when tslint is removed we will check *.ts files as well target: './src/legacy/.*js$', from: [ corePublicPattern, @@ -89,11 +90,12 @@ module.exports = { target: './src/plugins/.*/server/.*js$', from: [corePublicPattern, coreServerPattern, pluginsPublicPattern], }, - // the rule doesn't support 'from' as an array, so we flatten it - ].reduce( - (acc, zone) => acc.concat(zone.from.map(from => ({ target: zone.target, from }))), - [] - ), + { + target: './src/plugins/.*/(public|server)/.*js$', + from: [pluginsPublicPattern, pluginsServerPattern], + allowSameFolder: true, + }, + ], }, ], }, diff --git a/packages/kbn-eslint-plugin-eslint/index.js b/packages/kbn-eslint-plugin-eslint/index.js index 70b42d92d4b74c..d920685de2a619 100644 --- a/packages/kbn-eslint-plugin-eslint/index.js +++ b/packages/kbn-eslint-plugin-eslint/index.js @@ -22,5 +22,6 @@ module.exports = { 'require-license-header': require('./rules/require_license_header'), 'disallow-license-headers': require('./rules/disallow_license_headers'), 'no-default-export': require('./rules/no_default_export'), + 'no-restricted-paths': require('./rules/no_restricted_paths'), }, }; diff --git a/packages/kbn-eslint-plugin-eslint/package.json b/packages/kbn-eslint-plugin-eslint/package.json index b35387384071ed..51e774d0050153 100644 --- a/packages/kbn-eslint-plugin-eslint/package.json +++ b/packages/kbn-eslint-plugin-eslint/package.json @@ -7,7 +7,12 @@ "eslint": "^5.6.0", "babel-eslint": "^9.0.0" }, + "devDependencies": { + "@types/eslint": "^4.16.6" + }, "dependencies": { - "dedent": "^0.7.0" + "contains-path": "^0.1.0", + "dedent": "^0.7.0", + "eslint-module-utils": "^2.3.0" } } diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/client/a.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/client/a.js new file mode 100644 index 00000000000000..d15de7d98a9e0c --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/client/a.js @@ -0,0 +1 @@ +/* eslint-disable */ diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/b.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/b.js new file mode 100644 index 00000000000000..d15de7d98a9e0c --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/b.js @@ -0,0 +1 @@ +/* eslint-disable */ diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/c.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/c.js new file mode 100644 index 00000000000000..d15de7d98a9e0c --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/c.js @@ -0,0 +1 @@ +/* eslint-disable */ diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/deep/d.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/deep/d.js new file mode 100644 index 00000000000000..d15de7d98a9e0c --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/files/no_restricted_paths/server/deep/d.js @@ -0,0 +1 @@ +/* eslint-disable */ diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js new file mode 100644 index 00000000000000..b1d44e75cb1922 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js @@ -0,0 +1,228 @@ +/* eslint-disable @kbn/eslint/require-license-header */ +/* + * This product uses import/no-restricted-paths which is available under a + * "MIT" license. + * + * The MIT License (MIT) + * + * Copyright (c) 2015-present, Ben Mosher + * https://github.com/benmosher/eslint-plugin-import + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +const path = require('path'); +const { RuleTester } = require('eslint'); +const rule = require('../no_restricted_paths'); + +const ruleTester = new RuleTester({ + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module', + ecmaVersion: 2015, + }, +}); + +function resolve(relativePath) { + return path.join(__dirname, 'files', relativePath); +} + +ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { + valid: [ + { + code: 'import a from "../client/a.js"', + filename: resolve('./no_restricted_paths/server/b.js'), + options: [ + { + zones: [ + { + target: resolve('./no_restricted_paths/server'), + from: resolve('./no_restricted_paths/other'), + }, + ], + }, + ], + }, + { + code: 'const a = require("../client/a.js")', + filename: resolve('./no_restricted_paths/server/b.js'), + options: [ + { + zones: [ + { + target: './no_restricted_paths/server', + from: './no_restricted_paths/other', + }, + ], + }, + ], + }, + { + code: 'import b from "../server/b.js"', + filename: resolve('./no_restricted_paths/client/a.js'), + options: [ + { + zones: [ + { + target: './no_restricted_paths/client', + from: './no_restricted_paths/other', + }, + ], + }, + ], + }, + + // irrelevant function calls + { code: 'notrequire("../server/b.js")' }, + { + code: 'notrequire("../server/b.js")', + filename: resolve('./no_restricted_paths/client/a.js'), + options: [ + { + zones: [ + { + target: './no_restricted_paths/client', + from: './no_restricted_paths/server', + }, + ], + }, + ], + }, + + // no config + { code: 'require("../server/b.js")' }, + { code: 'import b from "../server/b.js"' }, + + // builtin (ignore) + { code: 'require("os")' }, + + { + code: 'const d = require("./deep/d.js")', + filename: resolve('./no_restricted_paths/server/b.js'), + options: [ + { + zones: [ + { + allowSameFolder: true, + target: resolve('./no_restricted_paths/'), + from: resolve('./no_restricted_paths/'), + }, + ], + }, + ], + }, + ], + + invalid: [ + { + code: 'import b from "../server/b.js"', + filename: resolve('./no_restricted_paths/client/a.js'), + options: [ + { + zones: [ + { + target: resolve('./no_restricted_paths/client'), + from: resolve('./no_restricted_paths/server'), + }, + ], + }, + ], + errors: [ + { + message: 'Unexpected path "../server/b.js" imported in restricted zone.', + line: 1, + column: 15, + }, + ], + }, + { + code: 'import a from "../client/a"\nimport c from "./c"', + filename: resolve('./no_restricted_paths/server/b.js'), + options: [ + { + zones: [ + { + target: resolve('./no_restricted_paths/server'), + from: resolve('./no_restricted_paths/client'), + }, + { + target: resolve('./no_restricted_paths/server'), + from: resolve('./no_restricted_paths/server/c.js'), + }, + ], + }, + ], + errors: [ + { + message: 'Unexpected path "../client/a" imported in restricted zone.', + line: 1, + column: 15, + }, + { + message: 'Unexpected path "./c" imported in restricted zone.', + line: 2, + column: 15, + }, + ], + }, + { + code: 'const b = require("../server/b.js")', + filename: resolve('./no_restricted_paths/client/a.js'), + options: [ + { + zones: [ + { + target: resolve('./no_restricted_paths/client'), + from: resolve('./no_restricted_paths/server'), + }, + ], + }, + ], + errors: [ + { + message: 'Unexpected path "../server/b.js" imported in restricted zone.', + line: 1, + column: 19, + }, + ], + }, + + { + code: 'const d = require("./deep/d.js")', + filename: resolve('./no_restricted_paths/server/b.js'), + options: [ + { + zones: [ + { + target: resolve('./no_restricted_paths/'), + from: resolve('./no_restricted_paths/'), + }, + ], + }, + ], + errors: [ + { + message: 'Unexpected path "./deep/d.js" imported in restricted zone.', + line: 1, + column: 19, + }, + ], + }, + ], +}); diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js new file mode 100644 index 00000000000000..a88aa08e9c0b55 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js @@ -0,0 +1,149 @@ +/* eslint-disable @kbn/eslint/require-license-header */ +/* @notice + * This product uses import/no-restricted-paths which is available under a + * "MIT" license. + * + * The MIT License (MIT) + * + * Copyright (c) 2015-present, Ben Mosher + * https://github.com/benmosher/eslint-plugin-import + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +const path = require('path'); +const resolve = require('eslint-module-utils/resolve').default; +const containsPath = require('contains-path'); + +function toArray(value) { + return Array.isArray(value) ? value : [value]; +} + +function isStaticRequire(node) { + return ( + node && + node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && + node.arguments.length === 1 && + node.arguments[0].type === 'Literal' && + typeof node.arguments[0].value === 'string' + ); +} + +function traverseToTopFolder(src, pattern) { + while (containsPath(src, pattern)) { + const srcIdx = src.lastIndexOf(path.sep); + src = src.slice(0, srcIdx); + } + return src; +} + +function isSameFolderOrDescendent(src, imported, pattern) { + const srcFileFolderRoot = traverseToTopFolder(src, pattern); + const importedFileFolderRoot = traverseToTopFolder(imported, pattern); + return srcFileFolderRoot === importedFileFolderRoot; +} + +module.exports = { + /** + *@type {import('eslint').Rule} + */ + meta: { + schema: [ + { + type: 'object', + properties: { + zones: { + type: 'array', + minItems: 1, + items: { + type: 'object', + properties: { + target: { type: 'string' }, + from: { + anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], + }, + allowSameFolder: { type: 'boolean' }, + }, + additionalProperties: false, + }, + }, + }, + additionalProperties: false, + }, + ], + }, + + /** + *@param {import('eslint').Rule.RuleContext} context runtime context + *@returns {import('eslint').Rule.RuleListener} + */ + create(context) { + const options = context.options[0] || {}; + const zones = options.zones || []; + const basePath = process.cwd(); + const currentFilename = context.getFilename(); + const matchingZones = zones.filter(zone => { + const targetPath = path.resolve(basePath, zone.target); + + return containsPath(currentFilename, targetPath); + }); + + function checkForRestrictedImportPath(importPath, node) { + const absoluteImportPath = resolve(importPath, context); + + if (!absoluteImportPath) return; + for (const zone of matchingZones) { + for (const from of toArray(zone.from)) { + const absoluteFrom = path.resolve(basePath, from); + + if (!containsPath(absoluteImportPath, absoluteFrom)) continue; + if ( + zone.allowSameFolder && + isSameFolderOrDescendent( + resolve(currentFilename, context), + absoluteImportPath, + absoluteFrom + ) + ) { + continue; + } + + context.report({ + node, + message: `Unexpected path "${importPath}" imported in restricted zone.`, + }); + } + } + } + + return { + ImportDeclaration(node) { + checkForRestrictedImportPath(node.source.value, node.source); + }, + CallExpression(node) { + if (isStaticRequire(node)) { + const [firstArgument] = node.arguments; + + checkForRestrictedImportPath(firstArgument.value, firstArgument); + } + }, + }; + }, +}; diff --git a/src/legacy/plugin_discovery/plugin_pack/package_json_at_path.js b/src/legacy/plugin_discovery/plugin_pack/package_json_at_path.js index 1d669b3bcb6642..24e442744dfd7f 100644 --- a/src/legacy/plugin_discovery/plugin_pack/package_json_at_path.js +++ b/src/legacy/plugin_discovery/plugin_pack/package_json_at_path.js @@ -22,6 +22,7 @@ import * as Rx from 'rxjs'; import { map, mergeMap, catchError } from 'rxjs/operators'; import { resolve } from 'path'; import { createInvalidPackError } from '../errors'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { isNewPlatformPlugin } from '../../../core/server/plugins'; import { isDirectory } from './lib'; diff --git a/src/legacy/ui/public/test_harness/test_harness.js b/src/legacy/ui/public/test_harness/test_harness.js index 36dc38b1c3b825..f1dfd533a2dc1e 100644 --- a/src/legacy/ui/public/test_harness/test_harness.js +++ b/src/legacy/ui/public/test_harness/test_harness.js @@ -26,6 +26,7 @@ import { parse as parseUrl } from 'url'; import sinon from 'sinon'; import { Notifier } from '../notify'; import { metadata } from '../metadata'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { UiSettingsClient } from '../../../../core/public/ui_settings'; import './test_harness.css'; diff --git a/yarn.lock b/yarn.lock index 02a508c6145016..cedfb53b660053 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1798,6 +1798,14 @@ "@types/estree" "*" "@types/json-schema" "*" +"@types/eslint@^4.16.6": + version "4.16.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-4.16.6.tgz#96d4ecddbea618ab0b55eaf0dffedf387129b06c" + integrity sha512-GL7tGJig55FeclpOytU7nCCqtR143jBoC7AUdH0DO9xBSIFiNNUFCY/S3KNWsHeQJuU3hjw/OC1+kRTFNXqUZQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + "@types/estree@*": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -9183,6 +9191,14 @@ eslint-module-utils@^2.2.0: debug "^2.6.8" pkg-dir "^1.0.0" +eslint-module-utils@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz#546178dab5e046c8b562bbb50705e2456d7bda49" + integrity sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w== + dependencies: + debug "^2.6.8" + pkg-dir "^2.0.0" + eslint-plugin-babel@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.2.0.tgz#3041a0c26aa3ca4a0e0f2aa11591f0396790d981" From f0a89b683fb6840ae784ffa78364c39e757f7c99 Mon Sep 17 00:00:00 2001 From: restrry Date: Mon, 25 Mar 2019 12:26:22 +0100 Subject: [PATCH 3/6] update notices --- NOTICE.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/NOTICE.txt b/NOTICE.txt index 216591e2b4150d..1a783d9a95c7ca 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -184,3 +184,30 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--- +This product uses import/no-restricted-paths which is available under a +"MIT" license. + +The MIT License (MIT) + +Copyright (c) 2015-present, Ben Mosher +https://github.com/benmosher/eslint-plugin-import + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + From 8c0d2b533bd2dc2a051e4c2ecbf7a5d63efa708b Mon Sep 17 00:00:00 2001 From: restrry Date: Tue, 26 Mar 2019 16:18:29 +0100 Subject: [PATCH 4/6] add basePath option --- .../rules/__tests__/no_restricted_paths.js | 19 ++++++++++++++++++- .../rules/no_restricted_paths.js | 5 +++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js index b1d44e75cb1922..45385d6b5e2a6d 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js @@ -1,4 +1,4 @@ -/* eslint-disable @kbn/eslint/require-license-header */ +/* eslint-disable-line @kbn/eslint/require-license-header */ /* * This product uses import/no-restricted-paths which is available under a * "MIT" license. @@ -202,6 +202,23 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, ], }, + { + code: 'const b = require("../server/b.js")', + filename: resolve('./no_restricted_paths/client/a.js'), + options: [ + { + zones: [{ target: './client', from: './server' }], + basePath: resolve('./no_restricted_paths'), + }, + ], + errors: [ + { + message: 'Unexpected path "../server/b.js" imported in restricted zone.', + line: 1, + column: 19, + }, + ], + }, { code: 'const d = require("./deep/d.js")', diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js index a88aa08e9c0b55..f34758b28f28fc 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js @@ -1,4 +1,4 @@ -/* eslint-disable @kbn/eslint/require-license-header */ +/* eslint-disable-line @kbn/eslint/require-license-header */ /* @notice * This product uses import/no-restricted-paths which is available under a * "MIT" license. @@ -84,6 +84,7 @@ module.exports = { additionalProperties: false, }, }, + basePath: { type: 'string' }, }, additionalProperties: false, }, @@ -97,7 +98,7 @@ module.exports = { create(context) { const options = context.options[0] || {}; const zones = options.zones || []; - const basePath = process.cwd(); + const basePath = options.basePath || process.cwd(); const currentFilename = context.getFilename(); const matchingZones = zones.filter(zone => { const targetPath = path.resolve(basePath, zone.target); From cdaf22e787e45d3ba70f85fccadd134e6c62edc4 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Tue, 26 Mar 2019 20:42:29 +0100 Subject: [PATCH 5/6] support glob pattern instead of reagexp --- .eslintrc.js | 56 +++++--------- .../kbn-eslint-plugin-eslint/package.json | 5 +- .../rules/__tests__/no_restricted_paths.js | 75 +++++++++++-------- .../rules/no_restricted_paths.js | 68 +++++++---------- x-pack/test/api_integration/services/es.js | 1 + yarn.lock | 46 +++++------- 6 files changed, 108 insertions(+), 143 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4525b3d189bb25..df7428e1bdb538 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,10 +32,6 @@ const ELASTIC_LICENSE_HEADER = ` */ `; -const corePublicPattern = './src/core/public/(?!utils|index).*'; -const coreServerPattern = './src/core/server/(?!index).*'; -const pluginsPublicPattern = './src/plugins/.*/public/(?!index).*'; -const pluginsServerPattern = './src/plugins/.*/server/(?!index).*'; module.exports = { extends: ['@elastic/eslint-config-kibana', '@elastic/eslint-config-kibana/jest'], plugins: ['@kbn/eslint-plugin-eslint'], @@ -56,43 +52,27 @@ module.exports = { { zones: [ { - // when tslint is removed we will check *.ts files as well - target: './src/legacy/.*js$', - from: [ - corePublicPattern, - coreServerPattern, - pluginsPublicPattern, - pluginsServerPattern, + target: [ + 'src/legacy/**/*', + 'x-pack/**/*', + '!x-pack/**/*.test.*', + 'src/plugins/**/(public|server)/**/*', + 'src/core/(public|server)/**/*', ], - }, - { - target: './x-pack/(?!.*test).*js$', from: [ - corePublicPattern, - coreServerPattern, - pluginsPublicPattern, - pluginsServerPattern, + 'src/core/public/**/*', + '!src/core/public/index*', + '!src/core/public/utils/**/*', + + 'src/core/server/**/*', + '!src/core/server/index*', + + 'src/plugins/**/public/**/*', + '!src/plugins/**/public/index*', + + 'src/plugins/**/server/**/*', + '!src/plugins/**/server/index*', ], - }, - { - target: './src/core/public/.*js$', - from: [coreServerPattern, pluginsPublicPattern, pluginsServerPattern], - }, - { - target: './src/core/server/.*js$', - from: [corePublicPattern, pluginsPublicPattern, pluginsServerPattern], - }, - { - target: './src/plugins/.*/public/.*js$', - from: [corePublicPattern, coreServerPattern, pluginsServerPattern], - }, - { - target: './src/plugins/.*/server/.*js$', - from: [corePublicPattern, coreServerPattern, pluginsPublicPattern], - }, - { - target: './src/plugins/.*/(public|server)/.*js$', - from: [pluginsPublicPattern, pluginsServerPattern], allowSameFolder: true, }, ], diff --git a/packages/kbn-eslint-plugin-eslint/package.json b/packages/kbn-eslint-plugin-eslint/package.json index 51e774d0050153..6bfc94ba4b41a8 100644 --- a/packages/kbn-eslint-plugin-eslint/package.json +++ b/packages/kbn-eslint-plugin-eslint/package.json @@ -7,11 +7,8 @@ "eslint": "^5.6.0", "babel-eslint": "^9.0.0" }, - "devDependencies": { - "@types/eslint": "^4.16.6" - }, "dependencies": { - "contains-path": "^0.1.0", + "micromatch": "3.1.10", "dedent": "^0.7.0", "eslint-module-utils": "^2.3.0" } diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js index 45385d6b5e2a6d..b7b7be86885d4a 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js @@ -39,21 +39,25 @@ const ruleTester = new RuleTester({ }, }); -function resolve(relativePath) { +function resolveAbsolute(relativePath) { return path.join(__dirname, 'files', relativePath); } +function resolveRelative(relativePath) { + return path.join(path.relative(process.cwd(), __dirname), 'files', relativePath); +} + ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { valid: [ { code: 'import a from "../client/a.js"', - filename: resolve('./no_restricted_paths/server/b.js'), + filename: resolveAbsolute('./no_restricted_paths/server/b.js'), options: [ { zones: [ { - target: resolve('./no_restricted_paths/server'), - from: resolve('./no_restricted_paths/other'), + target: '**/no_restricted_paths/server/**/*', + from: '**/no_restricted_paths/other/**/*', }, ], }, @@ -61,13 +65,13 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'const a = require("../client/a.js")', - filename: resolve('./no_restricted_paths/server/b.js'), + filename: resolveAbsolute('./no_restricted_paths/server/b.js'), options: [ { zones: [ { - target: './no_restricted_paths/server', - from: './no_restricted_paths/other', + target: resolveRelative('./no_restricted_paths/server/**/*'), + from: resolveRelative('./no_restricted_paths/other/**/*'), }, ], }, @@ -75,13 +79,13 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'import b from "../server/b.js"', - filename: resolve('./no_restricted_paths/client/a.js'), + filename: resolveAbsolute('./no_restricted_paths/client/a.js'), options: [ { zones: [ { - target: './no_restricted_paths/client', - from: './no_restricted_paths/other', + target: '**/no_restricted_paths/client/**/*', + from: '**/no_restricted_paths/other/**/*', }, ], }, @@ -92,13 +96,13 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { { code: 'notrequire("../server/b.js")' }, { code: 'notrequire("../server/b.js")', - filename: resolve('./no_restricted_paths/client/a.js'), + filename: resolveAbsolute('./no_restricted_paths/client/a.js'), options: [ { zones: [ { - target: './no_restricted_paths/client', - from: './no_restricted_paths/server', + target: '**/no_restricted_paths/client/**/*', + from: '**/no_restricted_paths/server/**/*', }, ], }, @@ -114,14 +118,14 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { { code: 'const d = require("./deep/d.js")', - filename: resolve('./no_restricted_paths/server/b.js'), + filename: resolveAbsolute('./no_restricted_paths/server/b.js'), options: [ { zones: [ { allowSameFolder: true, - target: resolve('./no_restricted_paths/'), - from: resolve('./no_restricted_paths/'), + target: '**/no_restricted_paths/**/*', + from: '**/no_restricted_paths/**/*', }, ], }, @@ -132,13 +136,13 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { invalid: [ { code: 'import b from "../server/b.js"', - filename: resolve('./no_restricted_paths/client/a.js'), + filename: resolveAbsolute('./no_restricted_paths/client/a.js'), options: [ { zones: [ { - target: resolve('./no_restricted_paths/client'), - from: resolve('./no_restricted_paths/server'), + target: '**/no_restricted_paths/client/**/*', + from: '**/no_restricted_paths/server/**/*', }, ], }, @@ -153,17 +157,17 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'import a from "../client/a"\nimport c from "./c"', - filename: resolve('./no_restricted_paths/server/b.js'), + filename: resolveAbsolute('./no_restricted_paths/server/b.js'), options: [ { zones: [ { - target: resolve('./no_restricted_paths/server'), - from: resolve('./no_restricted_paths/client'), + target: '**/no_restricted_paths/server/**/*', + from: '**/no_restricted_paths/client/**/*', }, { - target: resolve('./no_restricted_paths/server'), - from: resolve('./no_restricted_paths/server/c.js'), + target: '**/no_restricted_paths/server/**/*', + from: '**/no_restricted_paths/server/c.js', }, ], }, @@ -183,13 +187,13 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'const b = require("../server/b.js")', - filename: resolve('./no_restricted_paths/client/a.js'), + filename: resolveAbsolute('./no_restricted_paths/client/a.js'), options: [ { zones: [ { - target: resolve('./no_restricted_paths/client'), - from: resolve('./no_restricted_paths/server'), + target: '**/no_restricted_paths/client/**/*', + from: '**/no_restricted_paths/server/**/*', }, ], }, @@ -204,11 +208,16 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'const b = require("../server/b.js")', - filename: resolve('./no_restricted_paths/client/a.js'), + filename: resolveAbsolute('./no_restricted_paths/client/a.js'), options: [ { - zones: [{ target: './client', from: './server' }], - basePath: resolve('./no_restricted_paths'), + zones: [ + { + target: 'client/**/*', + from: 'server/**/*', + }, + ], + basePath: resolveRelative('./no_restricted_paths'), }, ], errors: [ @@ -222,13 +231,13 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { { code: 'const d = require("./deep/d.js")', - filename: resolve('./no_restricted_paths/server/b.js'), + filename: resolveAbsolute('./no_restricted_paths/server/b.js'), options: [ { zones: [ { - target: resolve('./no_restricted_paths/'), - from: resolve('./no_restricted_paths/'), + target: '**/no_restricted_paths/**/*', + from: '**/no_restricted_paths/**/*', }, ], }, diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js index f34758b28f28fc..cb6e0e9f2ac68f 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js @@ -28,11 +28,7 @@ */ const path = require('path'); const resolve = require('eslint-module-utils/resolve').default; -const containsPath = require('contains-path'); - -function toArray(value) { - return Array.isArray(value) ? value : [value]; -} +const mm = require('micromatch'); function isStaticRequire(node) { return ( @@ -46,8 +42,12 @@ function isStaticRequire(node) { ); } +function resolveBasePath(basePath) { + return path.isAbsolute(basePath) ? basePath : path.relative(process.cwd(), basePath); +} + function traverseToTopFolder(src, pattern) { - while (containsPath(src, pattern)) { + while (mm([src], pattern).length > 0) { const srcIdx = src.lastIndexOf(path.sep); src = src.slice(0, srcIdx); } @@ -61,9 +61,6 @@ function isSameFolderOrDescendent(src, imported, pattern) { } module.exports = { - /** - *@type {import('eslint').Rule} - */ meta: { schema: [ { @@ -75,7 +72,9 @@ module.exports = { items: { type: 'object', properties: { - target: { type: 'string' }, + target: { + anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], + }, from: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], }, @@ -91,46 +90,33 @@ module.exports = { ], }, - /** - *@param {import('eslint').Rule.RuleContext} context runtime context - *@returns {import('eslint').Rule.RuleListener} - */ create(context) { const options = context.options[0] || {}; const zones = options.zones || []; - const basePath = options.basePath || process.cwd(); - const currentFilename = context.getFilename(); - const matchingZones = zones.filter(zone => { - const targetPath = path.resolve(basePath, zone.target); - - return containsPath(currentFilename, targetPath); - }); + const basePath = options.basePath ? resolveBasePath(options.basePath) : process.cwd(); function checkForRestrictedImportPath(importPath, node) { const absoluteImportPath = resolve(importPath, context); - if (!absoluteImportPath) return; - for (const zone of matchingZones) { - for (const from of toArray(zone.from)) { - const absoluteFrom = path.resolve(basePath, from); - if (!containsPath(absoluteImportPath, absoluteFrom)) continue; - if ( - zone.allowSameFolder && - isSameFolderOrDescendent( - resolve(currentFilename, context), - absoluteImportPath, - absoluteFrom - ) - ) { - continue; - } + const currentFilename = context.getFilename(); + for (const { target, from, allowSameFolder } of zones) { + const srcFilePath = resolve(currentFilename, context); - context.report({ - node, - message: `Unexpected path "${importPath}" imported in restricted zone.`, - }); - } + const relativeSrcFile = path.relative(basePath, srcFilePath); + const relativeImportFile = path.relative(basePath, absoluteImportPath); + + if ( + !mm([relativeSrcFile], target).length || + !mm([relativeImportFile], from).length || + (allowSameFolder && isSameFolderOrDescendent(relativeSrcFile, relativeImportFile, from)) + ) + continue; + + context.report({ + node, + message: `Unexpected path "${importPath}" imported in restricted zone.`, + }); } } diff --git a/x-pack/test/api_integration/services/es.js b/x-pack/test/api_integration/services/es.js index 7efbf3a6e83043..3d6e5b0cee1a12 100644 --- a/x-pack/test/api_integration/services/es.js +++ b/x-pack/test/api_integration/services/es.js @@ -8,6 +8,7 @@ import { format as formatUrl } from 'url'; import elasticsearch from 'elasticsearch'; import shieldPlugin from '../../../server/lib/esjs_shield_plugin'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { DEFAULT_API_VERSION } from '../../../../src/core/server/elasticsearch/elasticsearch_config'; export function EsProvider({ getService }) { diff --git a/yarn.lock b/yarn.lock index cedfb53b660053..496538577e9c7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1798,14 +1798,6 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/eslint@^4.16.6": - version "4.16.6" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-4.16.6.tgz#96d4ecddbea618ab0b55eaf0dffedf387129b06c" - integrity sha512-GL7tGJig55FeclpOytU7nCCqtR143jBoC7AUdH0DO9xBSIFiNNUFCY/S3KNWsHeQJuU3hjw/OC1+kRTFNXqUZQ== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - "@types/estree@*": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -16010,6 +16002,25 @@ micro@9.1.0: mri "1.1.0" raw-body "2.3.2" +micromatch@3.1.10, micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + micromatch@3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.5.tgz#d05e168c206472dfbca985bfef4f57797b4cd4ba" @@ -16048,25 +16059,6 @@ micromatch@^2.1.5, micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" From 253a96fda1c73d5e423cb2cdba60f65a0007411d Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Wed, 27 Mar 2019 10:12:11 +0100 Subject: [PATCH 6/6] remove @notice, make basePath required --- .eslintrc.js | 1 + NOTICE.txt | 27 ----- .../rules/__tests__/no_restricted_paths.js | 107 +++++++++++------- .../rules/no_restricted_paths.js | 11 +- 4 files changed, 74 insertions(+), 72 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index ffb6725521eb3b..b49e073e1fd2de 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -50,6 +50,7 @@ module.exports = { '@kbn/eslint/no-restricted-paths': [ 'error', { + basePath: __dirname, zones: [ { target: [ diff --git a/NOTICE.txt b/NOTICE.txt index 1a783d9a95c7ca..216591e2b4150d 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -184,30 +184,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- -This product uses import/no-restricted-paths which is available under a -"MIT" license. - -The MIT License (MIT) - -Copyright (c) 2015-present, Ben Mosher -https://github.com/benmosher/eslint-plugin-import - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js index b7b7be86885d4a..28ea3d4c055fe5 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/__tests__/no_restricted_paths.js @@ -39,25 +39,18 @@ const ruleTester = new RuleTester({ }, }); -function resolveAbsolute(relativePath) { - return path.join(__dirname, 'files', relativePath); -} - -function resolveRelative(relativePath) { - return path.join(path.relative(process.cwd(), __dirname), 'files', relativePath); -} - ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { valid: [ { code: 'import a from "../client/a.js"', - filename: resolveAbsolute('./no_restricted_paths/server/b.js'), + filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'), options: [ { + basePath: __dirname, zones: [ { - target: '**/no_restricted_paths/server/**/*', - from: '**/no_restricted_paths/other/**/*', + target: 'files/no_restricted_paths/server/**/*', + from: 'files/no_restricted_paths/other/**/*', }, ], }, @@ -65,13 +58,14 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'const a = require("../client/a.js")', - filename: resolveAbsolute('./no_restricted_paths/server/b.js'), + filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'), options: [ { + basePath: __dirname, zones: [ { - target: resolveRelative('./no_restricted_paths/server/**/*'), - from: resolveRelative('./no_restricted_paths/other/**/*'), + target: 'files/no_restricted_paths/server/**/*', + from: 'files/no_restricted_paths/other/**/*', }, ], }, @@ -79,9 +73,10 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'import b from "../server/b.js"', - filename: resolveAbsolute('./no_restricted_paths/client/a.js'), + filename: path.join(__dirname, './files/no_restricted_paths/client/a.js'), options: [ { + basePath: __dirname, zones: [ { target: '**/no_restricted_paths/client/**/*', @@ -93,16 +88,24 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, // irrelevant function calls - { code: 'notrequire("../server/b.js")' }, { code: 'notrequire("../server/b.js")', - filename: resolveAbsolute('./no_restricted_paths/client/a.js'), options: [ { + basePath: __dirname, + }, + ], + }, + { + code: 'notrequire("../server/b.js")', + filename: path.join(__dirname, './files/no_restricted_paths/client/a.js'), + options: [ + { + basePath: __dirname, zones: [ { - target: '**/no_restricted_paths/client/**/*', - from: '**/no_restricted_paths/server/**/*', + target: 'files/no_restricted_paths/client/**/*', + from: 'files/no_restricted_paths/server/**/*', }, ], }, @@ -110,22 +113,44 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, // no config - { code: 'require("../server/b.js")' }, - { code: 'import b from "../server/b.js"' }, + { + code: 'require("../server/b.js")', + options: [ + { + basePath: __dirname, + }, + ], + }, + { + code: 'import b from "../server/b.js"', + options: [ + { + basePath: __dirname, + }, + ], + }, // builtin (ignore) - { code: 'require("os")' }, + { + code: 'require("os")', + options: [ + { + basePath: __dirname, + }, + ], + }, { code: 'const d = require("./deep/d.js")', - filename: resolveAbsolute('./no_restricted_paths/server/b.js'), + filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'), options: [ { + basePath: __dirname, zones: [ { allowSameFolder: true, - target: '**/no_restricted_paths/**/*', - from: '**/no_restricted_paths/**/*', + target: 'files/no_restricted_paths/**/*', + from: 'files/no_restricted_paths/**/*', }, ], }, @@ -136,13 +161,14 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { invalid: [ { code: 'import b from "../server/b.js"', - filename: resolveAbsolute('./no_restricted_paths/client/a.js'), + filename: path.join(__dirname, './files/no_restricted_paths/client/a.js'), options: [ { + basePath: __dirname, zones: [ { - target: '**/no_restricted_paths/client/**/*', - from: '**/no_restricted_paths/server/**/*', + target: 'files/no_restricted_paths/client/**/*', + from: 'files/no_restricted_paths/server/**/*', }, ], }, @@ -157,17 +183,18 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'import a from "../client/a"\nimport c from "./c"', - filename: resolveAbsolute('./no_restricted_paths/server/b.js'), + filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'), options: [ { + basePath: __dirname, zones: [ { - target: '**/no_restricted_paths/server/**/*', - from: '**/no_restricted_paths/client/**/*', + target: 'files/no_restricted_paths/server/**/*', + from: 'files/no_restricted_paths/client/**/*', }, { - target: '**/no_restricted_paths/server/**/*', - from: '**/no_restricted_paths/server/c.js', + target: 'files/no_restricted_paths/server/**/*', + from: 'files/no_restricted_paths/server/c.js', }, ], }, @@ -187,9 +214,10 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'const b = require("../server/b.js")', - filename: resolveAbsolute('./no_restricted_paths/client/a.js'), + filename: path.join(__dirname, './files/no_restricted_paths/client/a.js'), options: [ { + basePath: __dirname, zones: [ { target: '**/no_restricted_paths/client/**/*', @@ -208,16 +236,16 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { }, { code: 'const b = require("../server/b.js")', - filename: resolveAbsolute('./no_restricted_paths/client/a.js'), + filename: path.join(__dirname, './files/no_restricted_paths/client/a.js'), options: [ { + basePath: path.join(__dirname, 'files', 'no_restricted_paths'), zones: [ { target: 'client/**/*', from: 'server/**/*', }, ], - basePath: resolveRelative('./no_restricted_paths'), }, ], errors: [ @@ -231,13 +259,14 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, { { code: 'const d = require("./deep/d.js")', - filename: resolveAbsolute('./no_restricted_paths/server/b.js'), + filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'), options: [ { + basePath: __dirname, zones: [ { - target: '**/no_restricted_paths/**/*', - from: '**/no_restricted_paths/**/*', + target: 'files/no_restricted_paths/**/*', + from: 'files/no_restricted_paths/**/*', }, ], }, diff --git a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js index cb6e0e9f2ac68f..b9cad314a0dc71 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js +++ b/packages/kbn-eslint-plugin-eslint/rules/no_restricted_paths.js @@ -1,5 +1,5 @@ /* eslint-disable-line @kbn/eslint/require-license-header */ -/* @notice +/* * This product uses import/no-restricted-paths which is available under a * "MIT" license. * @@ -42,10 +42,6 @@ function isStaticRequire(node) { ); } -function resolveBasePath(basePath) { - return path.isAbsolute(basePath) ? basePath : path.relative(process.cwd(), basePath); -} - function traverseToTopFolder(src, pattern) { while (mm([src], pattern).length > 0) { const srcIdx = src.lastIndexOf(path.sep); @@ -93,7 +89,10 @@ module.exports = { create(context) { const options = context.options[0] || {}; const zones = options.zones || []; - const basePath = options.basePath ? resolveBasePath(options.basePath) : process.cwd(); + const basePath = options.basePath; + if (!basePath || !path.isAbsolute(basePath)) { + throw new Error('basePath option must be specified and must be absolute'); + } function checkForRestrictedImportPath(importPath, node) { const absoluteImportPath = resolve(importPath, context);