🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@hao360/eslint-plugin-cube

Package Overview
Dependencies
Maintainers
8
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hao360/eslint-plugin-cube - npm Package Compare versions

Comparing version
0.1.5
to
0.2.0
+131
lib/rules/no-cube-dynamic.js
"use strict";
const { getStaticValue } = require("eslint-utils");
const {
isSpecificMemberAccess,
getVariableByName,
isSpecificId,
getStaticPropertyName,
} = require("../utils");
const callMethods = new Set(["apply", "bind", "call"]);
module.exports = {
meta: {
docs: {
description: "",
recommended: false
}
},
create: function(context) {
const GLOBAL_CANDIDATES = Object.freeze(["global", "window", "globalThis"]);
const EVAL_LIKE_FUNC_PATTERN = /^(?:set(?:Interval|Timeout)|execScript)$/u;
function isEvaluatedString(node) {
if (
(node.type === "Literal" && typeof node.value === "string") ||
node.type === "TemplateLiteral"
) {
return true;
}
if (node.type === "BinaryExpression" && node.operator === "+") {
return isEvaluatedString(node.left) || isEvaluatedString(node.right);
}
return false;
}
function reportImpliedEvalCallExpression(node) {
const [firstArgument] = node.arguments;
if (firstArgument) {
const staticValue = getStaticValue(firstArgument, context.getScope());
const isStaticString = staticValue && typeof staticValue.value === "string";
const isString = isStaticString || isEvaluatedString(firstArgument);
if (isString) {
context.report({
node,
message: "禁止动态执行js"
});
}
}
}
function reportImpliedEvalViaGlobal(globalVar) {
const { references, name } = globalVar;
references.forEach(ref => {
const identifier = ref.identifier;
let node = identifier.parent;
while (isSpecificMemberAccess(node, null, name)) {
node = node.parent;
}
if (isSpecificMemberAccess(node, null, EVAL_LIKE_FUNC_PATTERN)) {
const calleeNode = node.parent.type === "ChainExpression" ? node.parent : node;
const parent = calleeNode.parent;
if (parent.type === "CallExpression" && parent.callee === calleeNode) {
reportImpliedEvalCallExpression(parent);
}
}
});
}
return {
CallExpression(node) {
if (isSpecificId(node.callee, EVAL_LIKE_FUNC_PATTERN)) {
reportImpliedEvalCallExpression(node);
}
},
"Program:exit"() {
const globalScope = context.getScope();
const variable = globalScope.set.get("Function");
GLOBAL_CANDIDATES
.map(candidate => getVariableByName(globalScope, candidate))
.filter(globalVar => !!globalVar && globalVar.defs.length === 0)
.forEach(reportImpliedEvalViaGlobal);
if (variable && variable.defs.length === 0) {
variable.references.forEach(ref => {
const node = ref.identifier;
const { parent } = node;
let evalNode;
if (parent) {
if (node === parent.callee && (
parent.type === "NewExpression" ||
parent.type === "CallExpression"
)) {
evalNode = parent;
} else if (
parent.type === "MemberExpression" &&
node === parent.object &&
callMethods.has(getStaticPropertyName(parent))
) {
const maybeCallee = parent.parent.type === "ChainExpression" ? parent.parent : parent;
if (maybeCallee.parent.type === "CallExpression" && maybeCallee.parent.callee === maybeCallee) {
evalNode = maybeCallee.parent;
}
}
}
if (evalNode) {
context.report({
node: evalNode,
message: "禁止动态执行js"
});
}
});
}
}
};
}
};
"use strict";
const {
isSpecificMemberAccess,
getVariableByName,
isCallee,
isSpecificId,
isDefaultThisBinding,
} = require("../utils");
const candidatesOfGlobalObject = Object.freeze([
"global",
"window",
"globalThis"
]);
function isMember(node, name) {
return isSpecificMemberAccess(node, null, name);
}
module.exports = {
meta: {
docs: {
description: "",
recommended: false
}
},
create: function(context) {
const sourceCode = context.getSourceCode();
let funcInfo = null;
function enterVarScope(node) {
const strict = context.getScope().isStrict;
funcInfo = {
upper: funcInfo,
node,
strict,
defaultThis: false,
initialized: strict
};
}
function exitVarScope() {
funcInfo = funcInfo.upper;
}
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,
message: "禁止使用eval"
});
}
function reportAccessingEvalViaGlobalObject(globalScope) {
for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
const name = candidatesOfGlobalObject[i];
const variable = getVariableByName(globalScope, name);
if (!variable) {
continue;
}
const references = variable.references;
for (let j = 0; j < references.length; ++j) {
const identifier = references[j].identifier;
let node = identifier.parent;
while (isMember(node, name)) {
node = node.parent;
}
if (isMember(node, "eval")) {
report(node);
}
}
}
}
function reportAccessingEval(globalScope) {
const variable = getVariableByName(globalScope, "eval");
if (!variable) {
return;
}
const references = variable.references;
for (let i = 0; i < references.length; ++i) {
const reference = references[i];
const id = reference.identifier;
if (id.name === "eval" && !isCallee(id)) {
report(id);
}
}
}
return {
"CallExpression:exit"(node) {
const callee = node.callee;
if (isSpecificId(callee, "eval")) {
report(callee);
}
},
Program(node) {
const scope = context.getScope(),
features = context.parserOptions.ecmaFeatures || {},
strict =
scope.isStrict ||
node.sourceType === "module" ||
(features.globalReturn && scope.childScopes[0].isStrict);
funcInfo = {
upper: null,
node,
strict,
defaultThis: true,
initialized: true
};
},
"Program:exit"() {
const globalScope = context.getScope();
exitVarScope();
reportAccessingEval(globalScope);
reportAccessingEvalViaGlobalObject(globalScope);
},
FunctionDeclaration: enterVarScope,
"FunctionDeclaration:exit": exitVarScope,
FunctionExpression: enterVarScope,
"FunctionExpression:exit": exitVarScope,
ArrowFunctionExpression: enterVarScope,
"ArrowFunctionExpression:exit": exitVarScope,
"PropertyDefinition > *.value": enterVarScope,
"PropertyDefinition > *.value:exit": exitVarScope,
StaticBlock: enterVarScope,
"StaticBlock:exit": exitVarScope,
ThisExpression(node) {
if (!isMember(node.parent, "eval")) {
return;
}
if (!funcInfo.initialized) {
funcInfo.initialized = true;
funcInfo.defaultThis = isDefaultThisBinding(
funcInfo.node,
sourceCode
);
}
if (!funcInfo.strict && funcInfo.defaultThis) {
report(node.parent);
}
}
};
}
};
+1
-0

