Skip to content

Commit 1d97ff4

Browse files
danbevMylesBorins
authored andcommitted
tools: add eslint rule for hasCrypto checking
The motivation for this commit is to pick up early on missing checks for crypto support (when Node is built --without-ssl). There are currently usages of common.hasCrypto which are not just for detecting if crypto support is available and then skip the test in question. For these case we still want to have a lint error generated which can then be disabled using an ESLint comment. PR-URL: #13813 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Teddy Katz <[email protected]>
1 parent 959b270 commit 1d97ff4

8 files changed

+152
-6
lines changed

test/.eslintrc.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ rules:
1010
prefer-assert-iferror: error
1111
prefer-assert-methods: error
1212
prefer-common-mustnotcall: error
13+
crypto-check: error
1314
## common module is mandatory in tests
1415
required-modules: [error, common]

test/common/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

22-
/* eslint-disable required-modules */
22+
/* eslint-disable required-modules, crypto-check */
2323
'use strict';
2424
const path = require('path');
2525
const fs = require('fs');

test/parallel/test-async-wrap-getasyncid.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,14 @@ function testInitialized(req, ctor_name) {
8181
}
8282

8383

84-
if (common.hasCrypto) {
84+
if (common.hasCrypto) { // eslint-disable-line crypto-check
8585
const tls = require('tls');
8686
// SecurePair
8787
testInitialized(tls.createSecurePair().ssl, 'Connection');
8888
}
8989

9090

91-
if (common.hasCrypto) {
91+
if (common.hasCrypto) { // eslint-disable-line crypto-check
9292
const crypto = require('crypto');
9393

9494
// The handle for PBKDF2 and RandomBytes isn't returned by the function call,
@@ -215,7 +215,7 @@ if (common.hasCrypto) {
215215
}
216216

217217

218-
if (common.hasCrypto) {
218+
if (common.hasCrypto) { // eslint-disable-line crypto-check
219219
const TCP = process.binding('tcp_wrap').TCP;
220220
const tcp = new TCP();
221221

test/parallel/test-buffer-alloc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,7 @@ assert.throws(() => Buffer.from('', 'buffer'),
905905
}
906906
}
907907

908-
if (common.hasCrypto) {
908+
if (common.hasCrypto) { // eslint-disable-line crypto-check
909909
// Test truncation after decode
910910
const crypto = require('crypto');
911911

test/parallel/test-buffer-concat.js

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ function assertWrongList(value) {
6161
});
6262
}
6363

64+
// eslint-disable-next-line crypto-check
6465
const random10 = common.hasCrypto ?
6566
require('crypto').randomBytes(10) :
6667
Buffer.alloc(10, 1);

test/parallel/test-http2-noflag.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
require('../common');
55
const assert = require('assert');
66

7-
assert.throws(() => require('http2'),
7+
assert.throws(() => require('http2'), // eslint-disable-line crypto-check
88
/^Error: Cannot find module 'http2'$/);

tools/eslint-rules/crypto-check.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* @fileoverview Check that common.hasCrypto is used if crypto, tls,
3+
* https, or http2 modules are required.
4+
*
5+
* This rule can be ignored using // eslint-disable-line crypto-check
6+
*
7+
* @author Daniel Bevenius <[email protected]>
8+
*/
9+
'use strict';
10+
11+
const utils = require('./rules-utils.js');
12+
13+
//------------------------------------------------------------------------------
14+
// Rule Definition
15+
//------------------------------------------------------------------------------
16+
const msg = 'Please add a hasCrypto check to allow this test to be skipped ' +
17+
'when Node is built "--without-ssl".';
18+
19+
module.exports = function(context) {
20+
const missingCheckNodes = [];
21+
const requireNodes = [];
22+
var hasSkipCall = false;
23+
24+
function testCryptoUsage(node) {
25+
if (utils.isRequired(node, ['crypto', 'tls', 'https', 'http2'])) {
26+
requireNodes.push(node);
27+
}
28+
}
29+
30+
function testIfStatement(node) {
31+
if (node.test.argument === undefined) {
32+
return;
33+
}
34+
if (isCryptoCheck(node.test.argument)) {
35+
checkCryptoCall(node);
36+
}
37+
}
38+
39+
function isCryptoCheck(node) {
40+
return utils.usesCommonProperty(node, ['hasCrypto', 'hasFipsCrypto']);
41+
}
42+
43+
function checkCryptoCall(node) {
44+
if (utils.inSkipBlock(node)) {
45+
hasSkipCall = true;
46+
} else {
47+
missingCheckNodes.push(node);
48+
}
49+
}
50+
51+
function testMemberExpression(node) {
52+
if (isCryptoCheck(node)) {
53+
checkCryptoCall(node);
54+
}
55+
}
56+
57+
function reportIfMissingCheck(node) {
58+
if (hasSkipCall) {
59+
return;
60+
}
61+
62+
if (requireNodes.length > 0) {
63+
if (missingCheckNodes.length > 0) {
64+
report(missingCheckNodes);
65+
} else {
66+
report(requireNodes);
67+
}
68+
}
69+
}
70+
71+
function report(nodes) {
72+
nodes.forEach((node) => {
73+
context.report(node, msg);
74+
});
75+
}
76+
77+
return {
78+
'CallExpression': (node) => testCryptoUsage(node),
79+
'IfStatement:exit': (node) => testIfStatement(node),
80+
'MemberExpression:exit': (node) => testMemberExpression(node),
81+
'Program:exit': (node) => reportIfMissingCheck(node)
82+
};
83+
};

tools/eslint-rules/rules-utils.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Utility functions common to ESLint rules.
3+
*/
4+
'use strict';
5+
6+
/**
7+
* Returns true if any of the passed in modules are used in
8+
* require calls.
9+
*/
10+
module.exports.isRequired = function(node, modules) {
11+
return node.callee.name === 'require' &&
12+
modules.includes(node.arguments[0].value);
13+
};
14+
15+
/**
16+
* Returns true is the node accesses any property in the properties
17+
* array on the 'common' object.
18+
*/
19+
module.exports.usesCommonProperty = function(node, properties) {
20+
if (node.name) {
21+
return properties.includes(node.name);
22+
}
23+
if (node.property) {
24+
return properties.includes(node.property.name);
25+
}
26+
return false;
27+
};
28+
29+
/**
30+
* Returns true if the passed in node is inside an if statement block,
31+
* and the block also has a call to skip.
32+
*/
33+
module.exports.inSkipBlock = function(node) {
34+
var hasSkipBlock = false;
35+
if (node.test &&
36+
node.test.type === 'UnaryExpression' &&
37+
node.test.operator === '!') {
38+
const consequent = node.consequent;
39+
if (consequent.body) {
40+
consequent.body.some(function(expressionStatement) {
41+
if (hasSkip(expressionStatement.expression)) {
42+
return hasSkipBlock = true;
43+
}
44+
return false;
45+
});
46+
} else {
47+
if (hasSkip(consequent.expression)) {
48+
hasSkipBlock = true;
49+
}
50+
}
51+
}
52+
return hasSkipBlock;
53+
};
54+
55+
function hasSkip(expression) {
56+
return expression &&
57+
expression.callee &&
58+
(expression.callee.name === 'skip' ||
59+
expression.callee.property &&
60+
expression.callee.property.name === 'skip');
61+
}

0 commit comments

Comments
 (0)