Skip to content

Commit

Permalink
feat(driver): Add --detectors option to quickly run Misti with spec…
Browse files Browse the repository at this point in the history
…ified detectors
  • Loading branch information
jubnzv committed Aug 28, 2024
1 parent 97c04bc commit 1c30505
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Public API to handle Tact stdlib paths
- Detector templates and the `--new-detector` CLI option: PR [#105](https://github.com/nowarp/misti/pull/105)
- A script to generate detectors documentation: `./scripts/generateDetectorDocs.ts`
- The `--detectors` CLI option can be used to quickly run Misti with the specified detectors, e.g., `yarn misti --detectors ReadOnlyVariables,./examples/implicit-init/implicitInit.ts:ImplicitInit`

### Changed

Expand Down
24 changes: 23 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,31 @@ export function createMistiCommand(): Command {
)
.option("--verbose", "Enable verbose output.", false)
.option("--quiet", "Suppress output.", false)
.option(
"--detectors <name|path:name>",
[
"A comma-separated list of detectors to enable.",
"If set, these detectors will override those specified in the configuration file.",
"Format: `<name>` for built-in detectors (e.g., `ReadOnlyVariables`), and `<path:name>` for custom detectors (e.g., `./examples/implicit-init/implicitInit.ts:ImplicitInit`).",
].join(" "),
(value) => {
const detectors = value
.split(",")
.filter((detector) => detector.trim() !== "");
if (detectors.length === 0) {
throw new Error(
"The --detectors option requires a non-empty list of detector names.",
);
}
return detectors;
},
)
.option(
"--all-detectors",
"Enable all the available built-in detectors.",
[
"Enable all the available built-in detectors.",
"If set, this option will override those detectors specified in the configuration file.",
].join(" "),
false,
)
.option("--config <path>", "Path to Misti configuration file")
Expand Down
14 changes: 14 additions & 0 deletions src/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class Driver {
tactStdlibPath?: string,
verbose?: boolean,
quiet?: boolean,
detectors?: string[],
allDetectors?: boolean,
mistiConfigPath?: string,
) {
Expand All @@ -71,6 +72,7 @@ export class Driver {
tactStdlibPath,
verbose,
quiet,
detectors,
allDetectors,
singleContractPath: singleContract ? tactPath : undefined,
souffleAvailable: this.checkSouffleInstallation(
Expand Down Expand Up @@ -102,6 +104,7 @@ export class Driver {
options.tactStdlibPath,
options.verbose,
options.quiet,
options.detectors,
options.allDetectors,
options.config,
);
Expand Down Expand Up @@ -183,6 +186,9 @@ export class Driver {
});
// Wait for all detectors to be initialized
this.detectors = await Promise.all(detectorPromises);
this.ctx.logger.debug(
`Enabled detectors (${this.detectors.length}): ${this.detectors.map((d) => d.id).join(", ")}`,
);
}

/**
Expand Down Expand Up @@ -404,6 +410,8 @@ interface CLIOptions {
verbose?: boolean;
/** Suppress driver's output. */
quiet?: boolean;
/** Detectors to run that will override those specified in the configuration file if set. */
detectors?: string[];
/** Enable all the available built-in detectors no matter if they are enabled in config. */
allDetectors?: boolean;
/** Optional path to the configuration file. If provided, the analyzer uses settings from this file. */
Expand Down Expand Up @@ -435,6 +443,7 @@ export class Runner {
tactStdlibPath: undefined,
verbose: false,
quiet: false,
detectors: undefined,
allDetectors: false,
config: undefined,
},
Expand Down Expand Up @@ -493,6 +502,11 @@ export class Runner {
if (options.verbose === true && options.quiet === true) {
throw new Error(`Please choose only one option: --verbose or --quiet`);
}
if (options.allDetectors === true && options.detectors !== undefined) {
throw new Error(
`--detectors and --all-detectors cannot be used simultaneously`,
);
}
}
}

Expand Down
44 changes: 40 additions & 4 deletions src/internals/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,17 @@ export class MistiConfig {
public verbosity: "quiet" | "debug" | "default";

constructor(
params: Partial<{ configPath?: string; allDetectors: boolean }> = {},
params: Partial<{
configPath?: string;
detectors?: string[];
allDetectors: boolean;
}> = {},
) {
const { configPath = undefined, allDetectors = false } = params;
const {
configPath = undefined,
detectors = undefined,
allDetectors = false,
} = params;
let configData;
if (configPath) {
try {
Expand All @@ -52,10 +60,17 @@ export class MistiConfig {
throw err;
}
}
// Override detectors if `--detectors` is set
if (detectors !== undefined) {
configData = {
...configData,
detectors: this.createDetectorConfigs(detectors, allDetectors),
};
}
} else {
// Use default detectors if no config file is provided
configData = {
detectors: this.createDetectorsConfig(allDetectors),
detectors: this.createDetectorConfigs(detectors, allDetectors),
ignoredProjects: [],
soufflePath: undefined,
tactStdlibPath: undefined,
Expand All @@ -82,7 +97,28 @@ export class MistiConfig {
}
}

private createDetectorsConfig(allDetectors: boolean): DetectorConfig[] {
private createDetectorConfigs(
detectors: string[] | undefined,
allDetectors: boolean,
): DetectorConfig[] {
if (detectors !== undefined) {
const builtinDetectors = new Set(getAllDetectors());
return detectors.reduce<DetectorConfig[]>((acc, detector) => {
if (builtinDetectors.has(detector)) {
acc.push({ className: detector });
} else {
const parts = detector.split(":");
if (parts.length !== 2) {
throw new Error(
`Cannot find built-in or custom detector: ${detector}`,
);
}
const [modulePath, className] = parts;
acc.push({ className, modulePath });
}
return acc;
}, []);
}
return (allDetectors ? getAllDetectors : getEnabledDetectors)().map(
(name) => ({ className: name }),
);
Expand Down
5 changes: 5 additions & 0 deletions src/internals/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export class MistiContext {
* - verbose: CLI option to force verbose output.
* - quiet: CLI option to forcefully suppress output.
* - quiet: CLI option to forcefully activate all the available built-in detectors.
* - detectors: Detectors to run that will override those specified in the configuration file if set.
* - allDetectors: Enable all the available built-in detectors no matter if they are enabled in config.
* - singleContractPath: Contains path to a single contract if executed without project configuration.
* - souffleAvailable: Indicates whether a Souffle binary is available..
*/
Expand All @@ -31,6 +33,7 @@ export class MistiContext {
tactStdlibPath?: string;
verbose?: boolean;
quiet?: boolean;
detectors?: string[];
allDetectors?: boolean;
singleContractPath?: string;
souffleAvailable?: boolean;
Expand All @@ -42,13 +45,15 @@ export class MistiContext {
tactStdlibPath = undefined,
verbose = false,
quiet = false,
detectors = undefined,
allDetectors = false,
singleContractPath: singleContractPath,
souffleAvailable = false,
} = params;
this.singleContractPath = singleContractPath;
this.souffleAvailable = souffleAvailable;
this.config = new MistiConfig({
detectors,
allDetectors,
configPath: mistiConfigPath,
});
Expand Down

0 comments on commit 1c30505

Please sign in to comment.