@@ -22,2 +22,3 @@ exports.windowGlobal = [

"window",
"globalThis",
"document",

@@ -24,0 +25,0 @@ "name",

+4
-2

@@ -6,3 +6,4 @@ "use strict";

const noCubeTags = require("./rules/no-cube-tags");
const noCubeWindow = require("./rules/no-cube-window");
const noCubeDynamic = require("./rules/no-cube-dynamic");
const noCubeEval = require("./rules/no-cube-eval");

@@ -13,3 +14,4 @@ module.exports.rules = {

'no-cube-tags': noCubeTags,
'no-cube-window': noCubeWindow,
'no-cube-eval': noCubeEval,
'no-cube-dynamic': noCubeDynamic,
}

@@ -16,0 +18,0 @@ module.exports.processors = {

@@ -0,1 +1,8 @@

const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u;
const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u;
const arrayOrTypedArrayPattern = /Array$/u;
const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u;
const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u;
const thisTagPattern = /^[\s*]*@this/mu;
function isNullLiteral(node) {

@@ -68,2 +75,206 @@ return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;

function isCallee(node) {
return node.parent.type === "CallExpression" && node.parent.callee === node;
}
function isSpecificId(node, name) {
return node.type === "Identifier" && checkText(node.name, name);
}
function checkText(actual, expected) {
return typeof expected === "string"
? actual === expected
: expected.test(actual);
}
function skipChainExpression(node) {
return node && node.type === "ChainExpression" ? node.expression : node;
}
function isSpecificMemberAccess(node, objectName, propertyName) {
const checkNode = skipChainExpression(node);
if (checkNode.type !== "MemberExpression") {
return false;
}
if (objectName && !isSpecificId(checkNode.object, objectName)) {
return false;
}
if (propertyName) {
const actualPropertyName = getStaticPropertyName(checkNode);
if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
return false;
}
}
return true;
}
function startsWithUpperCase(s) {
return s[0] !== s[0].toLocaleLowerCase();
}
function isES5Constructor(node) {
return (node.id && startsWithUpperCase(node.id.name));
}
function hasJSDocThisTag(node, sourceCode) {
const jsdocComment = sourceCode.getJSDocComment(node);
if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
return true;
}
return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
}
function getUpperFunction(node) {
for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
if (anyFunctionPattern.test(currentNode.type)) {
return currentNode;
}
}
return null;
}
function isNullOrUndefined(node) {
return (
isNullLiteral(node) ||
(node.type === "Identifier" && node.name === "undefined") ||
(node.type === "UnaryExpression" && node.operator === "void")
);
}
function isReflectApply(node) {
return isSpecificMemberAccess(node, "Reflect", "apply");
}
function isArrayFromMethod(node) {
return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
}
function isMethodWhichHasThisArg(node) {
return isSpecificMemberAccess(node, null, arrayMethodPattern);
}
function isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
if (node.parent.type === "PropertyDefinition" && node.parent.value === node) {
return false;
}
if (node.type === "StaticBlock") {
return false;
}
if (
(capIsConstructor && isES5Constructor(node)) ||
hasJSDocThisTag(node, sourceCode)
) {
return false;
}
const isAnonymous = node.id === null;
let currentNode = node;
while (currentNode) {
const parent = currentNode.parent;
switch (parent.type) {
case "LogicalExpression":
case "ConditionalExpression":
case "ChainExpression":
currentNode = parent;
break;
case "ReturnStatement": {
const func = getUpperFunction(parent);
if (func === null || !isCallee(func)) {
return true;
}
currentNode = func.parent;
break;
}
case "ArrowFunctionExpression":
if (currentNode !== parent.body || !isCallee(parent)) {
return true;
}
currentNode = parent.parent;
break;
case "Property":
case "PropertyDefinition":
case "MethodDefinition":
return parent.value !== currentNode;
case "AssignmentExpression":
case "AssignmentPattern":
if (parent.left.type === "MemberExpression") {
return false;
}
if (
capIsConstructor &&
isAnonymous &&
parent.left.type === "Identifier" &&
startsWithUpperCase(parent.left.name)
) {
return false;
}
return true;
case "VariableDeclarator":
return !(
capIsConstructor &&
isAnonymous &&
parent.init === currentNode &&
parent.id.type === "Identifier" &&
startsWithUpperCase(parent.id.name)
);
case "MemberExpression":
if (
parent.object === currentNode &&
isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
) {
const maybeCalleeNode = parent.parent.type === "ChainExpression"
? parent.parent
: parent;
return !(
isCallee(maybeCalleeNode) &&
maybeCalleeNode.parent.arguments.length >= 1 &&
!isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
);
}
return true;
case "CallExpression":
if (isReflectApply(parent.callee)) {
return (
parent.arguments.length !== 3 ||
parent.arguments[0] !== currentNode ||
isNullOrUndefined(parent.arguments[1])
);
}
if (isArrayFromMethod(parent.callee)) {
return (
parent.arguments.length !== 3 ||
parent.arguments[1] !== currentNode ||
isNullOrUndefined(parent.arguments[2])
);
}
if (isMethodWhichHasThisArg(parent.callee)) {
return (
parent.arguments.length !== 2 ||
parent.arguments[0] !== currentNode ||
isNullOrUndefined(parent.arguments[1])
);
}
return true;
default:
return true;
}
}
return true;
}
module.exports = {

@@ -73,3 +284,7 @@ getVariableByName,

getStaticStringValue,
isNullLiteral
isNullLiteral,
isCallee,
isSpecificId,
isSpecificMemberAccess,
isDefaultThisBinding,
}
{
"name": "@hao360/eslint-plugin-cube",
"version": "0.1.5",
"version": "0.2.0",
"description": "cube rule validation plugin",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -15,3 +15,5 @@ # eslint-plugin-cube

'@hao360/cube/no-cube-tags',
'@hao360/cube/no-cube-attributes'
'@hao360/cube/no-cube-attributes',
'@hao360/cube/no-cube-eval',
'@hao360/cube/no-cube-dynamic'
}

