eslint-plugin-node-security 4.13.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -1
- package/package.json +2 -2
- package/src/index.js +1 -1
- package/src/rules/detect-child-process/index.js +1 -1
- package/src/rules/detect-eval-with-expression/index.js +1 -1
- package/src/rules/detect-non-literal-fs-filename/index.js +1 -1
- package/src/rules/detect-suspicious-dependencies/index.js +1 -1
- package/src/rules/lock-file/index.js +1 -1
- package/src/rules/no-buffer-overread/index.js +1 -1
- package/src/rules/no-cryptojs/index.js +1 -1
- package/src/rules/no-cryptojs-weak-random/index.js +1 -1
- package/src/rules/no-data-in-temp-storage/index.js +1 -1
- package/src/rules/no-deprecated-buffer/index.js +1 -1
- package/src/rules/no-deprecated-cipher-method/index.js +1 -1
- package/src/rules/no-dynamic-command-string/index.js +1 -1
- package/src/rules/no-dynamic-dependency-loading/index.js +1 -1
- package/src/rules/no-dynamic-require/index.js +1 -1
- package/src/rules/no-ecb-mode/index.js +1 -1
- package/src/rules/no-env-injection/index.js +1 -1
- package/src/rules/no-insecure-http-parser/index.js +1 -1
- package/src/rules/no-insecure-key-derivation/index.js +1 -1
- package/src/rules/no-insecure-rsa-padding/index.js +1 -1
- package/src/rules/no-math-random-crypto/index.js +1 -1
- package/src/rules/no-self-signed-certs/index.js +1 -1
- package/src/rules/no-sha1-hash/index.js +1 -1
- package/src/rules/no-shell-injection/index.js +1 -1
- package/src/rules/no-ssrf/index.js +1 -1
- package/src/rules/no-static-iv/index.js +1 -1
- package/src/rules/no-timing-unsafe-compare/index.js +1 -1
- package/src/rules/no-toctou-vulnerability/index.js +1 -1
- package/src/rules/no-unbounded-decompression/index.js +1 -1
- package/src/rules/no-unsafe-buffer-alloc/index.js +1 -1
- package/src/rules/no-unsafe-dynamic-require/index.js +1 -1
- package/src/rules/no-weak-cipher-algorithm/index.js +1 -1
- package/src/rules/no-weak-hash-algorithm/index.js +1 -1
- package/src/rules/no-zip-slip/index.js +1 -1
- package/src/rules/prefer-native-crypto/index.js +1 -1
- package/src/rules/require-aead-tag-verification/index.js +1 -1
- package/src/rules/require-dependency-integrity/index.js +1 -1
- package/src/rules/require-secure-credential-storage/index.js +1 -1
- package/src/rules/require-secure-deletion/index.js +1 -1
- package/src/rules/require-storage-encryption/index.js +1 -1
- package/src/rules/require-stream-error-handler/index.js +1 -1
- package/src/utils/const-value.js +1 -0
- package/src/utils/credential-evidence.js +1 -0
- package/src/utils/provenance.js +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.requireStreamErrorHandler=void 0;exports.calleeMethodName=calleeMethodName;exports.constructsStream=constructsStream;const eslint_devkit_1=require("@interlace/eslint-devkit");const STREAM_CONSTRUCTORS=new Set(["createReadStream","createWriteStream","createGzip","createGunzip","createDeflate","createInflate","createBrotliCompress","createBrotliDecompress"]);const LISTENER_METHODS=new Set(["on","once","addListener","prependListener","prependOnceListener"]);function calleeMethodName(callee){if(callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier)return callee.name;if(callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&!callee.computed&&callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return callee.property.name}return void 0}function constructsStream(node){if(node.type!==eslint_devkit_1.AST_NODE_TYPES.CallExpression)return false;const name=calleeMethodName(node.callee);return name!==void 0&&STREAM_CONSTRUCTORS.has(name)}exports.requireStreamErrorHandler=(0,eslint_devkit_1.createRule)({name:"require-stream-error-handler",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/require-stream-error-handler.md",description:"Require an 'error' listener on streams passed to .pipe(), which does not forward errors",cwe:"CWE-248",cvss:7.5},
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.requireStreamErrorHandler=void 0;exports.calleeMethodName=calleeMethodName;exports.constructsStream=constructsStream;const eslint_devkit_1=require("@interlace/eslint-devkit");const STREAM_CONSTRUCTORS=new Set(["createReadStream","createWriteStream","createGzip","createGunzip","createDeflate","createInflate","createBrotliCompress","createBrotliDecompress"]);const LISTENER_METHODS=new Set(["on","once","addListener","prependListener","prependOnceListener"]);function calleeMethodName(callee){if(callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier)return callee.name;if(callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&!callee.computed&&callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return callee.property.name}return void 0}function constructsStream(node){if(node.type!==eslint_devkit_1.AST_NODE_TYPES.CallExpression)return false;const name=calleeMethodName(node.callee);return name!==void 0&&STREAM_CONSTRUCTORS.has(name)}exports.requireStreamErrorHandler=(0,eslint_devkit_1.createRule)({name:"require-stream-error-handler",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/require-stream-error-handler.md",description:"Require an 'error' listener on streams passed to .pipe(), which does not forward errors",cwe:"CWE-248",cvss:7.5},messages:{unhandledStreamError:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Unhandled stream error",cwe:"CWE-248",description:".pipe() forwards data but not errors. A stream that emits 'error' with no listener throws inside the EventEmitter, which is an uncaught exception \u2014 the process exits. A single request for a missing or unreadable file is enough to stop the server.",severity:"HIGH",fix:"Attach stream.on('error', handler) before piping, or use pipeline(), which destroys every stream and reports the failure.",documentationLink:"https://cwe.mitre.org/data/definitions/248.html"})},schema:[{type:"object",properties:{allowInTests:{type:"boolean",default:true,description:"Allow unhandled stream errors in test files"}},additionalProperties:false}]},defaultOptions:[{allowInTests:true}],create(context,[options={}]){const{allowInTests=true}=options;if(allowInTests&&(0,eslint_devkit_1.isTestFilePath)(context.filename)){return{}}const handled=new Set;const streamBindings=new Set;const pending=[];function unhandledStream(node){if(constructsStream(node))return node;if(node.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return null;if(!streamBindings.has(node.name))return null;if(handled.has(node.name))return null;return node}return{VariableDeclarator(node){if(node.init!==null&&node.id.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&constructsStream(node.init)){streamBindings.add(node.id.name)}},CallExpression(node){const callee=node.callee;if(callee.type!==eslint_devkit_1.AST_NODE_TYPES.MemberExpression||callee.computed||callee.property.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier){return}const method=callee.property.name;if(LISTENER_METHODS.has(method)){const event=node.arguments[0];if(event?.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&event.value==="error"&&callee.object.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){handled.add(callee.object.name)}return}if(method!=="pipe")return;pending.push({node,offender:node})},"Program:exit"(){for(const{node}of pending){const callee=node.callee;const source=unhandledStream(callee.object);const destinationArg=node.arguments[0];const destination=destinationArg===void 0||destinationArg.type===eslint_devkit_1.AST_NODE_TYPES.SpreadElement?null:unhandledStream(destinationArg);const offender=source??destination;if(offender===null)continue;context.report({node:offender,messageId:"unhandledStreamError"})}}}}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.resolveConstant=resolveConstant;exports.constInitializerOf=constInitializerOf;exports.resolveConstantString=resolveConstantString;const eslint_devkit_1=require("@interlace/eslint-devkit");const provenance_1=require("./provenance");function literalValue(node){if(node.type===eslint_devkit_1.AST_NODE_TYPES.Literal){return typeof node.value==="string"||typeof node.value==="number"?node.value:null}if(node.type===eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral&&node.expressions.length===0){return node.quasis[0].value.cooked}return null}function resolveConstant(sourceCode,node){const direct=literalValue(node);if(direct!==null)return{value:direct,source:node,viaAlias:false};if(node.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return null;const init=(0,provenance_1.constLiteralOf)(sourceCode,node);if(init===void 0)return null;const value=literalValue(init);return value===null?null:{value,source:init,viaAlias:true}}function constInitializerOf(sourceCode,node){const variable=(0,provenance_1.findVariable)(sourceCode,node);if(!variable||variable.defs.length!==1)return null;const def=variable.defs[0];if(def.type!=="Variable"||def.parent.kind!=="const")return null;if(def.node.id.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return null;return def.node.init??null}function resolveConstantString(sourceCode,node){const resolved=resolveConstant(sourceCode,node);return resolved!==null&&typeof resolved.value==="string"?resolved:null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.storesACredential=storesACredential;exports.expressionNamesACredential=expressionNamesACredential;exports.isEncrypted=isEncrypted;exports.isEncryptedExpression=isEncryptedExpression;exports.isWebStorageWrite=isWebStorageWrite;exports.isEnvironmentWrite=isEnvironmentWrite;exports.isFileWrite=isFileWrite;const eslint_devkit_1=require("@interlace/eslint-devkit");const CREDENTIAL_WORDS=["password","passwd","secret","token","credential","apikey","api_key","accesskey","access_key","privatekey","private_key","jwt","sessionid","session_id","refreshtoken","refresh_token","clientsecret","client_secret","authtoken","auth_token"];function namesACredential(text){const normalized=text.toLowerCase();return CREDENTIAL_WORDS.some(word=>normalized.includes(word))}function nameOf(node){switch(node.type){case eslint_devkit_1.AST_NODE_TYPES.Identifier:return node.name;case eslint_devkit_1.AST_NODE_TYPES.Literal:return typeof node.value==="string"?node.value:"";case eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral:return node.quasis.map(q=>q.value.raw).join("");case eslint_devkit_1.AST_NODE_TYPES.MemberExpression:if(node.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier)return node.property.name;return node.property.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&typeof node.property.value==="string"?node.property.value:"";default:return""}}function storesACredential(node){return node.arguments.some(argument=>{if(argument.type===eslint_devkit_1.AST_NODE_TYPES.SpreadElement)return false;return namesACredential(nameOf(argument))})}function expressionNamesACredential(node){return namesACredential(nameOf(node))}const CRYPTO_MODULES=new Set(["crypto","crypto-js","node-forge","tweetnacl","libsodium","libsodium-wrappers","libsodium-wrappers-sumo","sodium-native","jose","node-jose","@aws-crypto/client-node","@google-cloud/kms"]);const ENCRYPTION_APIS=new Set(["publicEncrypt","privateEncrypt","createCipheriv","createCipher","encrypt","wrapKey","CompactEncrypt","FlattenedEncrypt","GeneralEncrypt","compactEncrypt","crypto_secretbox_easy","crypto_box_easy","crypto_box_seal","crypto_aead_chacha20poly1305_ietf_encrypt","crypto_aead_xchacha20poly1305_ietf_encrypt","crypto_secretstream_xchacha20poly1305_push","secretbox","box"]);const CIPHER_OUTPUT_METHODS=new Set(["update","final"]);function localFunctionOf(node,scope){if(node.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return null;let current=scope;let variable;while(current&&!variable){variable=current.set?.get(node.name);current=current.upper}if(!variable||variable.defs.length!==1)return null;const def=variable.defs[0];if(def.type==="FunctionName")return def.node;if(def.type!=="Variable")return null;const init=def.node.init;if(init?.type===eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression||init?.type===eslint_devkit_1.AST_NODE_TYPES.FunctionExpression){return init}return null}function returnedExpressions(fn){const body=fn.body;if(!body)return[];if(body.type!==eslint_devkit_1.AST_NODE_TYPES.BlockStatement)return[body];const found=[];const walk=statements=>{for(const statement of statements){if(statement.type===eslint_devkit_1.AST_NODE_TYPES.ReturnStatement){if(statement.argument)found.push(statement.argument)}else if(statement.type===eslint_devkit_1.AST_NODE_TYPES.IfStatement){walk([statement.consequent]);if(statement.alternate)walk([statement.alternate])}else if(statement.type===eslint_devkit_1.AST_NODE_TYPES.BlockStatement){walk(statement.body)}else if(statement.type===eslint_devkit_1.AST_NODE_TYPES.TryStatement){walk(statement.block.body)}}};walk(body.body);return found}function provablyEncrypts(value,sourceCode,depth,seen){if(depth>8||seen.has(value))return false;seen.add(value);const recurse=node=>provablyEncrypts(node,sourceCode,depth+1,seen);switch(value.type){case eslint_devkit_1.AST_NODE_TYPES.AwaitExpression:return recurse(value.argument);case eslint_devkit_1.AST_NODE_TYPES.ArrayExpression:return value.elements.some(element=>element!==null&&recurse(element));case eslint_devkit_1.AST_NODE_TYPES.Identifier:{let current=sourceCode.getScope(value);let variable;while(current&&!variable){variable=current.set?.get(value.name);current=current.upper}if(!variable||variable.defs.length!==1)return false;const def=variable.defs[0];if(def.type!=="Variable"||!def.node.init)return false;return recurse(def.node.init)}case eslint_devkit_1.AST_NODE_TYPES.CallExpression:{const callee=value.callee;const scope=sourceCode.getScope(value);const binding=(0,eslint_devkit_1.resolveModuleBinding)(callee,scope);if(binding&&CRYPTO_MODULES.has(binding.module)&&binding.path.length>0&&ENCRYPTION_APIS.has(binding.path[binding.path.length-1])){return true}if(callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&!callee.computed&&callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&CIPHER_OUTPUT_METHODS.has(callee.property.name)&&recurse(callee.object)){return true}const fn=localFunctionOf(callee,scope);if(fn&&returnedExpressions(fn).some(returned=>recurse(returned))){return true}return value.arguments.some(argument=>argument.type!==eslint_devkit_1.AST_NODE_TYPES.SpreadElement&&recurse(argument))}default:return false}}function isEncrypted(node,sourceCode){return isEncryptedExpression(node.arguments[1],sourceCode)}function isEncryptedExpression(value,sourceCode){if(value===void 0)return false;return provablyEncrypts(value,sourceCode,0,new Set)}const CLIENT_STORES=new Set(["localStorage","sessionStorage","AsyncStorage"]);function isWebStorageWrite(node){const callee=node.callee;if(callee.type!==eslint_devkit_1.AST_NODE_TYPES.MemberExpression)return false;if(callee.property.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return false;if(callee.property.name!=="setItem")return false;const object=callee.object;if(object.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return CLIENT_STORES.has(object.name)}return object.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&object.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&CLIENT_STORES.has(object.property.name)}function isEnvironmentWrite(node){const target=node.left;if(target.type!==eslint_devkit_1.AST_NODE_TYPES.MemberExpression)return false;const object=target.object;return object.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&!object.computed&&object.object.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&object.object.name==="process"&&object.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&object.property.name==="env"}function isFileWrite(node){const callee=node.callee;if(callee.type!==eslint_devkit_1.AST_NODE_TYPES.MemberExpression)return false;if(callee.property.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return false;return["writeFile","writeFileSync","appendFile","appendFileSync"].includes(callee.property.name)}
|
package/src/utils/provenance.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.findVariable=findVariable;exports.bindingInit=bindingInit;exports.constLiteralOf=constLiteralOf;exports.makeReadsTaintSource=makeReadsTaintSource;const eslint_devkit_1=require("@interlace/eslint-devkit");function findVariable(sourceCode,node){let scope=sourceCode.getScope(node);while(scope){const found=scope.set?.get(node.name);if(found)return found;scope=scope.upper}return null}function bindingInit(sourceCode,node){const variable=findVariable(sourceCode,node);if(!variable||variable.defs.length!==1)return void 0;const def=variable.defs[0];if(def.type!=="Variable")return void 0;return def.node.init
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.findVariable=findVariable;exports.bindingInit=bindingInit;exports.constLiteralOf=constLiteralOf;exports.makeReadsTaintSource=makeReadsTaintSource;const eslint_devkit_1=require("@interlace/eslint-devkit");function findVariable(sourceCode,node){let scope=sourceCode.getScope(node);while(scope){const found=scope.set?.get(node.name);if(found)return found;scope=scope.upper}return null}function bindingInit(sourceCode,node){const variable=findVariable(sourceCode,node);if(!variable||variable.defs.length!==1)return void 0;const def=variable.defs[0];if(def.type!=="Variable")return void 0;return def.node.init?(0,eslint_devkit_1.unwrapTypeSyntax)(def.node.init):void 0}function constLiteralOf(sourceCode,node){const variable=findVariable(sourceCode,node);if(!variable||variable.defs.length!==1)return void 0;const def=variable.defs[0];if(def.type!=="Variable")return void 0;if(def.parent.kind!=="const")return void 0;if(def.node.id.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return void 0;const init=def.node.init;if(!init)return void 0;if(init.type===eslint_devkit_1.AST_NODE_TYPES.Literal)return init;if(init.type===eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral&&init.expressions.length===0){return init}return void 0}const REQUEST_PROPERTY_NAMES=new Set(["headers","query","body","params","cookies","searchparams","rawbody","querystringparameters","pathparameters","formdata"]);function makeReadsTaintSource(sourceCode,roots){const reads=(node,depth)=>{if(depth>6)return false;const bare=(0,eslint_devkit_1.unwrapTypeSyntax)(node);if(bare!==node)return reads(bare,depth+1);switch(node.type){case eslint_devkit_1.AST_NODE_TYPES.Identifier:{if(roots.has(node.name.toLowerCase()))return true;const init=bindingInit(sourceCode,node);if(init!==void 0&&reads(init,depth+1))return true;const variable=findVariable(sourceCode,node);const priorWrites=(variable?.references??[]).map(ref=>ref.writeExpr).filter(write=>write!=null).filter(write=>write.range[1]<=node.range[0]).sort((a,b)=>a.range[1]-b.range[1]);const lastWrite=priorWrites.at(-1);return lastWrite!==void 0&&reads(lastWrite,depth+1)}case eslint_devkit_1.AST_NODE_TYPES.MemberExpression:{if(!node.computed&&node.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&REQUEST_PROPERTY_NAMES.has(node.property.name.toLowerCase())){return true}return reads(node.object,depth+1)}case eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral:return node.expressions.some(expression=>reads(expression,depth+1));case eslint_devkit_1.AST_NODE_TYPES.BinaryExpression:return reads(node.left,depth+1)||reads(node.right,depth+1);case eslint_devkit_1.AST_NODE_TYPES.LogicalExpression:return reads(node.left,depth+1)||reads(node.right,depth+1);case eslint_devkit_1.AST_NODE_TYPES.ConditionalExpression:return reads(node.consequent,depth+1)||reads(node.alternate,depth+1);case eslint_devkit_1.AST_NODE_TYPES.AwaitExpression:return reads(node.argument,depth+1);case eslint_devkit_1.AST_NODE_TYPES.ArrayExpression:return node.elements.some(element=>element!==null&&reads(element,depth+1));case eslint_devkit_1.AST_NODE_TYPES.SpreadElement:return reads(node.argument,depth+1);case eslint_devkit_1.AST_NODE_TYPES.CallExpression:case eslint_devkit_1.AST_NODE_TYPES.NewExpression:return node.arguments.some(argument=>reads(argument,depth+1))||node.callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&!(node.callee.object.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.object.name==="process")&&reads(node.callee.object,depth+1);default:return false}};return node=>reads(node,0)}
|