8000 Suggested recommendations for test, dev env, supply chain and error handling by Sans-Atout · Pull Request #62 · ANSSI-FR/rust-guide · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Suggested recommendations for test, dev env, supply chain and error handling #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
4 changes: 2 additions & 2 deletions src/en/01_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Rust ecosystem for secure development.
A following chapter focuses on precautions to take when choosing and using
external libraries.
Then, recommendations about the Rust language constructs are exposed.
<!-- TODO: Finally, we introduce advices for writing
tests for a project in Rust, and for using Rust fuzzing tools.-->
Finally, we introduce advices for writing
tests for a project in Rust<!-- TODO: and for using Rust fuzzing tools-->.
A summary of recommendations presented throughout the document is listed at the
end of this guide.
2 changes: 1 addition & 1 deletion src/en/02_devenv.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,4 @@ or change the program semantics in some case.
There exist other useful tools or `cargo` subcommands for enforcing program
security whether by searching for specific code patterns or by providing
convenient commands for testing or fuzzing. They are discussed in the following
chapters, according to their goals.
chapters, according to their goals.
44 changes: 31 additions & 13 deletions src/en/03_libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,34 @@ reported to the RustSec Advisory Database.

[cargo-audit]: https://github.com/RustSec/cargo-audit

<!-- ## Unsafe code in libraries -->

<!--
<mark>TODO</mark>: `unsafe` blocks are discussed in the following chapter.
One needs to ensure that this kind of block is not misused in project
dependencies.
-->

<!--
> ### Recommendation {{#check LIBS-UNSAFE | Check for unsafe code in dependencies}}
> <mark>TODO</mark>: check that no `unsafe` blocks appear in the imported
> dependencies (with a tool?).
-->
## Checking the supply chain

Through its security working group, Rust offers a number of tools for checking the security of a program's supply-chain at library level.

### Cargo supply-chain

[Cargo-supply-chain] is the tool developed by the rust foundation's official working group, which collects all the people who can work on the libraries used by the project.

> ### Rule {{#check LIBS-SUPPLY-CHAIN | Check developers implicitly trusted}}
>
> The `cargo-supply-chain` tool may be used to find out contributors of the project dependencies.

[cargo-supply-chain]: https://github.com/rust-secure-code/cargo-supply-chain

## Unsafe code in libraries

[Cargo-geiger] is a tool maintained by the Rust security working group.
Its aim is to detect the use of the `unsafe` block in a project's supply chain. The results have three levels:

- `🔒` means that no `unsafe` usage found and the create declares #![forbid(unsafe_code)]
- `❓` means that no `unsafe` usage found and the create missing #![forbid(unsafe_code)]
- `☢️` means that `unsafe` usage found

> ### Rule {{#check LIBS-AUDIT-UNSAFE | Use fully audited libraries}}
>
> We strongly advise you to use only libraries that have been properly audited by trusted third parties or entities within your organization.
>
> Particular attention should be paid to libraries using `unsafe` code. As `unsafe` code does not benefit from the language's memory management protection mechanisms, it is more likely to contain security flaws.


[cargo-geiger]: https://github.com/geiger-rs/cargo-geiger
27 changes: 19 additions & 8 deletions src/en/04_language.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ manipulations of memory pointers, the language provides the `unsafe` keyword.
> the crate root (typically `main.rs` or `lib.rs`) to generate compilation
> errors if `unsafe` is used in the code base.

> ### Information
>
>You can also obtain the same result by adding one of the two blocks below to the `Cargo.toml` file.

```toml
[lints.rust]
unsafe_code="forbid"
Comment on lines +74 to +75

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

```

```toml
[lints.clippy]
unsafe_code = "forbid"
```
## Integer overflows

Although some verification is performed by Rust regarding potential integer
Expand Down Expand Up @@ -112,25 +125,23 @@ else { println!("{}", res); }
> specialized functions `overflowing_<op>`, `wrapping_<op>`, or the
> `Wrapping` type must be used.



## Error handling

<!-- <mark>TODO</mark>: explicit good practices in error handling. -->

The `Result` type is the preferred way of handling functions that can fail.
A `Result` object must be tested, and never ignored.