@@ -18,0 +20,0 @@ }

"use strict";
const {
getStaticPropertyName
} = require("../utils");
const restrictedCalls = [
'window'
]
module.exports = {
meta: {
docs: {
description: "",
recommended: false
}
},
create: function(context) {
const globallyRestrictedObjects = new Map();
restrictedCalls.forEach(objectName => {
globallyRestrictedObjects.set(objectName, { message: `禁止使用${objectName}` });
});
function checkPropertyAccess(node, objectName, propertyName) {
if (propertyName === null) {
return;
}
const matchedObjectProperty = globallyRestrictedObjects.get(objectName);
if (matchedObjectProperty) {
const message = matchedObjectProperty.message;
context.report({
node,
message
});
}
}
function checkDestructuringAssignment(node) {
if (node.right.type === "Identifier") {
const objectName = node.right.name;
if (node.left.type === "ObjectPattern") {
node.left.properties.forEach(property => {
checkPropertyAccess(node.left, objectName, getStaticPropertyName(property));
});
}
}
}
return {
MemberExpression(node) {
checkPropertyAccess(node, node.object && node.object.name, getStaticPropertyName(node));
},
VariableDeclarator(node) {
if (node.init && node.init.type === "Identifier") {
const objectName = node.init.name;
if (node.id.type === "ObjectPattern") {
node.id.properties.forEach(property => {
checkPropertyAccess(node.id, objectName, getStaticPropertyName(property));
});
}
}
},
AssignmentExpression: checkDestructuringAssignment,
AssignmentPattern: checkDestructuringAssignment
}
}
};