Skip to content

Commit

Permalink
Merge pull request #10 from tintinweb/clarifyFilterSelection
Browse files Browse the repository at this point in the history
Rework grep pattern selection
  • Loading branch information
tintinweb authored Mar 7, 2022
2 parents 6b0834b + b4d973d commit d3e5c77
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 72 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Change Log
All notable changes will be documented in this file.

## v0.0.9
- fix: allow `solgrep <path> --find <pattern> <pathN> <pathNN>
- update: new parser
- update: reworked parts of the grep pattern selection

## v0.0.8
- fix: support for old JS #7

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ Extract all Contract names? 👉
⇒ solgrep <folder> --find="contract.name"
```

Extract all Interface names? 👉

```javascript
⇒ solgrep <folder> --find="contract.name && contract.kind=='interface'"
```

Match against something in the `AST`? 👉

```javascript
Expand Down
12 changes: 9 additions & 3 deletions bin/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -139,15 +146,14 @@ 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(" ────────────────────────────")
console.log(sgrep.findings)
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();
Expand Down
40 changes: 20 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "solgrep",
"version": "0.0.8",
"version": "0.0.9",
"description": "",
"main": "src/index.js",
"bin": {
Expand Down
111 changes: 66 additions & 45 deletions src/rules/builtin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand All @@ -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);
}
}
});
});
}
});
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/solgrep.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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) {
Expand Down

0 comments on commit d3e5c77

Please sign in to comment.