Skip to content

Commit

Permalink
Merge pull request #196 from CoinFabrik/182-update-contribution-guide…
Browse files Browse the repository at this point in the history
…line-for-detectors

182 update contribution guideline for detectors
  • Loading branch information
arturoBeccar authored Sep 13, 2023
2 parents b545dde + 9b46540 commit 054f6db
Show file tree
Hide file tree
Showing 15 changed files with 340 additions and 30 deletions.
41 changes: 11 additions & 30 deletions docs/docs/contribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,28 @@ sidebar_position: 4

# Contribute

Thank you for your interest in contributing to the development of new detectors, test cases, or vulnerability classes for the Scout project. This document outlines the guidelines for contributing to these areas of the project.
Thank you for your interest in contributing to the development of new detectors.

### Getting Starting
### Getting Started

Create a new issue explaining your contribution and link a new branch to your issue.
Create a new issue on our [repository](https://github.com/CoinFabrik/scout) with the name of the new detector or test case you wish to contribute. Then, link a new branch to that issue.

If your detector or test-case does not belong to an existing vulnerability class, please include documentation of the new vulnerability class as specified in the respective section below.

You may also contribute a new detector or test-case for an existing vulnerability class. In this case only pay attention to the contribution guidelines for new detectors and test-cases.

Once you are finished with the sections below, please remember to update the Detectors table in the main `README.md` file by adding a new row with information about the new detector or test-case. Please do this before performing your pull request.
If your detector or test case doesn't belong to an existing [vulnerability class](https://coinfabrik.github.io/scout/docs/vulnerabilities#vulnerability-classes), please provide documentation for the new vulnerability class you're proposing.

> :exclamation: **Requirement**: All detectors and test cases should follow the **kebab-case** naming convention, using **lowercase and hyphens** only.
### Detectors

To contribute a new detector, please follow these steps:
To contribute a new detector:

1. Create a new readme file in the [`docs/docs/detectors`](https://github.com/CoinFabrik/scout/tree/main/docs/docs/detectors) folder with the name `<NUMBER>-<VULNERABILITY_NAME>.md`. Replace `<NUMBER>` with the appropriate number for the new detector and `<VULNERABILITY_NAME>` with a descriptive name for the vulnerability class it detects. Provide detailed documentation in the new readme file. Use as a template any of the existing detector documentations and keep the same sections and titles (e.g: [Detector documentation for integer-overflow-or-underflow](https://github.com/CoinFabrik/scout/blob/main/docs/docs/detectors/1-integer-overflow-or-underflow.md)).
1. Choose an appropriate template. Browse our templates at [`templates/detectors`](https://github.com/CoinFabrik/scout/tree/main/templates/detectors). Decide on the `early-lint` or `late-lint` template, based on whether you want to lint before or after macro expansion.

2. Add a new folder to [`detectors`](https://github.com/CoinFabrik/scout/tree/main/detectors) using the same `<VULNERABILITY_NAME>` and include all relevant files for the detector in that folder.
2. Add your modified detector files to a new folder, naming it after your detector, inside the [`detectors`](https://github.com/CoinFabrik/scout/tree/main/detectors) directory.

### Test Cases

To contribute new test cases for existing vulnerabilities, please follow these steps:

1. Create a new folder in the `test-cases` directory with a descriptive name for the vulnerability and the test case number appended at the end after a hyphen. If the vulnerability already has test cases, add the new test case to the existing folder (e.g: [Reentrancy test-cases](https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy)).

2. Create two sub-folders, one for the `vulnerable-example` and another one for the `remediated-example`. Include the necessary files for the test case.

3. If the test-case belongs to a new vulnerability class, follow first the instructions below.


### Vulnerability Classes

To contribute a new vulnerability class documentation, please follow these steps:

1. Create a new numbered section at the [bottom of the Vulnerability Classes documentation](https://github.com/CoinFabrik/scout/blob/main/docs/docs/vulnerabilities/README.md#vulnerability-classes) with the name `<NUMBER>-<VULNERABILITY__CLASS_NAME>`. Replace `<NUMBER>` with the appropriate number for the new vulnerability and `<VULNERABILITY_CLASS_NAME>` with a descriptive name.

2. Create a new readme file in the `docs/vulnerabilities` folder with the name `<NUMBER>-<VULNERABILITY_CLASS_NAME>.md`. Replace `<NUMBER>` with the appropriate number for the new vulnerability class and `<VULNERABILITY_CLASS_NAME>` with a descriptive name. Provide detailed documentation in the new readme file. Take as a reference the titles and sections of any of the existing [vulnerability class documetations](https://github.com/CoinFabrik/scout/tree/main/docs/docs/vulnerabilities).

3. Update the number of identified vulnerabilities at the [beginning of the Vulnerability Classes documentation](https://github.com/CoinFabrik/scout/blob/main/docs/docs/vulnerabilities/README.md#vulnerability-classes) to reflect the addition of the new vulnerability class.

To contribute a new test case:

1. Determine the [vulnerability class](https://coinfabrik.github.io/scout/docs/vulnerabilities#vulnerability-classes) to which your test case belongs. Then, create a new sub-folder under that class in the [`test-cases`](https://github.com/CoinFabrik/scout/tree/main/test-cases) directory. Remember to append the detector number at the end, separated by a hyphen.

2. Within this sub-folder, create two directories: `vulnerable-example` and `remediated-example`. Fill each with the relevant files for their respective test cases. If possible, incorporate integration or e2e tests. For guidance, refer to the `flipper` template in [`templates/test-case`](https://github.com/CoinFabrik/scout/tree/main/templates/test-case).
11 changes: 11 additions & 0 deletions templates/detector/early-lint/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[target.aarch64-apple-darwin]
linker = "dylint-link"

[target.x86_64-apple-darwin]
linker = "dylint-link"

[target.x86_64-unknown-linux-gnu]
linker = "dylint-link"

[target.x86_64-pc-windows-msvc]
linker = "dylint-link"
21 changes: 21 additions & 0 deletions templates/detector/early-lint/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "detector-name"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "1480cea393d0cee195e59949eabdfbcf1230f7f9" }
dylint_linting = "2.1.5"
if_chain = "1.0.2"

[dev-dependencies]
dylint_testing = "2.1.5"

[package.metadata.rust-analyzer]
rustc_private = true



3 changes: 3 additions & 0 deletions templates/detector/early-lint/rust-toolchain
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2023-01-27"
components = ["llvm-tools-preview", "rustc-dev"]
56 changes: 56 additions & 0 deletions templates/detector/early-lint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#![feature(rustc_private)]

extern crate rustc_ast;
extern crate rustc_span;

use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::visit::{FnKind, Visitor};
use rustc_ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_span::Span;

dylint_linting::declare_early_lint! {
/// ### What it does
/// Describe what the lint does.
///
/// ### Why is this bad?
/// Describe why the linted code is considered bad.
///
/// ### Example
/// ```rust
/// // example code where a warning is issued
/// ```
/// Use instead:
/// ```rust
/// // example code that does not raise a warning
/// ```
pub YOUR_LINT_NAME,
Warn,
"Short description of the lint"
}

struct YourVisitor {
// Add any fields necessary for your lint
}

impl<'ast> Visitor<'ast> for YourVisitor {
fn visit_expr(&mut self, ex: &'ast Expr) {
// Implement the logic of your lint here

// Call `walk_expr` to visit the descendants of `ex`
rustc_ast::visit::walk_expr(self, ex);
}
}

impl EarlyLintPass for YourLint {
fn check_fn(
&mut self,
cx: &EarlyContext<'_>,
fn_kind: FnKind<'_>,
_: Span,
_: rustc_ast::NodeId,
) {
// Implement check_fn and emit any necessary diagnostic messages
}
}

1 change: 1 addition & 0 deletions templates/detector/early-lint/ui/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn main() {}
Empty file.
11 changes: 11 additions & 0 deletions templates/detector/late-lint/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[target.aarch64-apple-darwin]
linker = "dylint-link"

[target.x86_64-apple-darwin]
linker = "dylint-link"

[target.x86_64-unknown-linux-gnu]
linker = "dylint-link"

[target.x86_64-pc-windows-msvc]
linker = "dylint-link"
21 changes: 21 additions & 0 deletions templates/detector/late-lint/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "detector-name"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "1480cea393d0cee195e59949eabdfbcf1230f7f9" }
dylint_linting = "2.1.5"
if_chain = "1.0.2"

[dev-dependencies]
dylint_testing = "2.1.5"

[package.metadata.rust-analyzer]
rustc_private = true



3 changes: 3 additions & 0 deletions templates/detector/late-lint/rust-toolchain
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2023-01-27"
components = ["llvm-tools-preview", "rustc-dev"]
60 changes: 60 additions & 0 deletions templates/detector/late-lint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![feature(rustc_private)]

extern crate rustc_hir;
extern crate rustc_span;

use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::intravisit::{self, FnKind, Visitor};
use rustc_hir::*;
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::Span;

dylint_linting::declare_late_lint! {
/// ### What it does
/// Describe what the lint does.
///
/// ### Why is this bad?
/// Describe why the linted code is considered bad.
///
/// ### Example
/// ```rust
/// // example code where a warning is issued
/// ```
/// Use instead:
/// ```rust
/// // example code that does not raise a warning
/// ```
pub YOUR_LINT_NAME,
Warn,
"Short description of the lint"
}

struct YourVisitor<'tcx> {
// Add any fields necessary for your lint
}

impl<'tcx> Visitor<'tcx> for YourVisitor<'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
// Implement the logic of your lint here

// Call `walk_expr` to visit the descendants of `ex`
intravisit::walk_expr(self, ex);
}

// Implement other methods of the `Visitor` trait as needed
}

impl<'tcx> LateLintPass<'tcx> for YourLint {
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
fn_kind: FnKind<'tcx>,
decl: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
span: Span,
hir_id: HirId,
) {
// Implement check_fn and emit any necessary diagnostic messages
}
}

1 change: 1 addition & 0 deletions templates/detector/late-lint/ui/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fn main() {}
Empty file.
32 changes: 32 additions & 0 deletions templates/test-case/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "flipper"
version = "0.1.0"
authors = ["[your_name] <[your_email]>"]
edition = "2021"

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",
]
ink-as-dependency = []
e2e-tests = []

[dependencies]
ink = { version = "4.2.1", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }

[dev-dependencies]
ink_e2e = "4.2.1"

[profile.dev]
overflow-checks = false

[profile.release]
overflow-checks = false
109 changes: 109 additions & 0 deletions templates/test-case/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
pub mod flipper {
#[ink(storage)]
pub struct Flipper {
value: bool,
}

impl Flipper {
/// Creates a new flipper smart contract initialized with the given value.
#[ink(constructor)]
pub fn new(init_value: bool) -> Self {
Self { value: init_value }
}

/// Flips the current value of the Flipper's boolean.
#[ink(message)]
pub fn flip(&mut self) {
self.value = !self.value;
}

/// Returns the current value of the Flipper's boolean.
#[ink(message)]
pub fn get(&self) -> bool {
self.value
}
}

#[cfg(test)]
mod tests {
use super::*;

#[ink::test]
fn default_works() {
let flipper = Flipper::new_default();
assert!(!flipper.get());
}

#[ink::test]
fn it_works() {
let mut flipper = Flipper::new(false);
assert!(!flipper.get());
flipper.flip();
assert!(flipper.get());
}
}

#[cfg(all(test, feature = "e2e-tests"))]
mod e2e_tests {
use super::*;
use ink_e2e::build_message;

type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[ink_e2e::test]
async fn it_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
// given
let constructor = FlipperRef::new(false);
let contract_acc_id = client
.instantiate("flipper", &ink_e2e::alice(), constructor, 0, None)
.await
.expect("instantiate failed")
.account_id;

let get = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.get());
let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_res.return_value(), false));

// when
let flip = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.flip());
let _flip_res = client
.call(&ink_e2e::bob(), flip, 0, None)
.await
.expect("flip failed");

// then
let get = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.get());
let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_res.return_value(), true));

Ok(())
}

#[ink_e2e::test]
async fn default_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
// given
let constructor = FlipperRef::new_default();

// when
let contract_acc_id = client
.instantiate("flipper", &ink_e2e::bob(), constructor, 0, None)
.await
.expect("instantiate failed")
.account_id;

// then
let get = build_message::<FlipperRef>(contract_acc_id.clone())
.call(|flipper| flipper.get());
let get_res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_res.return_value(), false));

Ok(())
}
}
}

0 comments on commit 054f6db

Please sign in to comment.