eslint-plugin-node-security 4.9.0 → 4.10.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-node-security",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "ESLint plugin for Node.js security — detects command injection, path traversal, SSRF, zip slip, and weak crypto (MD5/SHA-1, ECB, static IV) in fs, child_process, vm, and crypto.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"node": ">=18.0.0"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@interlace/eslint-devkit": "^1.
|
|
81
|
+
"@interlace/eslint-devkit": "^1.13.0"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
84
|
"eslint": "^8.40.0 || ^9.0.0 || ^10.0.0"
|
package/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;exports.rules={get"detect-child-process"(){return require("./rules/detect-child-process").detectChildProcess},get"detect-eval-with-expression"(){return require("./rules/detect-eval-with-expression").detectEvalWithExpression},get"detect-non-literal-fs-filename"(){return require("./rules/detect-non-literal-fs-filename").detectNonLiteralFsFilename},get"no-unsafe-dynamic-require"(){return require("./rules/no-unsafe-dynamic-require").noUnsafeDynamicRequire},get"no-buffer-overread"(){return require("./rules/no-buffer-overread").noBufferOverread},get"no-deprecated-buffer"(){return require("./rules/no-deprecated-buffer").noDeprecatedBuffer},get"no-unsafe-buffer-alloc"(){return require("./rules/no-unsafe-buffer-alloc").noUnsafeBufferAlloc},get"no-toctou-vulnerability"(){return require("./rules/no-toctou-vulnerability").noToctouVulnerability},get"no-zip-slip"(){return require("./rules/no-zip-slip").noZipSlip},get"no-arbitrary-file-access"(){return require("./rules/no-arbitrary-file-access").noArbitraryFileAccess},get"no-data-in-temp-storage"(){return require("./rules/no-data-in-temp-storage").noDataInTempStorage},get"no-ssrf"(){return require("./rules/no-ssrf").noSsrf},get"no-shell-injection"(){return require("./rules/no-shell-injection").noShellInjection},get"no-dynamic-command-string"(){return require("./rules/no-dynamic-command-string").noDynamicCommandString},get"no-dynamic-algorithm-selection"(){return require("./rules/no-dynamic-algorithm-selection").noDynamicAlgorithmSelection},get"detect-suspicious-dependencies"(){return require("./rules/detect-suspicious-dependencies").detectSuspiciousDependencies},get"lock-file"(){return require("./rules/lock-file").lockFile},get"no-dynamic-dependency-loading"(){return require("./rules/no-dynamic-dependency-loading").noDynamicDependencyLoading},get"require-dependency-integrity"(){return require("./rules/require-dependency-integrity").requireDependencyIntegrity},get"require-secure-credential-storage"(){return require("./rules/require-secure-credential-storage").requireSecureCredentialStorage},get"require-secure-deletion"(){return require("./rules/require-secure-deletion").requireSecureDeletion},get"require-storage-encryption"(){return require("./rules/require-storage-encryption").requireStorageEncryption},get"no-dynamic-require"(){return require("./rules/no-dynamic-require").noDynamicRequire},get"no-cryptojs"(){return require("./rules/no-cryptojs").noCryptojs},get"no-cryptojs-weak-random"(){return require("./rules/no-cryptojs-weak-random").noCryptojsWeakRandom},get"no-deprecated-cipher-method"(){return require("./rules/no-deprecated-cipher-method").noDeprecatedCipherMethod},get"no-ecb-mode"(){return require("./rules/no-ecb-mode").noEcbMode},get"no-insecure-key-derivation"(){return require("./rules/no-insecure-key-derivation").noInsecureKeyDerivation},get"no-insecure-rsa-padding"(){return require("./rules/no-insecure-rsa-padding").noInsecureRsaPadding},get"no-math-random-crypto"(){return require("./rules/no-math-random-crypto").noMathRandomCrypto},get"no-self-signed-certs"(){return require("./rules/no-self-signed-certs").noSelfSignedCerts},get"no-sha1-hash"(){return require("./rules/no-sha1-hash").noSha1Hash},get"no-static-iv"(){return require("./rules/no-static-iv").noStaticIv},get"no-timing-unsafe-compare"(){return require("./rules/no-timing-unsafe-compare").noTimingUnsafeCompare},get"no-weak-cipher-algorithm"(){return require("./rules/no-weak-cipher-algorithm").noWeakCipherAlgorithm},get"no-weak-hash-algorithm"(){return require("./rules/no-weak-hash-algorithm").noWeakHashAlgorithm},get"prefer-native-crypto"(){return require("./rules/prefer-native-crypto").preferNativeCrypto}};exports.plugin={meta:{name:"eslint-plugin-node-security",version:"4.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;exports.rules={get"detect-child-process"(){return require("./rules/detect-child-process").detectChildProcess},get"detect-eval-with-expression"(){return require("./rules/detect-eval-with-expression").detectEvalWithExpression},get"detect-non-literal-fs-filename"(){return require("./rules/detect-non-literal-fs-filename").detectNonLiteralFsFilename},get"no-unsafe-dynamic-require"(){return require("./rules/no-unsafe-dynamic-require").noUnsafeDynamicRequire},get"no-buffer-overread"(){return require("./rules/no-buffer-overread").noBufferOverread},get"no-deprecated-buffer"(){return require("./rules/no-deprecated-buffer").noDeprecatedBuffer},get"no-unsafe-buffer-alloc"(){return require("./rules/no-unsafe-buffer-alloc").noUnsafeBufferAlloc},get"no-toctou-vulnerability"(){return require("./rules/no-toctou-vulnerability").noToctouVulnerability},get"no-zip-slip"(){return require("./rules/no-zip-slip").noZipSlip},get"no-arbitrary-file-access"(){return require("./rules/no-arbitrary-file-access").noArbitraryFileAccess},get"no-data-in-temp-storage"(){return require("./rules/no-data-in-temp-storage").noDataInTempStorage},get"no-ssrf"(){return require("./rules/no-ssrf").noSsrf},get"no-shell-injection"(){return require("./rules/no-shell-injection").noShellInjection},get"no-dynamic-command-string"(){return require("./rules/no-dynamic-command-string").noDynamicCommandString},get"no-dynamic-algorithm-selection"(){return require("./rules/no-dynamic-algorithm-selection").noDynamicAlgorithmSelection},get"detect-suspicious-dependencies"(){return require("./rules/detect-suspicious-dependencies").detectSuspiciousDependencies},get"lock-file"(){return require("./rules/lock-file").lockFile},get"no-dynamic-dependency-loading"(){return require("./rules/no-dynamic-dependency-loading").noDynamicDependencyLoading},get"require-dependency-integrity"(){return require("./rules/require-dependency-integrity").requireDependencyIntegrity},get"require-secure-credential-storage"(){return require("./rules/require-secure-credential-storage").requireSecureCredentialStorage},get"require-secure-deletion"(){return require("./rules/require-secure-deletion").requireSecureDeletion},get"require-storage-encryption"(){return require("./rules/require-storage-encryption").requireStorageEncryption},get"no-dynamic-require"(){return require("./rules/no-dynamic-require").noDynamicRequire},get"no-cryptojs"(){return require("./rules/no-cryptojs").noCryptojs},get"no-cryptojs-weak-random"(){return require("./rules/no-cryptojs-weak-random").noCryptojsWeakRandom},get"no-deprecated-cipher-method"(){return require("./rules/no-deprecated-cipher-method").noDeprecatedCipherMethod},get"no-ecb-mode"(){return require("./rules/no-ecb-mode").noEcbMode},get"no-insecure-key-derivation"(){return require("./rules/no-insecure-key-derivation").noInsecureKeyDerivation},get"no-insecure-rsa-padding"(){return require("./rules/no-insecure-rsa-padding").noInsecureRsaPadding},get"no-math-random-crypto"(){return require("./rules/no-math-random-crypto").noMathRandomCrypto},get"no-self-signed-certs"(){return require("./rules/no-self-signed-certs").noSelfSignedCerts},get"no-sha1-hash"(){return require("./rules/no-sha1-hash").noSha1Hash},get"no-static-iv"(){return require("./rules/no-static-iv").noStaticIv},get"no-timing-unsafe-compare"(){return require("./rules/no-timing-unsafe-compare").noTimingUnsafeCompare},get"no-weak-cipher-algorithm"(){return require("./rules/no-weak-cipher-algorithm").noWeakCipherAlgorithm},get"no-weak-hash-algorithm"(){return require("./rules/no-weak-hash-algorithm").noWeakHashAlgorithm},get"prefer-native-crypto"(){return require("./rules/prefer-native-crypto").preferNativeCrypto}};exports.plugin={meta:{name:"eslint-plugin-node-security",version:"4.10.0"},rules:exports.rules};const recommendedRules={"node-security/detect-child-process":"error","node-security/detect-eval-with-expression":"error","node-security/detect-non-literal-fs-filename":"warn","node-security/no-unsafe-dynamic-require":"error","node-security/no-buffer-overread":"warn","node-security/no-deprecated-buffer":"error","node-security/no-unsafe-buffer-alloc":"warn","node-security/no-toctou-vulnerability":"error","node-security/no-zip-slip":"error","node-security/no-arbitrary-file-access":"error","node-security/no-data-in-temp-storage":"error","node-security/no-ssrf":"warn","node-security/no-shell-injection":"error","node-security/no-dynamic-command-string":"error","node-security/no-dynamic-algorithm-selection":"error","node-security/no-timing-unsafe-compare":"warn","node-security/detect-suspicious-dependencies":"warn","node-security/lock-file":"warn","node-security/require-dependency-integrity":"error","node-security/no-weak-hash-algorithm":"error","node-security/no-weak-cipher-algorithm":"error","node-security/no-static-iv":"error","node-security/no-ecb-mode":"error","node-security/no-math-random-crypto":"error","node-security/no-cryptojs":"error","node-security/no-self-signed-certs":"error"};exports.configs={recommended:{plugins:{"node-security":exports.plugin},rules:recommendedRules},strict:{plugins:{"node-security":exports.plugin},rules:Object.fromEntries(Object.keys(exports.rules).map(ruleName=>[`node-security/${ruleName}`,"error"]))}};exports.default=exports.plugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.detectSuspiciousDependencies=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.detectSuspiciousDependencies=(0,eslint_devkit_1.createRule)({name:"detect-suspicious-dependencies",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/detect-suspicious-dependencies.md",description:"Detect typosquatting in package names",cwe:"CWE-506",cvss:7.5},messages:{violationDetected:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Suspicious Dependency",cwe:"CWE-506",description:"Suspicious package name detected - possible typosquatting",severity:"HIGH",fix:"Verify package authenticity on npm registry",documentationLink:"https://cwe.mitre.org/data/definitions/506.html"})},schema:[]},defaultOptions:[],create(context){const popularPackages=["react","lodash","express","axios","webpack"];function levenshtein(a,b){const matrix=[];for(let i=0;i<=b.length;i++){matrix[i]=[i]}for(let j=0;j<=a.length;j++){matrix[0][j]=j}for(let i=1;i<=b.length;i++){for(let j=1;j<=a.length;j++){if(b.charAt(i-1)===a.charAt(j-1)){matrix[i][j]=matrix[i-1][j-1]}else{matrix[i][j]=Math.min(matrix[i-1][j-1]+1,matrix[i][j-1]+1,matrix[i-1][j]+1)}}}return matrix[b.length][a.length]}return{ImportDeclaration(node){const source=node.source.value;if(typeof source==="string"&&!source.startsWith(".")&&!source.startsWith("@")){for(const popular of popularPackages){const distance=levenshtein(source,popular);if(distance
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.detectSuspiciousDependencies=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.detectSuspiciousDependencies=(0,eslint_devkit_1.createRule)({name:"detect-suspicious-dependencies",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/detect-suspicious-dependencies.md",description:"Detect typosquatting in package names",cwe:"CWE-506",cvss:7.5},messages:{violationDetected:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Suspicious Dependency",cwe:"CWE-506",description:"Suspicious package name detected - possible typosquatting",severity:"HIGH",fix:"Verify package authenticity on npm registry",documentationLink:"https://cwe.mitre.org/data/definitions/506.html"})},schema:[]},defaultOptions:[],create(context){const popularPackages=["react","lodash","express","axios","webpack"];const KNOWN_LEGITIMATE=new Set(["preact","recast","react-dom","reactor","redux","lodash-es","expressive","axios-retry","webpack-cli"]);function levenshtein(a,b){const matrix=[];for(let i=0;i<=b.length;i++){matrix[i]=[i]}for(let j=0;j<=a.length;j++){matrix[0][j]=j}for(let i=1;i<=b.length;i++){for(let j=1;j<=a.length;j++){if(b.charAt(i-1)===a.charAt(j-1)){matrix[i][j]=matrix[i-1][j-1]}else{matrix[i][j]=Math.min(matrix[i-1][j-1]+1,matrix[i][j-1]+1,matrix[i-1][j]+1);if(i>1&&j>1&&b.charAt(i-1)===a.charAt(j-2)&&b.charAt(i-2)===a.charAt(j-1)){matrix[i][j]=Math.min(matrix[i][j],matrix[i-2][j-2]+1)}}}}return matrix[b.length][a.length]}return{ImportDeclaration(node){const source=node.source.value;if(typeof source==="string"&&!source.startsWith(".")&&!source.startsWith("@")){for(const popular of popularPackages){const distance=levenshtein(source,popular);if(distance===1&&!KNOWN_LEGITIMATE.has(source)){context.report({node,messageId:"violationDetected",data:{name:source,similar:popular}})}}}}}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.lockFile=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.lockFile=(0,eslint_devkit_1.createRule)({name:"lock-file",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/lock-file.md",description:"Ensure package lock file exists for the configured package manager",cwe:"CWE-829",cvss:7.5},messages:{violationDetected:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Lock File Missing",cwe:"CWE-829",description:"Package lock file missing ({{ lockFile }}) for {{ packageManager }}. Commit the lock file to ensure supply chain integrity.",severity:"HIGH",fix:"Generate and commit the {{ lockFile }} file.",documentationLink:"https://cwe.mitre.org/data/definitions/829.html"})},schema:[{type:"object",properties:{packageManager:{type:"string",enum:["npm","yarn","pnpm"],description:"Package manager whose lock file is required"}},additionalProperties:false}]},defaultOptions:[{}],create(context){const fs=require("node:fs");const path=require("node:path");
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.lockFile=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const reportedRoots=new Set;exports.lockFile=(0,eslint_devkit_1.createRule)({name:"lock-file",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/lock-file.md",description:"Ensure package lock file exists for the configured package manager",cwe:"CWE-829",cvss:7.5},messages:{violationDetected:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Lock File Missing",cwe:"CWE-829",description:"Package lock file missing ({{ lockFile }}) for {{ packageManager }}. Commit the lock file to ensure supply chain integrity.",severity:"HIGH",fix:"Generate and commit the {{ lockFile }} file.",documentationLink:"https://cwe.mitre.org/data/definitions/829.html"})},schema:[{type:"object",properties:{packageManager:{type:"string",enum:["npm","yarn","pnpm"],description:"Package manager whose lock file is required"}},additionalProperties:false}]},defaultOptions:[{}],create(context){const fs=require("node:fs");const path=require("node:path");const options=context.options[0]||{};const userPackageManager=options.packageManager;const lockFiles={npm:"package-lock.json",yarn:"yarn.lock",pnpm:"pnpm-lock.yaml"};const targetLockFiles=userPackageManager?[lockFiles[userPackageManager]]:Object.values(lockFiles);const targetLockFile=userPackageManager?lockFiles[userPackageManager]:"package-lock.json | yarn.lock | pnpm-lock.yaml";const reportedManager=userPackageManager??"any";return{Program(node){let dir=path.dirname(context.filename);let found=false;for(let i=0;i<10;i++){for(const lf of targetLockFiles){if(fs.existsSync(path.join(dir,lf))){found=true;break}}if(found)break;const parentDir=path.dirname(dir);if(parentDir===dir)break;dir=parentDir}if(!found){let root=path.dirname(context.filename);let manifest=false;for(let i=0;i<10;i++){if(fs.existsSync(path.join(root,"package.json"))){manifest=true;break}const parent=path.dirname(root);if(parent===root)break;root=parent}if(!manifest)return;if(reportedRoots.has(root))return;reportedRoots.add(root);context.report({node,messageId:"violationDetected",data:{packageManager:reportedManager,lockFile:targetLockFile}})}}}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noTimingUnsafeCompare=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const DEFAULT_SECRET_PATTERNS=["token","secret","password","hash","signature","mac","hmac","digest","apiKey","api_key","api-key","auth","credential","bearer","jwt","csrf","nonce","ssn","social_security","social-security","pii","private_key","private-key","privateKey","access_token","access-token","accessToken","refresh_token","refresh-token","refreshToken","session_id","session-id","sessionId","auth_token","auth-token","authToken","encryption_key","encryption-key","encryptionKey"];function
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noTimingUnsafeCompare=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const DEFAULT_SECRET_PATTERNS=["token","secret","password","hash","signature","mac","hmac","digest","apiKey","api_key","api-key","auth","credential","bearer","jwt","csrf","nonce","ssn","social_security","social-security","pii","private_key","private-key","privateKey","access_token","access-token","accessToken","refresh_token","refresh-token","refreshToken","session_id","session-id","sessionId","auth_token","auth-token","authToken","encryption_key","encryption-key","encryptionKey"];function isSourceConstant(node){if(node.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.name==="undefined")return true;if(node.type===eslint_devkit_1.AST_NODE_TYPES.Literal)return true;if(node.type===eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral)return node.expressions.length===0;return false}const BOOLEAN_PREDICATE_NAME=/^(?:is|has|should|can|did|was|will|does)[A-Z]/;const NAMED_CONSTANT=/^[A-Z][A-Z0-9_]*$/;const NAMESPACE_NAME=/^(?:[A-Z][A-Z0-9_]*|[A-Z][a-zA-Z0-9]*)$/;function isNamedConstant(node){if(node.type!==eslint_devkit_1.AST_NODE_TYPES.MemberExpression)return false;if(node.computed)return false;if(node.object.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return false;if(node.property.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier)return false;return NAMESPACE_NAME.test(node.object.name)&&NAMED_CONSTANT.test(node.property.name)}exports.noTimingUnsafeCompare=(0,eslint_devkit_1.createRule)({name:"no-timing-unsafe-compare",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-timing-unsafe-compare.md",description:"Disallow timing-unsafe comparison of secrets",cwe:"CWE-208",cvss:7.5},hasSuggestions:true,messages:{timingUnsafeCompare:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Timing-unsafe comparison",cwe:"CWE-208",description:"Using === to compare secrets enables timing attacks. The comparison short-circuits on first mismatch, leaking information about the secret.",severity:"HIGH",fix:"Use crypto.timingSafeEqual() for constant-time comparison",documentationLink:"https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b"}),useTimingSafeEqual:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use timingSafeEqual",description:"Use constant-time comparison to prevent timing attacks",severity:"LOW",fix:"crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))",documentationLink:"https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b"})},schema:[{type:"object",properties:{secretPatterns:{type:"array",items:{type:"string"},default:DEFAULT_SECRET_PATTERNS,description:"Variable name patterns that indicate secrets"}},additionalProperties:false}]},defaultOptions:[{secretPatterns:DEFAULT_SECRET_PATTERNS}],create(context,[options={}]){const{secretPatterns=DEFAULT_SECRET_PATTERNS}=options;const patterns=secretPatterns.map(p=>new RegExp(p,"i"));function nameLooksSecret(name){if(BOOLEAN_PREDICATE_NAME.test(name))return false;return patterns.some(p=>p.test(name))}function isSecretIdentifier(node){if(node.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return nameLooksSecret(node.name)}if(node.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression){const prop=node.property;if(prop.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return nameLooksSecret(prop.name)}}return false}function checkBinaryExpression(node){if(node.operator!=="==="&&node.operator!=="=="&&node.operator!=="!=="&&node.operator!=="!="){return}if(isSourceConstant(node.left)||isSourceConstant(node.right)){return}if(isNamedConstant(node.left)||isNamedConstant(node.right)){return}const leftIsSecret=isSecretIdentifier(node.left);const rightIsSecret=isSecretIdentifier(node.right);if(leftIsSecret||rightIsSecret){context.report({node,messageId:"timingUnsafeCompare",suggest:[{messageId:"useTimingSafeEqual",fix:()=>null}]})}}return{BinaryExpression:checkBinaryExpression}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noWeakHashAlgorithm=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const WEAK_HASH_PATTERNS=[{pattern:/\bmd5\b/i,name:"MD5",alternatives:["SHA-256","SHA-512","SHA-3"],replacement:"sha256"},{pattern:/\bmd4\b/i,name:"MD4",alternatives:["SHA-256","SHA-512","SHA-3"],replacement:"sha256"},{pattern:/\bsha1\b/i,name:"SHA-1",alternatives:["SHA-256","SHA-512","SHA-3"],replacement:"sha256"},{pattern:/\bripemd\b/i,name:"RIPEMD",alternatives:["SHA-256","SHA-512"],replacement:"sha256"}];function findWeakHash(value,additionalPatterns){for(const pattern of WEAK_HASH_PATTERNS){if(pattern.pattern.test(value)){return pattern}}for(const additionalPattern of additionalPatterns){const regex=new RegExp(`\\b${additionalPattern}\\b`,"i");if(regex.test(value)){return{pattern:regex,name:additionalPattern.toUpperCase(),alternatives:["SHA-256","SHA-512"],replacement:"sha256"}}}return null}exports.noWeakHashAlgorithm=(0,eslint_devkit_1.createRule)({name:"no-weak-hash-algorithm",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-weak-hash-algorithm.md",description:"Disallow weak hash algorithms (MD5, SHA1, MD4)",cwe:"CWE-327",cvss:7.5},hasSuggestions:true,messages:{weakHashAlgorithm:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Weak hash algorithm",cwe:"CWE-327",description:"Use of weak hash algorithm: {{algorithm}}. {{algorithm}} is cryptographically broken and unsuitable for security purposes.",severity:"CRITICAL",fix:'Replace with {{replacement}}: crypto.createHash("{{replacement}}").update(data)',documentationLink:"https://owasp.org/www-community/vulnerabilities/Weak_Cryptography"}),useSha256:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use SHA-256",description:"Replace with SHA-256 for secure hashing",severity:"LOW",fix:'crypto.createHash("sha256").update(data)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatehashmethod-options"}),useSha512:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use SHA-512",description:"Replace with SHA-512 for stronger hashing",severity:"LOW",fix:'crypto.createHash("sha512").update(data)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatehashmethod-options"}),useSha3:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use SHA-3",description:"Replace with SHA-3 for latest standard",severity:"LOW",fix:'crypto.createHash("sha3-256").update(data)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatehashmethod-options"})},schema:[{type:"object",properties:{additionalWeakAlgorithms:{type:"array",items:{type:"string"},default:[],description:"Additional weak algorithms to detect"},allowInTests:{type:"boolean",default:false,description:"Allow weak hashes in test files"}},additionalProperties:false}]},defaultOptions:[{additionalWeakAlgorithms:[],allowInTests:false}],create(context,[options={}]){const{additionalWeakAlgorithms=[],allowInTests=false}=options;const filename=context.filename;const isTestFile=allowInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);function checkCallExpression(node){if(isTestFile)return;if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&node.callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.property.name==="createHash"){checkHashArgument(node)}if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.name==="createHash"){checkHashArgument(node)}if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){const funcName=node.callee.name.toLowerCase();if(funcName==="sha1"||funcName==="md5"||funcName==="md4"){const weakPattern=findWeakHash(funcName,additionalWeakAlgorithms);context.report({node,messageId:"weakHashAlgorithm",data:{algorithm:weakPattern.name,replacement:weakPattern.replacement},suggest:[{messageId:"useSha256",fix:fixer=>{if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return fixer.replaceText(node.callee,"sha256")}return null}}]})}}}function checkHashArgument(node){for(const arg of node.arguments){if(arg.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&typeof arg.value==="string"){const weakPattern=findWeakHash(arg.value,additionalWeakAlgorithms);if(weakPattern){context.report({node:arg,messageId:"weakHashAlgorithm",data:{algorithm:weakPattern.name,replacement:weakPattern.replacement},suggest:[{messageId:"useSha256",fix:fixer=>fixer.replaceText(arg,`"sha256"`)},{messageId:"useSha512",fix:fixer=>fixer.replaceText(arg,`"sha512"`)},{messageId:"useSha3",fix:fixer=>fixer.replaceText(arg,`"sha3-256"`)}]})}}}}return{CallExpression:checkCallExpression}}});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noWeakHashAlgorithm=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const WEAK_HASH_PATTERNS=[{pattern:/\bmd5\b/i,name:"MD5",alternatives:["SHA-256","SHA-512","SHA-3"],replacement:"sha256"},{pattern:/\bmd4\b/i,name:"MD4",alternatives:["SHA-256","SHA-512","SHA-3"],replacement:"sha256"},{pattern:/\bsha1\b/i,name:"SHA-1",alternatives:["SHA-256","SHA-512","SHA-3"],replacement:"sha256"},{pattern:/\bripemd\b/i,name:"RIPEMD",alternatives:["SHA-256","SHA-512"],replacement:"sha256"}];const DEFAULT_NON_CRYPTOGRAPHIC_NAMES=["sha","etag","cachekey","cachebuster"];function normalizeName(name){return name.replaceAll(/[_-]/g,"").toLowerCase()}function assignedName(node){let current=node;let parent=current.parent;while(parent){if(parent.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&parent.object===current){current=parent;parent=current.parent;continue}if(parent.type===eslint_devkit_1.AST_NODE_TYPES.CallExpression&&parent.callee===current){current=parent;parent=current.parent;continue}break}if(!parent)return null;if(parent.type===eslint_devkit_1.AST_NODE_TYPES.VariableDeclarator&&parent.init===current){return parent.id.type===eslint_devkit_1.AST_NODE_TYPES.Identifier?parent.id.name:null}if(parent.type===eslint_devkit_1.AST_NODE_TYPES.AssignmentExpression&&parent.right===current){const target=parent.left;if(target.type===eslint_devkit_1.AST_NODE_TYPES.Identifier)return target.name;if(target.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&!target.computed&&target.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return target.property.name}return null}if(parent.type===eslint_devkit_1.AST_NODE_TYPES.Property&&parent.value===current){if(parent.computed)return null;if(parent.key.type===eslint_devkit_1.AST_NODE_TYPES.Identifier)return parent.key.name;if(parent.key.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&typeof parent.key.value==="string"){return parent.key.value}return null}return null}function findWeakHash(value,additionalPatterns){for(const pattern of WEAK_HASH_PATTERNS){if(pattern.pattern.test(value)){return pattern}}for(const additionalPattern of additionalPatterns){const regex=new RegExp(`\\b${additionalPattern}\\b`,"i");if(regex.test(value)){return{pattern:regex,name:additionalPattern.toUpperCase(),alternatives:["SHA-256","SHA-512"],replacement:"sha256"}}}return null}exports.noWeakHashAlgorithm=(0,eslint_devkit_1.createRule)({name:"no-weak-hash-algorithm",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-weak-hash-algorithm.md",description:"Disallow weak hash algorithms (MD5, SHA1, MD4)",cwe:"CWE-327",cvss:7.5},hasSuggestions:true,messages:{weakHashAlgorithm:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Weak hash algorithm",cwe:"CWE-327",description:"Use of weak hash algorithm: {{algorithm}}. {{algorithm}} is cryptographically broken and unsuitable for security purposes.",severity:"CRITICAL",fix:'Replace with {{replacement}}: crypto.createHash("{{replacement}}").update(data). If this hash is an identifier rather than a security control \u2014 an EVALSHA key, an ETag, a cache key \u2014 store it under one of the nonCryptographicNames instead.',documentationLink:"https://owasp.org/www-community/vulnerabilities/Weak_Cryptography"}),useSha256:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use SHA-256",description:"Replace with SHA-256 for secure hashing",severity:"LOW",fix:'crypto.createHash("sha256").update(data)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatehashmethod-options"}),useSha512:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use SHA-512",description:"Replace with SHA-512 for stronger hashing",severity:"LOW",fix:'crypto.createHash("sha512").update(data)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatehashmethod-options"}),useSha3:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use SHA-3",description:"Replace with SHA-3 for latest standard",severity:"LOW",fix:'crypto.createHash("sha3-256").update(data)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatehashmethod-options"})},schema:[{type:"object",properties:{additionalWeakAlgorithms:{type:"array",items:{type:"string"},default:[],description:"Additional weak algorithms to detect"},allowInTests:{type:"boolean",default:false,description:"Allow weak hashes in test files"},nonCryptographicNames:{type:"array",items:{type:"string"},default:DEFAULT_NON_CRYPTOGRAPHIC_NAMES,description:"Assignment target names that mark a hash as an identifier rather than a security control"}},additionalProperties:false}]},defaultOptions:[{additionalWeakAlgorithms:[],allowInTests:false,nonCryptographicNames:DEFAULT_NON_CRYPTOGRAPHIC_NAMES}],create(context,[options={}]){const{additionalWeakAlgorithms=[],allowInTests=false,nonCryptographicNames=DEFAULT_NON_CRYPTOGRAPHIC_NAMES}=options;const filename=context.filename;const isTestFile=allowInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);const nonCryptoNames=new Set(nonCryptographicNames.map(normalizeName));function isNonCryptographicUse(node){const name=assignedName(node);return name!==null&&nonCryptoNames.has(normalizeName(name))}function checkCallExpression(node){if(isTestFile)return;if(isNonCryptographicUse(node))return;if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&node.callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.property.name==="createHash"){checkHashArgument(node)}if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.name==="createHash"){checkHashArgument(node)}if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){const funcName=node.callee.name.toLowerCase();if(funcName==="sha1"||funcName==="md5"||funcName==="md4"){const weakPattern=findWeakHash(funcName,additionalWeakAlgorithms);context.report({node,messageId:"weakHashAlgorithm",data:{algorithm:weakPattern.name,replacement:weakPattern.replacement},suggest:[{messageId:"useSha256",fix:fixer=>{if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){return fixer.replaceText(node.callee,"sha256")}return null}}]})}}}function checkHashArgument(node){for(const arg of node.arguments){if(arg.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&typeof arg.value==="string"){const weakPattern=findWeakHash(arg.value,additionalWeakAlgorithms);if(weakPattern){context.report({node:arg,messageId:"weakHashAlgorithm",data:{algorithm:weakPattern.name,replacement:weakPattern.replacement},suggest:[{messageId:"useSha256",fix:fixer=>fixer.replaceText(arg,`"sha256"`)},{messageId:"useSha512",fix:fixer=>fixer.replaceText(arg,`"sha512"`)},{messageId:"useSha3",fix:fixer=>fixer.replaceText(arg,`"sha3-256"`)}]})}}}}return{CallExpression:checkCallExpression}}});
|