summaryrefslogtreecommitdiff
path: root/tools/eslint-rules
diff options
context:
space:
mode:
Diffstat (limited to 'tools/eslint-rules')
-rw-r--r--tools/eslint-rules/crypto-check.js83
-rw-r--r--tools/eslint-rules/rules-utils.js61
2 files changed, 144 insertions, 0 deletions
diff --git a/tools/eslint-rules/crypto-check.js b/tools/eslint-rules/crypto-check.js
new file mode 100644
index 0000000000..b1b2a03f50
--- /dev/null
+++ b/tools/eslint-rules/crypto-check.js
@@ -0,0 +1,83 @@
+/**
+ * @fileoverview Check that common.hasCrypto is used if crypto, tls,
+ * https, or http2 modules are required.
+ *
+ * This rule can be ignored using // eslint-disable-line crypto-check
+ *
+ * @author Daniel Bevenius <daniel.bevenius@gmail.com>
+ */
+'use strict';
+
+const utils = require('./rules-utils.js');
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+const msg = 'Please add a hasCrypto check to allow this test to be skipped ' +
+ 'when Node is built "--without-ssl".';
+
+module.exports = function(context) {
+ const missingCheckNodes = [];
+ const requireNodes = [];
+ var hasSkipCall = false;
+
+ function testCryptoUsage(node) {
+ if (utils.isRequired(node, ['crypto', 'tls', 'https', 'http2'])) {
+ requireNodes.push(node);
+ }
+ }
+
+ function testIfStatement(node) {
+ if (node.test.argument === undefined) {
+ return;
+ }
+ if (isCryptoCheck(node.test.argument)) {
+ checkCryptoCall(node);
+ }
+ }
+
+ function isCryptoCheck(node) {
+ return utils.usesCommonProperty(node, ['hasCrypto', 'hasFipsCrypto']);
+ }
+
+ function checkCryptoCall(node) {
+ if (utils.inSkipBlock(node)) {
+ hasSkipCall = true;
+ } else {
+ missingCheckNodes.push(node);
+ }
+ }
+
+ function testMemberExpression(node) {
+ if (isCryptoCheck(node)) {
+ checkCryptoCall(node);
+ }
+ }
+
+ function reportIfMissingCheck(node) {
+ if (hasSkipCall) {
+ return;
+ }
+
+ if (requireNodes.length > 0) {
+ if (missingCheckNodes.length > 0) {
+ report(missingCheckNodes);
+ } else {
+ report(requireNodes);
+ }
+ }
+ }
+
+ function report(nodes) {
+ nodes.forEach((node) => {
+ context.report(node, msg);
+ });
+ }
+
+ return {
+ 'CallExpression': (node) => testCryptoUsage(node),
+ 'IfStatement:exit': (node) => testIfStatement(node),
+ 'MemberExpression:exit': (node) => testMemberExpression(node),
+ 'Program:exit': (node) => reportIfMissingCheck(node)
+ };
+};
diff --git a/tools/eslint-rules/rules-utils.js b/tools/eslint-rules/rules-utils.js
new file mode 100644
index 0000000000..e3e5e6e5ef
--- /dev/null
+++ b/tools/eslint-rules/rules-utils.js
@@ -0,0 +1,61 @@
+/**
+ * Utility functions common to ESLint rules.
+ */
+'use strict';
+
+/**
+ * Returns true if any of the passed in modules are used in
+ * require calls.
+ */
+module.exports.isRequired = function(node, modules) {
+ return node.callee.name === 'require' &&
+ modules.includes(node.arguments[0].value);
+};
+
+/**
+ * Returns true is the node accesses any property in the properties
+ * array on the 'common' object.
+ */
+module.exports.usesCommonProperty = function(node, properties) {
+ if (node.name) {
+ return properties.includes(node.name);
+ }
+ if (node.property) {
+ return properties.includes(node.property.name);
+ }
+ return false;
+};
+
+/**
+ * Returns true if the passed in node is inside an if statement block,
+ * and the block also has a call to skip.
+ */
+module.exports.inSkipBlock = function(node) {
+ var hasSkipBlock = false;
+ if (node.test &&
+ node.test.type === 'UnaryExpression' &&
+ node.test.operator === '!') {
+ const consequent = node.consequent;
+ if (consequent.body) {
+ consequent.body.some(function(expressionStatement) {
+ if (hasSkip(expressionStatement.expression)) {
+ return hasSkipBlock = true;
+ }
+ return false;
+ });
+ } else {
+ if (hasSkip(consequent.expression)) {
+ hasSkipBlock = true;
+ }
+ }
+ }
+ return hasSkipBlock;
+};
+
+function hasSkip(expression) {
+ return expression &&
+ expression.callee &&
+ (expression.callee.name === 'skip' ||
+ expression.callee.property &&
+ expression.callee.property.name === 'skip');
+}