### Custom Error type implementation

> ### Recommendation {{#check LANG-ERRWRAP | Implement custom `Error` type, wrapping all possible errors}}
>
> A crate can implement its own `Error` type, wrapping all possible errors.
> It must be careful to make this type exception-safe (RFC 1236), and implement
> `Error + Send + Sync + 'static` as well as `Display`.

> ### Recommendation {{#check LANG-ERRDO | Use the `?` operator and do not use the `try!` macro}}
>
> The `?` operator should be used to improve readability of code.
> The `try!` macro should not be used.
To ensure that the above recommendation is implemented correctly, you may check
the [test implementing trait](08_testfuzz.md#implementing-a-trait) section of this guide.

### Third-party library use

Third-party crates may be used to facilitate error handling. Most of them
(notably [failure], [snafu], [thiserror]) address the creation of new custom
Expand Down
119 changes: 117 additions & 2 deletions src/en/08_testfuzz.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,125 @@

## Writing tests

<mark>TODO</mark>: good practices in writing tests.
Rust offers two types of test built in by default: internal tests and integration tests.
In this section, we will discuss these two types of test as well as a rather special type of test, which is the trait implementation test.

## Fuzzing
> Recommendation {{#check TEST-DRIVEN-DEV | Adopt a test-driven development's method}}
>
> One of the best development habits is to start development by writing the set of tests to which the functionality must respond.

### Internal

Internal tests define all the tests present in the `src/` folder of a Rust project. They have the great advantage of being able to test all the functions (even private ones) if they are placed in the same file as the project.


> ### Recommendation {{#check TEST-UNIT | Testing the critical path of your code}}
> It is important to test the entire critical path of your application.
>
> This way, if a future modification causes a side effect that alters its behavior, you will notice it much sooner.

```rust
// private function
fn my_function(){
... // Your code here
}

#[cfg(test)]
mod tests{
#[test]
fn test_my_function(){
... // Your tests here
}
}
```

> ### Recommendation {{#check TEST-IGNORE | Limit the number of ignored tests}}
>
> It is recommended to limit the number of tests that will be ignored as much as possible.

Rust has an attribute system that allows part of the code to be compiled only when necessary.
This makes it possible to define code that will only be compiled when a particular feature is requested.

One of the basic features of any project is `test`. This allows you to describe code which will only be present when the code is compiled for testing (via the `cargo test` command).

To do this, add the `#[cfg(test)]` attribute to the line above the function or module concerned:
```rust
#[cfg(test)]
mod test{

#[test]
fn test_1(){}
}
```

> ### Rules {{#check TEST-CFG | Wrap tests in a sub-module with the attribute `#[cfg(test)]`}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should explain the rationale

in particular to not leak test helpers into binaries

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to admit I'm not sure if this is what was expected. The recommendation seems so natural to me that I find it complex to give a more detailed explanation.
But I tried to do this in commit [ca549e3]

>
> All internal tests must be wrapped in a sub-module with the `#[cfg(test)]` attribute. Similarly, any potential functions you may develop to help these tests must also have the `#[cfg(test)]` attribute.
>
> The use of a sub-module makes it possible to bring together all the tests and functions required for their proper execution. This makes it quick and easy to ensure that the code does not end up in the final binary or library and compromise the application's security.

### Integration

> Attention
>
> This type of test is only available for crates which are libraries.

The integration tests are the set of tests in the `tests/` folder at the root of the crate.
In this folder, each `*.rs` file will be compiled as a different crate and the library tested will be used as if an external project were using it.

For example, if we were developing a library called `example`, we could run the following integration test:
```rust
use example::method_name;

#[test]
fn test_api(){
method_name();
}
```

These tests are run at the same time as all the other tests using the following command:
```bash
cargo test
```

> ### Rule {{#check TEST-IMPL | Check that the public behavior of the API is as expected}}
>
> Integration tests must ensure that the library's behavior is as expected. These tests must cover all the solution's public functions (including the import of types, functions, enums, etc.).
>
> This also ensures that the API is user-friendly.

### Implementing a trait
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will have to check on the current best practice

They more static_asserts than tests.
In particular, a simple const is now enough to make them.

const _: () = {
  const fn test_eq_trait<X: Eq>() {}
  test_eq_trait::<i32>();  // while <f32> fails
};

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a clear preference on how to implement them.
I think that these checks are still tests because I can't find their final use in code otherwise.


The example below is used to create a test to ensure that a struct or enum implements a trait.

These tests are a little unusual. If positioned in a project, they can prevent the project from compiling if they are not valid.

Here is an example of code used to ensure that an enum has the Send and Sync traits:

```rust
enum Example {}

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

fn send_sync_trait<T : Sendc + Sync>(){}

#[test]
fn test_traits_impl(){
send_sync_trait::<Exemple>();
}
}
```

> ### Recommendation {{#check TEST-TRAIT | Create tests to ensure that certain traits are implemented for structures/enums}}
>
> In certain contexts, it is necessary for certain struct or enum to implement particular traits.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain why.

For instance, to check that exposed API is complete.

> In such cases, it is strongly recommended that you implement this type of test.

<!-- ## Fuzzing

### cargo-fuzz

<mark>TODO</mark>: good practices in fuzzing programs or part of programs.
-->
5 changes: 2 additions & 3 deletions src/en/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
- [Memory management](05_memory.md)
- [Type system](06_typesystem.md)
- [Foreign Function Interface](07_ffi.md)
- [Test and fuzzing](08_testfuzz.md)

[Licence](LICENCE.md)

<!-- TODO - [Test and fuzzing](08_testfuzz.md) -->
[Licence](LICENCE.md)
4 changes: 2 additions & 2 deletions src/fr/01_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ nous proposons des recommandations concernant l'utilisation des outils de
l'écosystème Rust dans un cadre sécurisé. Ensuite, nous détaillons les
précautions à prendre durant le choix et l'utilisation de bibliothèques
externes. Ensuite, les recommandations à propos des constructions du langage
sont présentées. <!-- TODO: Enfin, nous discutons de la bonne utilisation des outils de
test et de *fuzzing* pour un projet réalisé en Rust.--> Un résumé des règles et
sont présentées. Enfin, nous discutons de la bonne utilisation des outils de
test <!-- TODO: et de *fuzzing*--> pour un projet réalisé en Rust. Un résumé des règles et
recommandations est disponible à la fin de ce guide.
4 changes: 1 addition & 3 deletions src/fr/02_devenv.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ $ rustup override list
$
```

<!-- -->

> ### Règle {{#check DENV-STABLE | Utilisation de la chaîne d'outils *stable*}}
>
> Le développement d'applications sécurisées doit être mené en utilisant la
Expand Down Expand Up @@ -284,4 +282,4 @@ la sémantique d'un programme dans certains cas.
D'autres outils ou sous-commandes `cargo` utiles pour renforcer la sécurité
d'un programme existent, par exemple, en recherchant des motifs de code
particuliers. Nous en discutons dans les chapitres suivants en fonction de leurs
portées et de leurs objectifs.
portées et de leurs objectifs.
41 changes: 30 additions & 11 deletions src/fr/03_libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,37 @@ un projet.
> connues dans les dépendances d'un projet.

[cargo-audit]: https://github.com/RustSec/cargo-audit
## Vérification de la supply-chain

<!-- ## Code *unsafe* dans les bibliothèques -->
Rust propose, via son groupe de travail sur la sécurité un certain nombre d'outils permettant de s'assurer de la sécurité de la supply-chain d'un programme au niveau de ses bibliothèques.

<!--
<mark>TODO</mark>: les blocs de code `unsafe` sont discutés dans le chapitre
suivant. Le développeur a besoin de s'assurer que ces types de blocs ne sont pas
mal utilisés dans les dépendances de son projet.
-->
### Cargo supply-chain

<!--
> ### Recommandation {{#check LIBS-UNSAFE | Vérification du code *unsafe* dans les dépendances}}
[Cargo-supply-chain] est l'outil développé par le groupe de travail officiel de la fondation rust et qui collecte l'ensemble des personnes qui peuvent intervenir sur les bibliothèques utilisées par le projet.

> ### Règle {{#check LIBS-SUPPLY-CHAIN | Vérification des développeurs implicitement de confiance}}
>
> L'outil `cargo-supply-chain` devrait être utilisé afin de connaître les contributeurs des différentes dépendances que votre projet utilise.

[cargo-supply-chain]: https://github.com/rust-secure-code/cargo-supply-chain

## Code *unsafe* dans les bibliothèques

[Cargo-geiger] est un outil maintenu par le groupe de travail permettant de sécuriser Rust.
Son but est de détecter l'utilisation du block `unsafe` dans la supply-chain d'un projet. Les résultats possèdent trois
niveaux :

- `🔒` lorsqu'il n'y a pas d'utilisation du bloc `unsafe` trouvée et la ligne #![forbid(unsafe_code)] est déclarés
- `❓` lorsqu'il n'y a pas d'utilisation du bloc `unsafe` trouvée et la ligne #![forbid(unsafe_code)] est déclarés
- `☢️` = utilisation de bloc `unsafe` trouvée dans le code

> ### Règle {{#check LIBS-AUDIT-UNSAFE | Utiliser des librairies proprement auditées}}
> Il est fortement conseillé de n'utiliser que des bibliothèques qui ont été proprement auditées par des tiers de confiances ou des entités de votre organisation.
>
> <mark>TODO</mark>: vérifier qu'il n'y a pas de bloc `unsafe` dans les
> dépendances (à l'aide d'un outil ?).
-->
> Une attention toute particulière doit être donnée aux bibliothèques utilisant du code `unsafe`. En effet, les codes `unsafe` ne bénéficiant pas des mécanismes de protection de la gestion mémorielles du langage, ils sont plus susceptibles de contenir des failles de sécurités.

> Attention
>
> A ce jour, l'outil `cargo-geiger` ne prend pas en compte quand # ![forbid(unsafe_code)] est dans le fichier `Cargo.toml`.

[cargo-geiger]: https://github.com/geiger-rs/cargo-geiger
27 changes: 19 additions & 8 deletions src/fr/04_language.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ langage fournit le mot-clé `unsafe`.
> afin de générer des erreurs de compilation dans le cas ou le mot-clé `unsafe`
> est utilisé dans le projet.

> ### Information
>
>Il est également possible d'obtenir le même résultat en rajoutant l'un des deux blocs ci-dessous au fichier `Cargo.toml`.

```toml
[lints.rust]
unsafe_code="forbid"
```

```toml
[lints.clippy]
unsafe_code = "forbid"
```
## Dépassement d'entiers

Bien que des vérifications soient effectuées par Rust en ce qui concerne les
Expand Down Expand Up @@ -124,25 +137,23 @@ else { println!("{}", res); }

## Gestion des erreurs

<!--
<mark>TODO</mark>: décrire les bonnes pratiques de gestion d'erreurs.
-->

Le type `Result` est la façon privilégiée en Rust pour décrire le type de retour
des fonctions dont le traitement peut échouer. Un objet `Result` doit être
testé et jamais ignoré.

### Implémentation d'un type d'Erreur personnalisé

> ### Recommandation {{#check LANG-ERRWRAP | Mise en place d'un type `Error` personnalisé, pouvant contenir toutes les erreurs possibles}}
>
> Une *crate* peut implanter son propre type `Error` qui peut contenir toutes
> les erreurs possibles. Des précautions supplémentaires doivent être prises :
> ce type doit être *exception-safe* (RFC 1236) et implémenter les traits
> `Error + Send + Sync + 'static` ainsi que `Display`.

> ### Recommandation {{#check LANG-ERRDO | Utilisation de l'opérateur `?` et non-utilisation de la macro `try!`}}
>
> L'opérateur `?` doit être utilisé pour améliorer la lisibilité du code.
> La macro `try!` ne doit pas être utilisée.
Pour s'assurer que la recommandation ci-dessus soit bien implémenter, vous pouvez vous réferez à la section
concernant [les tests de bonnes implémentations](08_testfuzz.md#Implémentation-de-trait) des traits de ce guide.

### Utilisation de bibliothèque tierce

Des *crates* tierces peuvent être utilisées pour faciliter la gestion d'erreurs.
La plupart ([failure], [snafu], [thiserror]) proposent la création de types
Expand Down
Loading
0