diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e0b3a..93178ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log All notable changes will be documented in this file. +## v0.0.9 +- fix: allow `solgrep --find +- update: new parser +- update: reworked parts of the grep pattern selection + ## v0.0.8 - fix: support for old JS #7 diff --git a/README.md b/README.md index 45b221f..e8bdf49 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,12 @@ Extract all Contract names? 👉 ⇒ solgrep --find="contract.name" ``` +Extract all Interface names? 👉 + +```javascript +⇒ solgrep --find="contract.name && contract.kind=='interface'" +``` + Match against something in the `AST`? 👉 ```javascript diff --git a/bin/main.js b/bin/main.js index bf2c0cf..a4fdfa7 100755 --- a/bin/main.js +++ b/bin/main.js @@ -8,6 +8,7 @@ const cliProgress = require('cli-progress'); const chalk = require('chalk'); const {SolGrep, rules} = require('../src/'); +const fs = require('fs'); const argv = require('yargs') // eslint-disable-line @@ -87,7 +88,13 @@ function main(){ } if(argv.find.length){ - selectedModules.push(new rules.GenericGrep(undefined, argv.find)); + /* bug: argv parser takes everything after --find as grep pattern instead of potential paths. + fix: check if pattern is a path and add it to argv instead + */ + let paths = argv.find.filter(a => fs.existsSync(a)); + argv._.push(...paths); + + selectedModules.push(new rules.GenericGrep(undefined, argv.find.filter(a => !paths.includes(a)))); } argv.rule.forEach(r => { @@ -139,7 +146,6 @@ function main(){ //Promise.all(argv._.map(p => sgrep.analyzeDirQueue(p),this)).then(() => { Promise.all(promises).then(() => { //multibar.stop() - if(Object.keys(sgrep.findings).length) { console.log("") console.log(" ────────────────────────────") @@ -147,7 +153,7 @@ function main(){ console.log(" ────────────────────────────") } if(argv.output){ - require('fs').writeFileSync(argv.output, JSON.stringify(sgrep.findings, null, 2)); + fs.writeFileSync(argv.output, JSON.stringify(sgrep.findings, null, 2)); } sgrep.close(); diff --git a/package-lock.json b/package-lock.json index 36e37a3..c93d553 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "solgrep", - "version": "0.0.8", + "version": "0.0.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "solgrep", - "version": "0.0.8", + "version": "0.0.9", "license": "MIT", "dependencies": { "@solidity-parser/parser": "^0.14.0", @@ -22,9 +22,9 @@ } }, "node_modules/@solidity-parser/parser": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.0.tgz", - "integrity": "sha512-cX0JJRcmPtNUJpzD2K7FdA7qQsTOk1UZnFx2k7qAg9ZRvuaH5NBe5IEdBMXGlmf2+FmjhqbygJ26H8l2SV7aKQ==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.1.tgz", + "integrity": "sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw==", "dependencies": { "antlr4ts": "^0.5.0-alpha.4" } @@ -211,9 +211,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -359,9 +359,9 @@ } }, "node_modules/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", "engines": { "node": ">=12" } @@ -369,9 +369,9 @@ }, "dependencies": { "@solidity-parser/parser": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.0.tgz", - "integrity": "sha512-cX0JJRcmPtNUJpzD2K7FdA7qQsTOk1UZnFx2k7qAg9ZRvuaH5NBe5IEdBMXGlmf2+FmjhqbygJ26H8l2SV7aKQ==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.1.tgz", + "integrity": "sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw==", "requires": { "antlr4ts": "^0.5.0-alpha.4" } @@ -519,9 +519,9 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } @@ -635,9 +635,9 @@ } }, "yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" } } } diff --git a/package.json b/package.json index b2d653b..be393b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solgrep", - "version": "0.0.8", + "version": "0.0.9", "description": "", "main": "src/index.js", "bin": { diff --git a/src/rules/builtin.js b/src/rules/builtin.js index be19f20..18283d2 100644 --- a/src/rules/builtin.js +++ b/src/rules/builtin.js @@ -17,6 +17,41 @@ class BaseRule { } BaseRule.description = "N/A"; +class Pattern { + constructor(p){ + this.TYPES = ["function", "modifier", "contract", "sourceUnit"]; + this.pattern = p; + + this.TYPES.forEach(t => { + this[t] = p.includes(`${t}.`); + }, this) + + + this.isEmpty = this.TYPES.map((t) => this[t]).every(v => v === false); //static + this.onlySourceUnit = this.onlyOne("sourceUnit"); + this.onlyContract = this.onlyOne("contract"); + + } + + onlyOne(name){ + if(!this.TYPES.includes(name)){ + throw "illegal type"; + } + const checkOthersUnset = this.TYPES.filter((t) => t !== name).map((t) => this[t]).every( v => v === false); + return checkOthersUnset && this[name]; // name==true; others==false; + } + + oneOf(arr){ + for(let x of arr){ + if(!this.TYPES.includes(x)){ + throw "illegal type" + } + if (this[x]) return true; + } + return false; + } +} + class GenericGrep extends BaseRule { constructor(solgrep, patterns) { super(solgrep); @@ -35,18 +70,6 @@ class GenericGrep extends BaseRule { }).filter(p => p.length > 0); } - _getPatternType(p) { - if (p.includes("function.")) { - return "function" - } else if (p.includes("modifier.")) { - return "modifier" - } else if (p.includes("contract.")) { - return "contract" - } else if (p.includes("sourceUnit")) { - return "sourceUnit" - } - } - onProcess(sourceUnit) { let context = { sourceUnit: sourceUnit, @@ -55,60 +78,58 @@ class GenericGrep extends BaseRule { modifier: undefined } - for (let pat of this.patterns) { - var patternType = this._getPatternType(pat); - if (patternType === "sourceUnit") { - let ret = safeEval(pat, context); + let pattern = new Pattern(pat); + /* no pattern at all */ + if(pattern.isEmpty) continue; //shortcut: skip invalid pattern + + /* sourceUnit only */ + if (pattern.onlySourceUnit) { + let ret = safeEval(pattern.pattern, context); if (ret) { //allows match & extract (fuzzy) this.solgrep.report(sourceUnit, this, `match-sourceUnit`, `${ret}`, typeof ret === 'object' && ret.hasOwnKey('loc') ? ret.loc : sourceUnit.ast.loc); } - continue; //exit early + continue; //skip early } - // -- parse pattern -- - // SourceUnit + /* sourceUnit & ... */ Object.values(sourceUnit.contracts).forEach(contract => { - // update context context.contract = contract; - - if (patternType === "contract") { - let ret = safeEval(pat, context); + /* (1) contract only */ + if (pattern.onlyContract) { // if only contract in pattern, match it and return + let ret = safeEval(pattern.pattern, context); if (ret) { this.solgrep.report(sourceUnit, this, `match-contract: ${contract.name}`, `${ret}`, typeof ret === 'object' && ret.hasOwnKey('loc') ? ret.loc : contract.ast.loc); } return; } - - // Contract - contract.functions.forEach(_function => { - // Function - - //update context - context._function = _function; - - if (patternType === "function") { - let ret = safeEval(pat, context); + /* (2) contract & function */ + if(pattern.function){ + contract.functions.forEach(_function => { + // Function + + //update context + context._function = _function; + let ret = safeEval(pattern.pattern, context); if (ret) { this.solgrep.report(sourceUnit, this, `match-function: ${contract.name}.${_function.name}`, `${ret}`, typeof ret === 'object' && ret.hasOwnKey('loc') ? ret.loc : _function.ast.loc); } - } - }); - - Object.values(contract.modifiers).forEach(_modifier => { - // Modifier - //update context - context.modifier = _modifier; - - if (patternType === "modifier") { - let ret = safeEval(pat, context); + }); + } + /* (3) contract & modifier */ + if(pattern.modifier){ + Object.values(contract.modifiers).forEach(_modifier => { + // Modifier + //update context + context.modifier = _modifier; + let ret = safeEval(pattern.pattern, context); if (ret) { this.solgrep.report(sourceUnit, this, `match-modifier: ${contract.name}.${_modifier.name}`, `${ret}`, typeof ret === 'object' && ret.hasOwnKey('loc') ? ret.loc : _modifier.ast.loc); } - } - }); + }); + } }); } } diff --git a/src/solgrep.js b/src/solgrep.js index d997ad0..d5756b8 100644 --- a/src/solgrep.js +++ b/src/solgrep.js @@ -74,7 +74,7 @@ class SolGrep { return new Promise((resolve, reject) => { const files = utils.getAllDirFiles(targetDir, (f) => f.endsWith('.sol')); //sync: const numFiles = files.length; - console.log(this) + this.notify("onAnalyzeDir", targetDir, numFiles, this); /* block until all files finished */ @@ -89,17 +89,19 @@ class SolGrep { analyzeDirQueue(targetDir) { return new Promise((resolve, reject) => { - const files = utils.getAllDirFiles(targetDir, (f) => f.endsWith('.sol')); //sync: const numFiles = files.length; this.notify("onAnalyzeDir", targetDir, numFiles, this); + if(numFiles == 0){ + return resolve([]); + } const q = fastq(this, worker); q.drain = () => { this.notify("onDirAnalyzed", targetDir); this.notifyRules("onDirAnalyzed") - resolve(this.findings); + return resolve(this.findings); }; async function worker (arg, done) {