{"id":18016910,"url":"https://github.com/fitzgen/mutatis","last_synced_at":"2025-03-26T18:32:33.330Z","repository":{"id":253185189,"uuid":"840834582","full_name":"fitzgen/mutatis","owner":"fitzgen","description":"`mutatis` is a library for writing custom, structure-aware test-case mutators for fuzzers in Rust. ","archived":false,"fork":false,"pushed_at":"2025-01-14T00:31:01.000Z","size":128,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-17T11:59:42.768Z","etag":null,"topics":["fuzzing","property-based-testing"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fitzgen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-10T20:38:26.000Z","updated_at":"2025-01-14T00:31:04.000Z","dependencies_parsed_at":"2024-08-19T03:30:54.942Z","dependency_job_id":"bae0df73-3045-4b62-a0e9-433d55da53cd","html_url":"https://github.com/fitzgen/mutatis","commit_stats":null,"previous_names":["fitzgen/mutatis"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fmutatis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fmutatis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fmutatis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fmutatis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fitzgen","download_url":"https://codeload.github.com/fitzgen/mutatis/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245713150,"owners_count":20660353,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fuzzing","property-based-testing"],"created_at":"2024-10-30T04:19:40.303Z","updated_at":"2025-03-26T18:32:32.916Z","avatar_url":"https://github.com/fitzgen.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e\u003ccode\u003emutatis\u003c/code\u003e\u003c/h1\u003e\n  \u003cp\u003e\n    \u003cstrong\u003eEasily create custom, structure-aware mutators for fuzzing.\u003c/strong\u003e\n  \u003c/p\u003e\n  \u003cp\u003e\n    \u003ca href=\"https://crates.io/crates/mutatis\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/mutatis.svg\" alt=\"crates.io\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://docs.rs/mutatis\"\u003e\u003cimg src=\"https://docs.rs/mutatis/badge.svg\" alt=\"docs.rs\"\u003e\u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/rustc-stable+-green.svg\" alt=\"supported rustc stable\" /\u003e\n  \u003c/p\u003e\n  \u003ch3\u003e\n    \u003ca href=\"https://github.com/fitzgen/mutatis\"\u003eRepository\u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://docs.rs/mutatis\"\u003eDocs\u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://docs.rs/mutatis/latest/mutatis/_guide/index.html\"\u003eGuide\u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://github.com/fitzgen/mutatis/blob/main/CONTRIBUTING.md\"\u003eContributing\u003c/a\u003e\n  \u003c/h3\u003e\n\u003c/div\u003e\n\n## About\n\nThe most popular fuzzers — including [`libfuzzer`][libfuzzer] and [AFL] — are\n*coverage-guided* and *mutation-based*.\n\n[libfuzzer]: https://crates.io/crates/libfuzzer-sys\n[AFL]: https://crates.io/crates/afl\n\n*Coverage-guided* means that the fuzzer observes which code is dynamically\nexecuted while running an input through the system under test. When creating new\ninputs, it will try to make inputs that execute new code paths, maximizing the\namount of code that's been explored. If a new input triggers new code paths to\nbe executed, then it is added to the corpus. If a new input only exercises code\npaths that have already been discovered, then it is thrown away.\n\n*Mutation-based* means that, when creating a new input, the fuzzer modifies an\nexisting input from its corpus. The idea is that, if the existing input\ntriggered interesting behavior in the system under test, then a modification of\nthat input probably will as well, but might additionally trigger some new\nbehavior as well. Consider the scenario where we are fuzzing a compiler: if some\ninput made it all the way through the parser, type checker, and into code\ngeneration \u0026mdash; rather than bouncing off early due to an invalid token\n\u0026mdash; then a new input derived from this one is also likely to go deep into\nthe compiler's pipeline. At least it is more likely to do so than a completely\nnew, random string.\n\nBut what happens when we aren't fuzzing a text or binary interface? What happens\nwhen we have a custom input type that the fuzzer's built-in mutation strategies\naren't very good at targeting? Many fuzzers will expose a hook for customizing\nthe routine for mutating an existing input from its corpus to create a new\ncandidate input, for example `libfuzzer` has the [`fuzz_mutator!`][fuzz-mutator]\nhook.\n\n**`mutatis` exists to make writing these custom mutators easy and efficient.**\n\n[fuzz-mutator]: https://docs.rs/libfuzzer-sys/latest/libfuzzer_sys/macro.fuzz_mutator.html\n\n## Using Default Mutators\n\nTo randomly mutate a value with its default, off-the-shelf mutator:\n\n* Create a [`mutatis::Session`](https://docs.rs/mutatis/latest/mutatis/struct.Session.html).\n* Call\n  [`session.mutate`](https://docs.rs/mutatis/latest/mutatis/struct.Session.html#method.mutate),\n  passing in the value you wish to mutate.\n\nHere's a simple example of using `mutatis` and its default mutators to randomly\nmutate a value:\n\n```rust\n# fn foo() -\u003e mutatis::Result\u003c()\u003e {\nlet mut point = (42, 36);\n\nlet mut session = mutatis::Session::new();\nfor _ in 0..3 {\n    session.mutate(\u0026mut point)?;\n    println!(\"mutated point is {point:?}\");\n}\n\n// Example output:\n//\n//     mutated point is (-565504428, 36)\n//     mutated point is (-565504428, 49968845)\n//     mutated point is (-1854163941, 49968845)\n# Ok(())\n# }\n# foo().unwrap()\n```\n\n## Combining and Customizing Mutators\n\nYou can use the mutator combinators in the\n[`mutatis::mutators`](https://docs.rs/mutatis/latest/mutatis/mutators/index.html)\nmodule to build more complex mutators from simpler ones or to customize mutation\nstrategies to, for example, maintain a type's internal invariants or bound the\nresulting values into a particular range. The `mutatis::mutators` module is\ntypically imported under the alias `m`.\n\nTo randomly mutate a value with a custom mutator:\n\n* Create the custom mutator from\n  [`mutatis::mutators`](https://docs.rs/mutatis/latest/mutatis/mutators/)\n  combinators and `Mutate` trait adapter methods.\n* Create a [`mutatis::Session`](https://docs.rs/mutatis/latest/mutatis/struct.Session.html).\n* Call\n  [`session.mutate_with`](https://docs.rs/mutatis/latest/mutatis/struct.Session.html#method.mutate_with),\n  passing in the value you wish to mutate and the mutator you wish to use to\n  perform the mutation.\n\nHere's an example of using `mutatis` to define a custom mutator for a custom\n`struct` type that has multiple fields, and maintains a relationship between the\nfields' values:\n\n```rust\n# fn foo() -\u003e mutatis::Result\u003c()\u003e {\nuse mutatis::{mutators as m, Mutate, Session};\n\n/// A scary monster type.\n#[derive(Debug)]\npub struct Monster {\n    pos: [i32; 2],\n    hp: u16,\n\n    // Invariant: ghost's are already dead, so when `is_ghost = true` it must\n    // always be the case that `hp = 0`.\n    is_ghost: bool,\n}\n\n/// A mutator that mutates one of a monster's fields, while maintaining our\n/// invariant that ghosts always have zero HP.\nlet mut mutator =\n    // Mutate the `pos` field...\n    m::array(m::i32()).proj(|x: \u0026mut Monster| \u0026mut x.pos)\n        // ...or mutate the `hp` field...\n        .or(\n            m::u16()\n                .proj(|x: \u0026mut Monster| \u0026mut x.hp)\n                .map(|_ctx, monster| {\n                    // If we mutated the `hp` such that it is non-zero, then the\n                    // monster cannot be a ghost.\n                    if monster.hp \u003e 0 {\n                        monster.is_ghost = false;\n                    }\n                    Ok(())\n                }),\n        )\n        // ...or mutate the `is_ghost` field.\n        .or(\n            m::bool()\n                .proj(|x: \u0026mut Monster| \u0026mut x.is_ghost)\n                .map(|_ctx, monster| {\n                    // If we turned this monster into a ghost, then its `hp`\n                    // must be zero.\n                    if monster.is_ghost {\n                        monster.hp = 0;\n                    }\n                    Ok(())\n                }),\n        );\n\n// Define a monster...\nlet mut monster = Monster {\n    hp: 36,\n    is_ghost: false,\n    pos: [-8, 9000],\n};\n\n// ...and mutate it a bunch of times!\nlet mut session = Session::new();\nfor _ in 0..5 {\n    session.mutate_with(\u0026mut mutator, \u0026mut monster)?;\n    println!(\"mutated monster is {monster:?}\");\n}\n\n// Example output:\n//\n//     mutated monster is Monster { pos: [-8, -1647191276], hp: 36, is_ghost: false }\n//     mutated monster is Monster { pos: [-8, -1062708247], hp: 36, is_ghost: false }\n//     mutated monster is Monster { pos: [-8, -1062708247], hp: 61401, is_ghost: false }\n//     mutated monster is Monster { pos: [-8, -1062708247], hp: 0, is_ghost: true }\n//     mutated monster is Monster { pos: [-8, 1487274938], hp: 0, is_ghost: true }\n# Ok(())\n# }\n# foo().unwrap()\n```\n\n## Automatically Deriving Mutators with `#[derive(Mutate)]`\n\nFirst, enable this crate's `derive` feature, then slap `#[derive(Mutate)]` onto\nyour type definitions:\n\n```rust\n# fn foo() -\u003e mutatis::Result\u003c()\u003e {\n#![cfg(feature = \"derive\")]\nuse mutatis::{Mutate, Session};\n\n// An RGB color.\n#[derive(Debug)]\n#[derive(Mutate)] // Automatically derive a mutator for `Rgb`!\npub struct Rgb {\n    r: u8,\n    g: u8,\n    b: u8,\n}\n\n// Create an RGB color: chartreuse.\nlet mut color = Rgb {\n    r: 0x7f,\n    g: 0xff,\n    b: 0x00,\n};\n\n// ...and mutate it a bunch of times!\nlet mut session = Session::new();\nfor _ in 0..5 {\n    session.mutate(\u0026mut color)?;\n    println!(\"mutated color is {color:?}\");\n}\n\n// Example output:\n//\n//     mutated color is Rgb { r: 127, g: 45, b: 0 }\n//     mutated color is Rgb { r: 127, g: 134, b: 0 }\n//     mutated color is Rgb { r: 127, g: 10, b: 0 }\n//     mutated color is Rgb { r: 127, g: 10, b: 29 }\n//     mutated color is Rgb { r: 172, g: 10, b: 29 }\n# Ok(())\n# }\n# #[cfg(feature = \"derive\")] foo().unwrap()\n```\n\n## Writing Smoke Tests with `mutatis::check`\n\nWhen you enable the `check` feature in `Cargo.toml`, [the `mutatis::check`\nmodule](https://docs.rs/mutatis/latest/mutatis/check/index.html) provides a tiny\nproperty-based testing framework that is suitable for writing smoke tests that\nyou use for local development and CI. It is not intended to replace a\nfull-fledged, coverage-guided fuzzing engine that you'd use for in-depth,\ncontinuous fuzzing.\n\n```rust\n# #[cfg(feature = \"check\")]\n#[cfg(test)]\nmod tests {\n    use mutatis::check::Check;\n\n    #[test]\n    fn test_that_addition_commutes() {\n        Check::new()\n            .iters(1000)\n            .shrink_iters(1000)\n            .run(|(a, b): \u0026(i32, i32)| {\n                if a + b == b + a {\n                    Ok(())\n                } else {\n                    Err(\"addition is not commutative!\")\n                }\n            })\n            .unwrap();\n    }\n}\n```\n\nSee [the `check` module's\ndocumentation](https://docs.rs/mutatis/latest/mutatis/check/index.html) for more\ndetails.\n\n## Documentation\n\n#### API Reference Documentation\n\nThe API reference documentation is available on\n[docs.rs](https://docs.rs/mutatis).\n\n#### Guide\n\n[Check out the guide](https://docs.rs/mutatis/latest/mutatis/_guide/index.html)\nfor tutorials, discussions, and recipes; everything else that doesn't fall into\nthe API-reference category.\n\n## License\n\nLicensed under dual MIT or Apache-2.0 at your choice.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in this project by you, as defined in the Apache-2.0 license,\nshall be dual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffitzgen%2Fmutatis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffitzgen%2Fmutatis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffitzgen%2Fmutatis/lists"}