@locker/eslint-plugin-unsafe-types
Version:
Detect usage of unsigned unsafe types.
108 lines (92 loc) • 3.57 kB
JavaScript
/**
* @fileoverview Rule to flag use of $A.util.globalEval() statement
* @author Lightning Web Security Team
*/
;
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require('../utils/ast-utils');
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Checks a given node is a MemberExpression node which has the specified name's
* property.
* @param {ASTNode} node A node to check.
* @param {string} name A name to check.
* @returns {boolean} `true` if the node is a MemberExpression node which has
* the specified name's property
*/
function isMember(node, name) {
return astUtils.isSpecificMemberAccess(node, null, name);
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Disallow the use of unsigned `$A.util.globalEval()`',
recommended: true,
url: './docs/rules/unsafe-aura-globalEval',
},
messages: {
unexpected: 'eval can be harmful.',
},
},
create(context) {
/**
* Reports a given node.
*
* `node` is `Identifier` or `MemberExpression`.
* The parent of `node` might be `CallExpression`.
*
* The location of the report is always `eval` `Identifier` (or possibly
* `Literal`). The type of the report is `CallExpression` if the parent is
* `CallExpression`. Otherwise, it's the given node type.
* @param {ASTNode} node A node to report.
* @returns {void}
*/
function report(node) {
const parent = node.parent;
const locationNode = node.type === 'MemberExpression' ? node.property : node;
const reportNode =
parent.type === 'CallExpression' && parent.callee === node ? parent : node;
context.report({
node: reportNode,
loc: locationNode.loc,
messageId: 'unexpected',
});
}
function isAuraGlobalEval(node) {
if (isMember(node, 'globalEval') || isMember(node, '$globalEval$')) {
const parent = node.object;
if (isMember(parent, 'util') || isMember(parent, '$util$')) {
if (parent.object.type === 'MemberExpression') {
if (isMember(parent.object, '$A')) {
return true;
}
} else if (parent.object.name === '$A') {
return true;
}
}
}
return false;
}
return {
'CallExpression:exit'(node) {
const callee = node.callee;
// LWS BEGIN
if (callee.type === 'MemberExpression') {
const [argument] = node.arguments;
if (isAuraGlobalEval(callee) && !astUtils.isSignatureCall(argument)) {
report(callee);
}
}
},
};
},
};