-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
511c75a
commit cfff11f
Showing
25 changed files
with
534 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
name: Deploy | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
tags: | ||
- 'pages-*' | ||
|
||
|
||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write # To push a branch | ||
pages: write # To push to a GitHub Pages site | ||
id-token: write # To update the deployment status | ||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: 0 | ||
- name: Install latest mdbook | ||
run: | | ||
tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') | ||
url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" | ||
mkdir mdbook | ||
curl -sSL $url | tar -xz --directory=./mdbook | ||
echo `pwd`/mdbook >> $GITHUB_PATH | ||
- name: Build Book | ||
run: | | ||
# This assumes your book is in the root of your repository. | ||
# Just add a `cd` here if you need to change to another directory. | ||
cd docs | ||
mdbook build | ||
- name: Setup Pages | ||
uses: actions/configure-pages@v4 | ||
- name: Upload artifact | ||
uses: actions/upload-pages-artifact@v3 | ||
with: | ||
# Upload entire repository | ||
path: 'docs/book' | ||
- name: Deploy to GitHub Pages | ||
id: deployment | ||
uses: actions/deploy-pages@v4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
book |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[book] | ||
authors = ["Vance Longwill"] | ||
language = "en" | ||
multilingual = false | ||
src = "src" | ||
title = "Expunge" | ||
|
||
[output.html.playground] | ||
runnable = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Summary | ||
|
||
- [Overview](./overview.md) | ||
- [Usage](./usage.md) | ||
- [Attributes](./attributes/README.md) | ||
- [Container attributes](./attributes/container_attributes.md) | ||
- [Field & variant attributes](./attributes/field_attributes.md) | ||
- [Integration with slog](./slog.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Attributes | ||
|
||
- [Container attributes](./container_attributes.md) (attributes that apply to a struct or enum declaration) | ||
- [Field & variant attributes](./field_attributes.md) (attributes that can be applied to a struct field, enum variant or field in an enum variant) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Container attributes | ||
|
||
> attributes that apply to a struct or enum declaration | ||
### `as` | ||
|
||
Provide a value that all the fields should be set to when expunged. e.g. `Default::default()` or `"<expunged>".to_string()` | ||
|
||
Example: | ||
|
||
In this example, all fields will be replaced with the string `"<redacted>"` when expunged. | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/container_as.rs}} | ||
``` | ||
|
||
### `default` | ||
|
||
Shorthand for `as = Default::default()`. All fields will be expunged using their `Default::default()` implementations. | ||
|
||
Example: | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/container_default.rs}} | ||
``` | ||
|
||
### `with` | ||
|
||
Expunge all fields using this function. | ||
|
||
It must return the same type as it takes. e.g. hash a `String` with `sha256::digest`. | ||
|
||
If you own the type, then could also implement `Expunge` directly. | ||
Using `with`, however, allows you to use different transformations for different fields of the same type. | ||
|
||
Example: | ||
|
||
In this example, fields will be replaced with their sha256 hashes. | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/container_with.rs}} | ||
``` | ||
|
||
### `allow_debug` | ||
|
||
By default, expunge provides its own `Debug` implementation. | ||
This attribute disables the default implementation, allowing the user to implement or derive their own. | ||
|
||
Example: | ||
|
||
In this example, fields will be replaced with their sha256 hashes. | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/allow_debug.rs}} | ||
``` | ||
|
||
### `slog` | ||
|
||
Integrates with slog, see [slog.md](../../slog.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Field attributes | ||
|
||
> attributes that can be applied to a struct field, enum variant or field in an enum variant | ||
### `as` | ||
|
||
Provide a value that the given field/variant should be set to when expunged. e.g. `"<expunged>".to_string()` | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/field_as.rs}} | ||
``` | ||
|
||
### `default` | ||
|
||
Shorthand for `as = Default::default()` | ||
|
||
Example: | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/field_default.rs}} | ||
``` | ||
|
||
### `with` | ||
|
||
Expunge the field/variant using this function. | ||
|
||
It must return the same type as it takes. e.g. hash a `String` with `sha256::digest` | ||
|
||
If you own the type, then could also implement `Expunge` directly. | ||
Using `with`, however, allows you to use different transformations for different fields of the same type. | ||
|
||
Example: | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/field_with.rs}} | ||
``` | ||
|
||
### `skip` | ||
|
||
Skips a field. Fields marked `skip` will be left as-is. This is useful when: | ||
1. You want to preserve fields within a struct that are not sensitive | ||
2. The type cannot be expunged in a meaningful way | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/field_skip.rs}} | ||
``` | ||
|
||
### `zeroize` | ||
|
||
Zeroize memory for extra security via the [secrecy](https://crates.io/crates/secrecy) & [zeroize](https://crates.io/crates/zeroize) crates. | ||
|
||
Example: | ||
|
||
```rust | ||
{{#include ../../../expunge/tests/book/field_zeroize.rs}} | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Expunge | ||
|
||
A crate for expunging sensitive fields. | ||
|
||
> **Expunge** | ||
> | ||
> 1: to strike out, obliterate, or mark for deletion | ||
> | ||
> *"In medieval and Renaissance manuscripts, a series of dots was used to mark mistakes or to label material that should be deleted from a text, and those deletion dots can help you remember the history of expunge. They were known as puncta delentia. The puncta part of the name derives from the Latin verb pungere, which can be translated as "to prick or sting" (and you can imagine that a scribe may have felt stung when their mistakes were so punctuated in a manuscript). Pungere is also an ancestor of expunge, as well as a parent of other dotted, pointed, or stinging terms such as punctuate, compunction, poignant, puncture, and pungent."* | ||
> | ||
> Source: [https://www.merriam-webster.com/dictionary/expunge](https://www.merriam-webster.com/dictionary/expunge) | ||
|
||
## About | ||
|
||
At the core of `Expunge` is the `Expunge` trait, which is used for all transformations. | ||
|
||
```rust | ||
pub trait Expunge { | ||
fn expunge(self) -> Self | ||
where | ||
Self: Sized; | ||
} | ||
``` | ||
|
||
Other crates offer similar functionality, but either require types to be changed or | ||
make it difficult for both the expunged and unexpunged data being used at runtime. | ||
|
||
This crate provides a proc_macro that derives the `Expunge` trait for the given type. | ||
When the `Expunge::expunge` method is called, sensitive fields are transformed/redacted. | ||
|
||
- All fields are transformed unless annotated with `#[expunge(skip)]` | ||
- The `Expunge` macro first looks for transformations declared on field/struct attributes i.e. `as` or `with`. | ||
If these aren't set then `Expunge` macro will use the `Expunge::expunge` implementation for the type. | ||
- A default implementation for the `Expunge` trait is provided for primitive types and common container types. | ||
These will be expunged as their default values, unless otherwise specified. | ||
|
||
Since expunge doesn't require types to be changed, migrating to this crate should be completely frictionless. | ||
|
||
## Similar crates | ||
|
||
- [secrecy](https://crates.io/crates/secrecy): Prevents secrets being logged/serialized by wrapping them in a `Secret<T>` type | ||
- [veil](https://crates.io/crates/veil): A proc_macro similar to this crate to implement expunged `std::fmt::Debug` and/or `std::fmt::Display` | ||
- [redact](https://crates.io/crates/redact): Similar to [secrecy](https://docs.rs/secrecy/latest/secrecy/), but without the memory zeroizing | ||
- [redacted](https://crates.io/crates/redacted): Wrappers to control debug formatting of potentially sensitive byte arrays | ||
|
||
|
||
### Comparison | ||
|
||
| crate | proc_macro | implements Debug | serde support | toggle on/off at runtime | uses original types | slog support | | ||
| --- | --- | --- | --- | --- | --- | --- | | ||
| [secrecy](https://crates.io/crates/secrecy) | ✘ | ✔ | ✔ | ✘ | ✘ | ✘ | | ||
| [redact](https://crates.io/crates/redact) | ✘ | ✔ | ✔ | ✘ | ✘ | ✘ | | ||
| [veil](https://crates.io/crates/veil) | ✔ | ✔ | ✘ | ✘ | ✘ | ✘ | | ||
| [redacted](https://crates.io/crates/redacted) | ✘ | ✔ | ✘ | ✘ | ✘ | ✘ | | ||
| [expunge](#Expunge) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Logging with `slog` | ||
|
||
Expunge provides a painless and (relatively) foolproof way to log structs that may contain sensitive fields. | ||
As long as your type implements `serde::Serialize`, the `slog` attribute will derive `slog::SerdeValue`. | ||
Internally the value will be expunged before logging. | ||
|
||
#### Example | ||
|
||
```rust | ||
{{#include ../../expunge/tests/book/slog.rs}} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Usage | ||
|
||
## Basic usage | ||
|
||
```rust | ||
use expunge::Expunge; | ||
use serde::{Serialize, Deserialize}; | ||
|
||
#[derive(Clone, Serialize, Deserialize, Expunge)] | ||
struct User { | ||
#[expunge(skip)] // skipped fields are not transformed | ||
id: i64, | ||
#[expunge(as = "Randy".to_string())] | ||
first_name: String, | ||
#[expunge(as = "Lahey".to_string())] | ||
last_name: String, | ||
#[expunge(with = sha256::digest)] | ||
date_of_birth: String, | ||
latitude: f64, | ||
longitude: f64, | ||
#[expunge(as = "<expunged>".to_string(), zeroize)] | ||
password_hash: String, | ||
} | ||
|
||
let user = User{ | ||
id: 101, | ||
first_name: "Ricky".to_string(), | ||
last_name: "LaFleur".to_string(), | ||
date_of_birth: "02/02/1960".to_string(), | ||
latitude: 45.0778, | ||
longitude: 63.546, | ||
password_hash: "2f089e52def4cec8b911883fecdd6d8febe9c9f362d15e3e33feb2c12f07ccc1".to_string(), | ||
}; | ||
|
||
let expunged_user = user.expunge(); | ||
|
||
let output = serde_json::to_string_pretty(&expunged_user).expect("should serialize"); | ||
|
||
assert_eq!(r#"{ | ||
"id": 101, | ||
"first_name": "Randy", | ||
"last_name": "Lahey", | ||
"date_of_birth": "eeb98c815ae11240b563892c52c8735472bb8259e9a6477e179a9ea26e7a695a", | ||
"latitude": 0.0, | ||
"longitude": 0.0, | ||
"password_hash": "<expunged>" | ||
}"#, | ||
output, | ||
) | ||
``` |
Oops, something went wrong.