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 <daniel.bevenius@gmail.com> 8 */ 9'use strict'; 10 11const utils = require('./rules-utils.js'); 12 13//------------------------------------------------------------------------------ 14// Rule Definition 15//------------------------------------------------------------------------------ 16const msg = 'Please add a hasCrypto check to allow this test to be skipped ' + 17 'when Node is built "--without-ssl".'; 18 19const cryptoModules = ['crypto', 'http2']; 20const requireModules = cryptoModules.concat(['tls', 'https']); 21const bindingModules = cryptoModules.concat(['tls_wrap']); 22 23module.exports = function(context) { 24 const missingCheckNodes = []; 25 const requireNodes = []; 26 let commonModuleNode = null; 27 let hasSkipCall = false; 28 29 function testCryptoUsage(node) { 30 if (utils.isRequired(node, requireModules) || 31 utils.isBinding(node, bindingModules)) { 32 requireNodes.push(node); 33 } 34 35 if (utils.isCommonModule(node)) { 36 commonModuleNode = node; 37 } 38 } 39 40 function testIfStatement(node) { 41 if (node.test.argument === undefined) { 42 return; 43 } 44 if (isCryptoCheck(node.test.argument)) { 45 checkCryptoCall(node); 46 } 47 } 48 49 function isCryptoCheck(node) { 50 return utils.usesCommonProperty(node, ['hasCrypto', 'hasFipsCrypto']); 51 } 52 53 function checkCryptoCall(node) { 54 if (utils.inSkipBlock(node)) { 55 hasSkipCall = true; 56 } else { 57 missingCheckNodes.push(node); 58 } 59 } 60 61 function testMemberExpression(node) { 62 if (isCryptoCheck(node)) { 63 checkCryptoCall(node); 64 } 65 } 66 67 function reportIfMissingCheck() { 68 if (hasSkipCall) { 69 // There is a skip, which is good, but verify that the require() calls 70 // in question come after at least one check. 71 if (missingCheckNodes.length > 0) { 72 requireNodes.forEach((requireNode) => { 73 const beforeAllChecks = missingCheckNodes.every((checkNode) => { 74 return requireNode.range[0] < checkNode.range[0]; 75 }); 76 77 if (beforeAllChecks) { 78 context.report({ 79 node: requireNode, 80 message: msg 81 }); 82 } 83 }); 84 } 85 return; 86 } 87 88 if (requireNodes.length > 0) { 89 if (missingCheckNodes.length > 0) { 90 report(missingCheckNodes); 91 } else { 92 report(requireNodes); 93 } 94 } 95 } 96 97 function report(nodes) { 98 nodes.forEach((node) => { 99 context.report({ 100 node, 101 message: msg, 102 fix: (fixer) => { 103 if (commonModuleNode) { 104 return fixer.insertTextAfter( 105 commonModuleNode, 106 '\nif (!common.hasCrypto) {' + 107 ' common.skip("missing crypto");' + 108 '}' 109 ); 110 } 111 } 112 }); 113 }); 114 } 115 116 return { 117 'CallExpression': (node) => testCryptoUsage(node), 118 'IfStatement:exit': (node) => testIfStatement(node), 119 'MemberExpression:exit': (node) => testMemberExpression(node), 120 'Program:exit': () => reportIfMissingCheck() 121 }; 122}; 123 124module.exports.meta = { 125 fixable: 'code' 126}; 127