├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── crates ├── tree-crasher-c │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-css │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-html │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-javascript │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-nix │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-python │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-regex │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-ruby │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-rust │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-solidity │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-sql │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tree-crasher-typescript │ ├── Cargo.toml │ └── src │ │ └── main.rs └── tree-crasher │ ├── Cargo.toml │ └── src │ └── lib.rs ├── doc ├── .gitignore ├── README.md ├── SUMMARY.md ├── book.toml ├── contributing.md ├── dev.md ├── install.md ├── overview.md └── usage.md ├── rustc-flags └── scripts └── corpora ├── c.sh ├── css.sh ├── js.sh ├── python.sh ├── ruby.sh ├── sol.sh ├── sql.sh └── ts.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | assignees: 8 | - "langston-barrett" 9 | labels: 10 | - "infrastructure" 11 | reviewers: 12 | - "langston-barrett" 13 | 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "monthly" 18 | assignees: 19 | - "langston-barrett" 20 | labels: 21 | - "infrastructure" 22 | reviewers: 23 | - "langston-barrett" 24 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | env: 10 | # The NAME makes it easier to copy/paste snippets from other CI configs 11 | NAME: tree-crasher 12 | 13 | jobs: 14 | doc: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Cancel previous run 18 | uses: styfle/cancel-workflow-action@0.12.1 19 | with: 20 | access_token: ${{ github.token }} 21 | 22 | - uses: actions/checkout@v4 23 | 24 | - name: Setup mdBook 25 | uses: peaceiris/actions-mdbook@v2 26 | with: 27 | mdbook-version: '0.4.10' 28 | 29 | - name: Build docs 30 | run: | 31 | cd doc 32 | mdbook build 33 | 34 | - name: Push docs 35 | uses: peaceiris/actions-gh-pages@v4 36 | if: ${{ github.ref == 'refs/heads/main' }} 37 | with: 38 | github_token: ${{ secrets.GITHUB_TOKEN }} 39 | publish_dir: doc/book 40 | 41 | - name: Package docs 42 | shell: bash 43 | run: | 44 | tar -cvf doc.tar.gz doc/book/* 45 | 46 | - name: Upload docs 47 | uses: actions/upload-artifact@v4 48 | if: github.repository == 'langston-barrett/${{ env.NAME }}' 49 | with: 50 | name: "${{ env.NAME }}-docs" 51 | path: "*.tar.gz" 52 | if-no-files-found: error 53 | 54 | lint: 55 | runs-on: ubuntu-latest 56 | steps: 57 | - uses: actions/checkout@v4 58 | - name: Format 59 | run: cargo fmt && git diff --exit-code 60 | - name: Deps 61 | run: | 62 | rustup update 63 | rustup component add clippy 64 | - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 65 | - name: Lint 66 | run: cargo clippy -- --deny warnings 67 | 68 | static: 69 | runs-on: ubuntu-latest 70 | steps: 71 | - uses: actions/checkout@v4 72 | - name: Deps 73 | run: | 74 | sudo apt-get install -y musl-tools 75 | rustup target add x86_64-unknown-linux-musl 76 | - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 77 | - run: | 78 | cargo build \ 79 | --bin ${NAME}-c \ 80 | --bin ${NAME}-css \ 81 | --bin ${NAME}-javascript \ 82 | --bin ${NAME}-regex \ 83 | --bin ${NAME}-rust \ 84 | --bin ${NAME}-solidity \ 85 | --bin ${NAME}-sql \ 86 | --bin ${NAME}-typescript \ 87 | --locked \ 88 | --release \ 89 | --target=x86_64-unknown-linux-musl 90 | 91 | test: 92 | runs-on: ubuntu-latest 93 | steps: 94 | - uses: actions/checkout@v4 95 | - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 96 | - run: env RUSTFLAGS="@$PWD/rustc-flags" cargo test --locked --no-run 97 | - run: env RUSTFLAGS="@$PWD/rustc-flags" cargo test 98 | - run: env RUSTFLAGS="@$PWD/rustc-flags" cargo test --locked --no-run --features=radamsa 99 | - run: env RUSTFLAGS="@$PWD/rustc-flags" cargo test --features=radamsa 100 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | branches: 6 | - release* 7 | tags: 8 | - 'v*' 9 | 10 | env: 11 | # The NAME makes it easier to copy/paste snippets from other CI configs 12 | NAME: tree-crasher 13 | 14 | jobs: 15 | release: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - id: release 21 | uses: ncipollo/release-action@v1 22 | if: ${{ startsWith(github.ref, 'refs/tags/v') }} 23 | with: 24 | artifactErrorsFailBuild: true 25 | body: "See [CHANGELOG.md](https://github.com/langston-barrett/${{ env.NAME }}/blob/main/CHANGELOG.md)." 26 | draft: true 27 | token: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | - name: Publish to crates.io 30 | env: 31 | CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} 32 | # Only push on actual release tags 33 | PUSH: ${{ startsWith(github.ref, 'refs/tags/v') }} 34 | run: | 35 | # tree-crasher-{regex,solidity,sql} currently aren't suitable for upload to 36 | # crates.io because they have git dependencies 37 | for pkg in tree-crasher{,-c,-css,-html,-javascript,-python,-rust,-typescript}; do 38 | if [[ ${PUSH} == true ]]; then 39 | cargo publish --token ${CRATES_IO_TOKEN} -p "${pkg}" 40 | else 41 | cargo publish --dry-run --token ${CRATES_IO_TOKEN} -p "${pkg}" 42 | break 43 | fi 44 | # crates.io uses "leaky bucket" rate limiting, with a new token every 45 | # 60s. It takes a bit to build each package, so we use 50s. 46 | sleep 50 47 | done 48 | 49 | # Inspired by rustfmt: 50 | # https://github.com/rust-lang/rustfmt/blob/master/.github/workflows/upload-assets.yml 51 | artifacts: 52 | needs: release 53 | strategy: 54 | matrix: 55 | build: [linux-x86_64-gnu, linux-x86_64-musl, macos-x86_64] 56 | # build: [linux-x86_64-gnu, linux-x86_64-musl, macos-x86_64, windows-x86_64-gnu, windows-x86_64-msvc] 57 | include: 58 | - build: linux-x86_64-gnu 59 | os: ubuntu-latest 60 | rust: stable 61 | target: x86_64-unknown-linux-gnu 62 | - build: linux-x86_64-musl 63 | os: ubuntu-latest 64 | rust: stable 65 | target: x86_64-unknown-linux-musl 66 | # TODO(lb): Can these also be made stable? 67 | - build: macos-x86_64 68 | os: macos-latest 69 | rust: nightly 70 | target: x86_64-apple-darwin 71 | # TODO(lb): Currently uses unix-specific APIs. 72 | # - build: windows-x86_64-gnu 73 | # os: windows-latest 74 | # rust: nightly-x86_64-gnu 75 | # target: x86_64-pc-windows-gnu 76 | # - build: windows-x86_64-msvc 77 | # os: windows-latest 78 | # rust: nightly-x86_64-msvc 79 | # target: x86_64-pc-windows-msvc 80 | runs-on: ${{ matrix.os }} 81 | steps: 82 | - uses: actions/checkout@v4 83 | 84 | - name: Install rustup 85 | shell: bash 86 | run: | 87 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh 88 | sh rustup-init.sh -y --default-toolchain none 89 | rustup target add ${{ matrix.target }} 90 | 91 | - name: Add mingw64 to path for x86_64-gnu 92 | run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH 93 | if: matrix.rust == 'nightly-x86_64-gnu' 94 | shell: bash 95 | 96 | - name: Deps 97 | if: matrix.target == 'x86_64-unknown-linux-musl' 98 | run: | 99 | sudo apt-get install -y musl-tools 100 | 101 | - name: Build executables 102 | shell: bash 103 | run: | 104 | for bin in ${NAME}-{c,css,javascript,regex,rust,solidity,sql,typescript}; do 105 | cargo build \ 106 | --bin ${bin} \ 107 | --locked \ 108 | --release \ 109 | --target=${{ matrix.target }} 110 | cp target/${{ matrix.target }}/release/${bin} ${bin}_${{ matrix.target }} 111 | done 112 | 113 | - name: Upload binaries 114 | uses: ncipollo/release-action@v1 115 | if: ${{ startsWith(github.ref, 'refs/tags/v') }} 116 | with: 117 | allowUpdates: true 118 | artifactErrorsFailBuild: true 119 | replacesArtifacts: false 120 | artifacts: > 121 | ${{ env.NAME }}-c_${{ matrix.target }}, 122 | ${{ env.NAME }}-css_${{ matrix.target }}, 123 | ${{ env.NAME }}-javascript_${{ matrix.target }}, 124 | ${{ env.NAME }}-regex_${{ matrix.target }}, 125 | ${{ env.NAME }}-rust_${{ matrix.target }}, 126 | ${{ env.NAME }}-typescript_${{ matrix.target }}, 127 | ${{ env.NAME }}-solidity_${{ matrix.target }} 128 | ${{ env.NAME }}-sql_${{ matrix.target }} 129 | body: "See [CHANGELOG.md](https://github.com/langston-barrett/${{ env.NAME }}/blob/main/CHANGELOG.md)." 130 | draft: true 131 | token: ${{ secrets.GITHUB_TOKEN }} 132 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | tmp/ 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | 5 | ## [0.4.0] - 2023-07-17 6 | 7 | - Bump dependencies 8 | - Support Python 9 | 10 | ## [0.3.0] - 2023-04-07 11 | 12 | - Bump dependencies to fix some panics 13 | - Support Solidity 14 | 15 | ## [0.2.2] - 2023-04-01 16 | 17 | - Releases now include executables for OSX 18 | 19 | ## [0.2.1] - 2023-04-01 20 | 21 | - Bump `tree-sitter-edit` to v0.1.2 22 | 23 | ## [0.2.0] - 2023-03-22 24 | 25 | - Many more languages 26 | 27 | ## [0.1.2] - 2023-03-21 28 | 29 | - Slightly lower memory usage 30 | - Update dependencies 31 | 32 | ## [0.1.1] - 2023-03-19 33 | 34 | - Fix issue with release CI 35 | 36 | ## [0.1.0] - 2023-03-19 37 | 38 | Initial release! 39 | 40 | [0.1.0]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.1.0 41 | [0.1.1]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.1.1 42 | [0.1.2]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.1.2 43 | [0.2.0]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.2.0 44 | [0.2.1]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.2.1 45 | [0.2.2]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.2.2 46 | [0.3.0]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.3.0 47 | [0.4.0]: https://github.com/langston-barrett/tree-crasher/releases/tag/v0.4.0 48 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.0.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anstream" 16 | version = "0.6.13" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" 19 | dependencies = [ 20 | "anstyle", 21 | "anstyle-parse", 22 | "anstyle-query", 23 | "anstyle-wincon", 24 | "colorchoice", 25 | "utf8parse", 26 | ] 27 | 28 | [[package]] 29 | name = "anstyle" 30 | version = "1.0.8" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 33 | 34 | [[package]] 35 | name = "anstyle-parse" 36 | version = "0.2.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" 39 | dependencies = [ 40 | "utf8parse", 41 | ] 42 | 43 | [[package]] 44 | name = "anstyle-query" 45 | version = "1.0.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 48 | dependencies = [ 49 | "windows-sys 0.48.0", 50 | ] 51 | 52 | [[package]] 53 | name = "anstyle-wincon" 54 | version = "3.0.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 57 | dependencies = [ 58 | "anstyle", 59 | "windows-sys 0.52.0", 60 | ] 61 | 62 | [[package]] 63 | name = "anyhow" 64 | version = "1.0.98" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 67 | 68 | [[package]] 69 | name = "bindgen" 70 | version = "0.64.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" 73 | dependencies = [ 74 | "bitflags", 75 | "cexpr", 76 | "clang-sys", 77 | "lazy_static", 78 | "lazycell", 79 | "log", 80 | "peeking_take_while", 81 | "proc-macro2", 82 | "quote", 83 | "regex", 84 | "rustc-hash", 85 | "shlex", 86 | "syn 1.0.109", 87 | "which", 88 | ] 89 | 90 | [[package]] 91 | name = "bitflags" 92 | version = "1.3.2" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 95 | 96 | [[package]] 97 | name = "cc" 98 | version = "1.0.83" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 101 | dependencies = [ 102 | "libc", 103 | ] 104 | 105 | [[package]] 106 | name = "cexpr" 107 | version = "0.6.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 110 | dependencies = [ 111 | "nom", 112 | ] 113 | 114 | [[package]] 115 | name = "cfg-if" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 119 | 120 | [[package]] 121 | name = "clang-sys" 122 | version = "1.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" 125 | dependencies = [ 126 | "glob", 127 | "libc", 128 | "libloading", 129 | ] 130 | 131 | [[package]] 132 | name = "clap" 133 | version = "4.5.37" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" 136 | dependencies = [ 137 | "clap_builder", 138 | "clap_derive", 139 | ] 140 | 141 | [[package]] 142 | name = "clap-verbosity-flag" 143 | version = "3.0.2" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "2678fade3b77aa3a8ff3aae87e9c008d3fb00473a41c71fbf74e91c8c7b37e84" 146 | dependencies = [ 147 | "clap", 148 | "log", 149 | ] 150 | 151 | [[package]] 152 | name = "clap_builder" 153 | version = "4.5.37" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" 156 | dependencies = [ 157 | "anstream", 158 | "anstyle", 159 | "clap_lex", 160 | "strsim", 161 | ] 162 | 163 | [[package]] 164 | name = "clap_derive" 165 | version = "4.5.32" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 168 | dependencies = [ 169 | "heck", 170 | "proc-macro2", 171 | "quote", 172 | "syn 2.0.13", 173 | ] 174 | 175 | [[package]] 176 | name = "clap_lex" 177 | version = "0.7.4" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 180 | 181 | [[package]] 182 | name = "colorchoice" 183 | version = "1.0.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 186 | 187 | [[package]] 188 | name = "either" 189 | version = "1.8.1" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" 192 | 193 | [[package]] 194 | name = "errno" 195 | version = "0.3.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 198 | dependencies = [ 199 | "errno-dragonfly", 200 | "libc", 201 | "windows-sys 0.48.0", 202 | ] 203 | 204 | [[package]] 205 | name = "errno-dragonfly" 206 | version = "0.1.2" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 209 | dependencies = [ 210 | "cc", 211 | "libc", 212 | ] 213 | 214 | [[package]] 215 | name = "fastrand" 216 | version = "1.9.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 219 | dependencies = [ 220 | "instant", 221 | ] 222 | 223 | [[package]] 224 | name = "getrandom" 225 | version = "0.2.8" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 228 | dependencies = [ 229 | "cfg-if", 230 | "libc", 231 | "wasi", 232 | ] 233 | 234 | [[package]] 235 | name = "glob" 236 | version = "0.3.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 239 | 240 | [[package]] 241 | name = "heck" 242 | version = "0.5.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 245 | 246 | [[package]] 247 | name = "hermit-abi" 248 | version = "0.3.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 251 | 252 | [[package]] 253 | name = "instant" 254 | version = "0.1.12" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 257 | dependencies = [ 258 | "cfg-if", 259 | ] 260 | 261 | [[package]] 262 | name = "io-lifetimes" 263 | version = "1.0.9" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" 266 | dependencies = [ 267 | "hermit-abi", 268 | "libc", 269 | "windows-sys 0.45.0", 270 | ] 271 | 272 | [[package]] 273 | name = "itoa" 274 | version = "1.0.6" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 277 | 278 | [[package]] 279 | name = "lazy_static" 280 | version = "1.4.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 283 | 284 | [[package]] 285 | name = "lazycell" 286 | version = "1.3.0" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 289 | 290 | [[package]] 291 | name = "libc" 292 | version = "0.2.140" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" 295 | 296 | [[package]] 297 | name = "libloading" 298 | version = "0.7.4" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" 301 | dependencies = [ 302 | "cfg-if", 303 | "winapi", 304 | ] 305 | 306 | [[package]] 307 | name = "linux-raw-sys" 308 | version = "0.3.1" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" 311 | 312 | [[package]] 313 | name = "log" 314 | version = "0.4.17" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 317 | dependencies = [ 318 | "cfg-if", 319 | ] 320 | 321 | [[package]] 322 | name = "memchr" 323 | version = "2.7.2" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 326 | 327 | [[package]] 328 | name = "minimal-lexical" 329 | version = "0.2.1" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 332 | 333 | [[package]] 334 | name = "nom" 335 | version = "7.1.3" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 338 | dependencies = [ 339 | "memchr", 340 | "minimal-lexical", 341 | ] 342 | 343 | [[package]] 344 | name = "nu-ansi-term" 345 | version = "0.50.1" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" 348 | dependencies = [ 349 | "windows-sys 0.52.0", 350 | ] 351 | 352 | [[package]] 353 | name = "num_cpus" 354 | version = "1.16.0" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 357 | dependencies = [ 358 | "hermit-abi", 359 | "libc", 360 | ] 361 | 362 | [[package]] 363 | name = "once_cell" 364 | version = "1.17.1" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 367 | 368 | [[package]] 369 | name = "peeking_take_while" 370 | version = "0.1.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 373 | 374 | [[package]] 375 | name = "pin-project-lite" 376 | version = "0.2.9" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 379 | 380 | [[package]] 381 | name = "ppv-lite86" 382 | version = "0.2.17" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 385 | 386 | [[package]] 387 | name = "proc-macro2" 388 | version = "1.0.79" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" 391 | dependencies = [ 392 | "unicode-ident", 393 | ] 394 | 395 | [[package]] 396 | name = "quote" 397 | version = "1.0.26" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 400 | dependencies = [ 401 | "proc-macro2", 402 | ] 403 | 404 | [[package]] 405 | name = "radamsa-sys" 406 | version = "0.1.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "e717084a334e0135f7da8c453d406fce876477d637b750fab2ab3d408641ddc1" 409 | dependencies = [ 410 | "bindgen", 411 | ] 412 | 413 | [[package]] 414 | name = "rand" 415 | version = "0.8.5" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 418 | dependencies = [ 419 | "libc", 420 | "rand_chacha", 421 | "rand_core", 422 | ] 423 | 424 | [[package]] 425 | name = "rand_chacha" 426 | version = "0.3.1" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 429 | dependencies = [ 430 | "ppv-lite86", 431 | "rand_core", 432 | ] 433 | 434 | [[package]] 435 | name = "rand_core" 436 | version = "0.6.4" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 439 | dependencies = [ 440 | "getrandom", 441 | ] 442 | 443 | [[package]] 444 | name = "redox_syscall" 445 | version = "0.3.5" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 448 | dependencies = [ 449 | "bitflags", 450 | ] 451 | 452 | [[package]] 453 | name = "regex" 454 | version = "1.11.1" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 457 | dependencies = [ 458 | "aho-corasick", 459 | "memchr", 460 | "regex-automata", 461 | "regex-syntax", 462 | ] 463 | 464 | [[package]] 465 | name = "regex-automata" 466 | version = "0.4.8" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 469 | dependencies = [ 470 | "aho-corasick", 471 | "memchr", 472 | "regex-syntax", 473 | ] 474 | 475 | [[package]] 476 | name = "regex-syntax" 477 | version = "0.8.5" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 480 | 481 | [[package]] 482 | name = "rustc-hash" 483 | version = "1.1.0" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 486 | 487 | [[package]] 488 | name = "rustix" 489 | version = "0.37.6" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849" 492 | dependencies = [ 493 | "bitflags", 494 | "errno", 495 | "io-lifetimes", 496 | "libc", 497 | "linux-raw-sys", 498 | "windows-sys 0.45.0", 499 | ] 500 | 501 | [[package]] 502 | name = "ryu" 503 | version = "1.0.13" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 506 | 507 | [[package]] 508 | name = "serde" 509 | version = "1.0.159" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" 512 | dependencies = [ 513 | "serde_derive", 514 | ] 515 | 516 | [[package]] 517 | name = "serde_derive" 518 | version = "1.0.159" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" 521 | dependencies = [ 522 | "proc-macro2", 523 | "quote", 524 | "syn 2.0.13", 525 | ] 526 | 527 | [[package]] 528 | name = "serde_json" 529 | version = "1.0.95" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" 532 | dependencies = [ 533 | "itoa", 534 | "ryu", 535 | "serde", 536 | ] 537 | 538 | [[package]] 539 | name = "shlex" 540 | version = "1.1.0" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" 543 | 544 | [[package]] 545 | name = "strsim" 546 | version = "0.11.0" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 549 | 550 | [[package]] 551 | name = "syn" 552 | version = "1.0.109" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 555 | dependencies = [ 556 | "proc-macro2", 557 | "quote", 558 | "unicode-ident", 559 | ] 560 | 561 | [[package]] 562 | name = "syn" 563 | version = "2.0.13" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" 566 | dependencies = [ 567 | "proc-macro2", 568 | "quote", 569 | "unicode-ident", 570 | ] 571 | 572 | [[package]] 573 | name = "tempfile" 574 | version = "3.5.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" 577 | dependencies = [ 578 | "cfg-if", 579 | "fastrand", 580 | "redox_syscall", 581 | "rustix", 582 | "windows-sys 0.45.0", 583 | ] 584 | 585 | [[package]] 586 | name = "thiserror" 587 | version = "1.0.40" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" 590 | dependencies = [ 591 | "thiserror-impl", 592 | ] 593 | 594 | [[package]] 595 | name = "thiserror-impl" 596 | version = "1.0.40" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" 599 | dependencies = [ 600 | "proc-macro2", 601 | "quote", 602 | "syn 2.0.13", 603 | ] 604 | 605 | [[package]] 606 | name = "tracing" 607 | version = "0.1.37" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 610 | dependencies = [ 611 | "cfg-if", 612 | "pin-project-lite", 613 | "tracing-attributes", 614 | "tracing-core", 615 | ] 616 | 617 | [[package]] 618 | name = "tracing-attributes" 619 | version = "0.1.23" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 622 | dependencies = [ 623 | "proc-macro2", 624 | "quote", 625 | "syn 1.0.109", 626 | ] 627 | 628 | [[package]] 629 | name = "tracing-core" 630 | version = "0.1.30" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 633 | dependencies = [ 634 | "once_cell", 635 | ] 636 | 637 | [[package]] 638 | name = "tree-crasher" 639 | version = "0.4.0" 640 | dependencies = [ 641 | "anyhow", 642 | "clap", 643 | "clap-verbosity-flag", 644 | "nu-ansi-term", 645 | "num_cpus", 646 | "radamsa-sys", 647 | "rand", 648 | "regex", 649 | "tree-sitter", 650 | "tree-splicer", 651 | "treereduce", 652 | ] 653 | 654 | [[package]] 655 | name = "tree-crasher-c" 656 | version = "0.4.0" 657 | dependencies = [ 658 | "anyhow", 659 | "tree-crasher", 660 | "tree-sitter-c", 661 | ] 662 | 663 | [[package]] 664 | name = "tree-crasher-css" 665 | version = "0.4.0" 666 | dependencies = [ 667 | "anyhow", 668 | "tree-crasher", 669 | "tree-sitter-css", 670 | ] 671 | 672 | [[package]] 673 | name = "tree-crasher-html" 674 | version = "0.4.0" 675 | dependencies = [ 676 | "anyhow", 677 | "tree-crasher", 678 | "tree-sitter-html", 679 | ] 680 | 681 | [[package]] 682 | name = "tree-crasher-javascript" 683 | version = "0.4.0" 684 | dependencies = [ 685 | "anyhow", 686 | "tree-crasher", 687 | "tree-sitter-javascript", 688 | ] 689 | 690 | [[package]] 691 | name = "tree-crasher-nix" 692 | version = "0.1.0" 693 | dependencies = [ 694 | "anyhow", 695 | "tree-crasher", 696 | "tree-sitter-nix", 697 | ] 698 | 699 | [[package]] 700 | name = "tree-crasher-python" 701 | version = "0.4.0" 702 | dependencies = [ 703 | "anyhow", 704 | "tree-crasher", 705 | "tree-sitter-python", 706 | ] 707 | 708 | [[package]] 709 | name = "tree-crasher-regex" 710 | version = "0.4.0" 711 | dependencies = [ 712 | "anyhow", 713 | "tree-crasher", 714 | "tree-sitter-regex", 715 | ] 716 | 717 | [[package]] 718 | name = "tree-crasher-ruby" 719 | version = "0.4.0" 720 | dependencies = [ 721 | "anyhow", 722 | "tree-crasher", 723 | "tree-sitter-ruby", 724 | ] 725 | 726 | [[package]] 727 | name = "tree-crasher-rust" 728 | version = "0.4.0" 729 | dependencies = [ 730 | "anyhow", 731 | "tree-crasher", 732 | "tree-sitter-rust", 733 | ] 734 | 735 | [[package]] 736 | name = "tree-crasher-solidity" 737 | version = "0.4.0" 738 | dependencies = [ 739 | "anyhow", 740 | "tree-crasher", 741 | "tree-sitter-solidity", 742 | ] 743 | 744 | [[package]] 745 | name = "tree-crasher-sql" 746 | version = "0.4.0" 747 | dependencies = [ 748 | "anyhow", 749 | "tree-crasher", 750 | "tree-sitter-sql", 751 | ] 752 | 753 | [[package]] 754 | name = "tree-crasher-typescript" 755 | version = "0.4.0" 756 | dependencies = [ 757 | "anyhow", 758 | "tree-crasher", 759 | "tree-sitter-typescript", 760 | ] 761 | 762 | [[package]] 763 | name = "tree-sitter" 764 | version = "0.20.10" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d" 767 | dependencies = [ 768 | "cc", 769 | "regex", 770 | ] 771 | 772 | [[package]] 773 | name = "tree-sitter-c" 774 | version = "0.20.8" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "4bbd5f3d8658c08581f8f2adac6c391c2e9fa00fe9246bf6c5f52213b9cc6b72" 777 | dependencies = [ 778 | "cc", 779 | "tree-sitter", 780 | ] 781 | 782 | [[package]] 783 | name = "tree-sitter-css" 784 | version = "0.19.0" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "c44fce8f9b603fef51e6384e2771ec5448bca1b71f1aa4ee2717a1803f9b279b" 787 | dependencies = [ 788 | "cc", 789 | "tree-sitter", 790 | ] 791 | 792 | [[package]] 793 | name = "tree-sitter-edit" 794 | version = "0.3.0" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "9ed3213ee656e99748eca539913b5c90df3d52618d9a1714e0935013955c8031" 797 | dependencies = [ 798 | "tree-sitter", 799 | "tree-sitter-traversal", 800 | ] 801 | 802 | [[package]] 803 | name = "tree-sitter-html" 804 | version = "0.20.0" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "017822b6bd42843c4bd67fabb834f61ce23254e866282dd93871350fd6b7fa1d" 807 | dependencies = [ 808 | "cc", 809 | "tree-sitter", 810 | ] 811 | 812 | [[package]] 813 | name = "tree-sitter-javascript" 814 | version = "0.20.3" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "38d1463af5be7052171161db7cfe45c7621ed959ae533972ab47a09b1ed70ec0" 817 | dependencies = [ 818 | "cc", 819 | "tree-sitter", 820 | ] 821 | 822 | [[package]] 823 | name = "tree-sitter-nix" 824 | version = "0.0.1" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "ae8c93b7dd2afcd9667daae048135be9ee268e9e3900f4d7d0556a63ec5336b1" 827 | dependencies = [ 828 | "cc", 829 | "tree-sitter", 830 | ] 831 | 832 | [[package]] 833 | name = "tree-sitter-python" 834 | version = "0.20.4" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "e6c93b1b1fbd0d399db3445f51fd3058e43d0b4dcff62ddbdb46e66550978aa5" 837 | dependencies = [ 838 | "cc", 839 | "tree-sitter", 840 | ] 841 | 842 | [[package]] 843 | name = "tree-sitter-regex" 844 | version = "1.0.0" 845 | source = "git+https://github.com/tree-sitter/tree-sitter-regex#17a3293714312c691ef14217f60593a3d093381c" 846 | dependencies = [ 847 | "cc", 848 | "tree-sitter", 849 | ] 850 | 851 | [[package]] 852 | name = "tree-sitter-ruby" 853 | version = "0.20.0" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "0ac30cbb1560363ae76e1ccde543d6d99087421e228cc47afcec004b86bb711a" 856 | dependencies = [ 857 | "cc", 858 | "tree-sitter", 859 | ] 860 | 861 | [[package]] 862 | name = "tree-sitter-rust" 863 | version = "0.20.4" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "b0832309b0b2b6d33760ce5c0e818cb47e1d72b468516bfe4134408926fa7594" 866 | dependencies = [ 867 | "cc", 868 | "tree-sitter", 869 | ] 870 | 871 | [[package]] 872 | name = "tree-sitter-solidity" 873 | version = "1.2.9" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "12e75b12cb5c30eabde6e7ee181c18d8582b8f4ea965a989b9eed1cb322a7b61" 876 | dependencies = [ 877 | "cc", 878 | "tree-sitter", 879 | ] 880 | 881 | [[package]] 882 | name = "tree-sitter-sql" 883 | version = "0.0.1" 884 | source = "git+https://github.com/langston-barrett/tree-sitter-sql#1e79451c00ef0c8f9f0c5d7e6e04f9f742827483" 885 | dependencies = [ 886 | "cc", 887 | "tree-sitter", 888 | ] 889 | 890 | [[package]] 891 | name = "tree-sitter-traversal" 892 | version = "0.1.2" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "df8a158225e4a4d8505f071340bba9edd109b23f01b70540dccb7c799868f307" 895 | dependencies = [ 896 | "tree-sitter", 897 | ] 898 | 899 | [[package]] 900 | name = "tree-sitter-typescript" 901 | version = "0.20.5" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "c8bc1d2c24276a48ef097a71b56888ac9db63717e8f8d0b324668a27fd619670" 904 | dependencies = [ 905 | "cc", 906 | "tree-sitter", 907 | ] 908 | 909 | [[package]] 910 | name = "tree-splicer" 911 | version = "0.5.0" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "a966d44e4ad95e4c1f1b3e8e93fabccd1e08f4b377f57f04ce17dfb61df41d77" 914 | dependencies = [ 915 | "log", 916 | "rand", 917 | "serde", 918 | "serde_json", 919 | "tracing", 920 | "tree-sitter", 921 | "tree-sitter-edit", 922 | ] 923 | 924 | [[package]] 925 | name = "treereduce" 926 | version = "0.3.1" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "25140845f21c4c0e2cd87350e15b1f0c8f2f13befec2382544384af3362d0cf5" 929 | dependencies = [ 930 | "log", 931 | "regex", 932 | "serde", 933 | "serde_json", 934 | "tempfile", 935 | "thiserror", 936 | "tracing", 937 | "tree-sitter", 938 | "tree-sitter-edit", 939 | "wait-timeout", 940 | ] 941 | 942 | [[package]] 943 | name = "unicode-ident" 944 | version = "1.0.8" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 947 | 948 | [[package]] 949 | name = "utf8parse" 950 | version = "0.2.1" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 953 | 954 | [[package]] 955 | name = "wait-timeout" 956 | version = "0.2.0" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 959 | dependencies = [ 960 | "libc", 961 | ] 962 | 963 | [[package]] 964 | name = "wasi" 965 | version = "0.11.0+wasi-snapshot-preview1" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 968 | 969 | [[package]] 970 | name = "which" 971 | version = "4.4.0" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" 974 | dependencies = [ 975 | "either", 976 | "libc", 977 | "once_cell", 978 | ] 979 | 980 | [[package]] 981 | name = "winapi" 982 | version = "0.3.9" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 985 | dependencies = [ 986 | "winapi-i686-pc-windows-gnu", 987 | "winapi-x86_64-pc-windows-gnu", 988 | ] 989 | 990 | [[package]] 991 | name = "winapi-i686-pc-windows-gnu" 992 | version = "0.4.0" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 995 | 996 | [[package]] 997 | name = "winapi-x86_64-pc-windows-gnu" 998 | version = "0.4.0" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1001 | 1002 | [[package]] 1003 | name = "windows-sys" 1004 | version = "0.45.0" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1007 | dependencies = [ 1008 | "windows-targets 0.42.2", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "windows-sys" 1013 | version = "0.48.0" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1016 | dependencies = [ 1017 | "windows-targets 0.48.0", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "windows-sys" 1022 | version = "0.52.0" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1025 | dependencies = [ 1026 | "windows-targets 0.52.4", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "windows-targets" 1031 | version = "0.42.2" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 1034 | dependencies = [ 1035 | "windows_aarch64_gnullvm 0.42.2", 1036 | "windows_aarch64_msvc 0.42.2", 1037 | "windows_i686_gnu 0.42.2", 1038 | "windows_i686_msvc 0.42.2", 1039 | "windows_x86_64_gnu 0.42.2", 1040 | "windows_x86_64_gnullvm 0.42.2", 1041 | "windows_x86_64_msvc 0.42.2", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "windows-targets" 1046 | version = "0.48.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 1049 | dependencies = [ 1050 | "windows_aarch64_gnullvm 0.48.0", 1051 | "windows_aarch64_msvc 0.48.0", 1052 | "windows_i686_gnu 0.48.0", 1053 | "windows_i686_msvc 0.48.0", 1054 | "windows_x86_64_gnu 0.48.0", 1055 | "windows_x86_64_gnullvm 0.48.0", 1056 | "windows_x86_64_msvc 0.48.0", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "windows-targets" 1061 | version = "0.52.4" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 1064 | dependencies = [ 1065 | "windows_aarch64_gnullvm 0.52.4", 1066 | "windows_aarch64_msvc 0.52.4", 1067 | "windows_i686_gnu 0.52.4", 1068 | "windows_i686_msvc 0.52.4", 1069 | "windows_x86_64_gnu 0.52.4", 1070 | "windows_x86_64_gnullvm 0.52.4", 1071 | "windows_x86_64_msvc 0.52.4", 1072 | ] 1073 | 1074 | [[package]] 1075 | name = "windows_aarch64_gnullvm" 1076 | version = "0.42.2" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 1079 | 1080 | [[package]] 1081 | name = "windows_aarch64_gnullvm" 1082 | version = "0.48.0" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 1085 | 1086 | [[package]] 1087 | name = "windows_aarch64_gnullvm" 1088 | version = "0.52.4" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 1091 | 1092 | [[package]] 1093 | name = "windows_aarch64_msvc" 1094 | version = "0.42.2" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 1097 | 1098 | [[package]] 1099 | name = "windows_aarch64_msvc" 1100 | version = "0.48.0" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 1103 | 1104 | [[package]] 1105 | name = "windows_aarch64_msvc" 1106 | version = "0.52.4" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 1109 | 1110 | [[package]] 1111 | name = "windows_i686_gnu" 1112 | version = "0.42.2" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 1115 | 1116 | [[package]] 1117 | name = "windows_i686_gnu" 1118 | version = "0.48.0" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 1121 | 1122 | [[package]] 1123 | name = "windows_i686_gnu" 1124 | version = "0.52.4" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 1127 | 1128 | [[package]] 1129 | name = "windows_i686_msvc" 1130 | version = "0.42.2" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 1133 | 1134 | [[package]] 1135 | name = "windows_i686_msvc" 1136 | version = "0.48.0" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 1139 | 1140 | [[package]] 1141 | name = "windows_i686_msvc" 1142 | version = "0.52.4" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 1145 | 1146 | [[package]] 1147 | name = "windows_x86_64_gnu" 1148 | version = "0.42.2" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 1151 | 1152 | [[package]] 1153 | name = "windows_x86_64_gnu" 1154 | version = "0.48.0" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 1157 | 1158 | [[package]] 1159 | name = "windows_x86_64_gnu" 1160 | version = "0.52.4" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 1163 | 1164 | [[package]] 1165 | name = "windows_x86_64_gnullvm" 1166 | version = "0.42.2" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 1169 | 1170 | [[package]] 1171 | name = "windows_x86_64_gnullvm" 1172 | version = "0.48.0" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 1175 | 1176 | [[package]] 1177 | name = "windows_x86_64_gnullvm" 1178 | version = "0.52.4" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 1181 | 1182 | [[package]] 1183 | name = "windows_x86_64_msvc" 1184 | version = "0.42.2" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 1187 | 1188 | [[package]] 1189 | name = "windows_x86_64_msvc" 1190 | version = "0.48.0" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 1193 | 1194 | [[package]] 1195 | name = "windows_x86_64_msvc" 1196 | version = "0.52.4" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 1199 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "crates/tree-crasher", 4 | "crates/tree-crasher-c", 5 | "crates/tree-crasher-css", 6 | "crates/tree-crasher-html", 7 | "crates/tree-crasher-javascript", 8 | "crates/tree-crasher-nix", 9 | "crates/tree-crasher-python", 10 | "crates/tree-crasher-regex", 11 | "crates/tree-crasher-ruby", 12 | "crates/tree-crasher-rust", 13 | "crates/tree-crasher-sql", 14 | "crates/tree-crasher-typescript", 15 | "crates/tree-crasher-solidity", 16 | ] 17 | 18 | # https://github.com/mstange/samply#turn-on-debug-info-for-full-stacks 19 | [profile.profiling] 20 | inherits = "release" 21 | debug = true 22 | 23 | # https://nnethercote.github.io/perf-book/build-configuration.html 24 | [profile.release] 25 | lto = true 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2023 Brian Langston Barrett 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the “Software”), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree-crasher 2 | 3 | tree-crasher is an easy-to-use grammar-based black-box fuzzer. It parses a 4 | number of input files using [tree-sitter][tree-sitter] grammars, and produces 5 | new files formed by splicing together their ASTs. 6 | 7 | tree-crasher aims to occupy a different niche from more advanced grammar-based 8 | fuzzers like Gramatron, Nautilus, and Grammarinator. Rather than achieve 9 | maximal coverage and bug-finding through complete, hand-written grammars and 10 | complex techniques like coverage-based feedback, tree-crasher aims to achieve 11 | maximal ease-of-use by using off-the-shelf tree-sitter grammars and not 12 | requiring any instrumentation (nor even source code) for the target. In short, 13 | tree-crasher wants to be the [Radamsa][radamsa] of grammar-based fuzzing. 14 | 15 | tree-crasher uses [treereduce][treereduce] to automatically minimize generated 16 | test-cases. 17 | 18 | For more information, see [the documentation][doc]. 19 | 20 | ## Examples 21 | 22 | When reading these examples, keep in mind that fuzzing can cause unpredictable 23 | behaviors. Always fuzz in a VM or Docker container with a memory limit, no 24 | network access, and no important files. 25 | 26 | ### JavaScript interpreters 27 | 28 | Obtain a collection of JavaScript files and put them in `corpus/` (for 29 | example, using [this script](./scripts/corpora/js.sh)). Then here's how to fuzz 30 | [JerryScript][jerryscript] and [Boa][boa]: 31 | 32 | ```sh 33 | tree-crasher-javascript corpus/ jerry 34 | tree-crasher-javascript corpus/ boa 35 | ``` 36 | 37 | (By default, tree-crasher passes input to the target on stdin.) 38 | 39 | [boa]: https://github.com/boa-dev/boa 40 | [jerryscript]: https://github.com/jerryscript-project/jerryscript 41 | 42 | ### Python's regex engine 43 | 44 | Write `rx.py` like so: 45 | ```python 46 | import re 47 | import sys 48 | try: 49 | s = sys.stdin.read() 50 | r = re.compile(s) 51 | print(r.match(s)) 52 | except: 53 | pass 54 | ``` 55 | 56 | Put some sample regular expressions in `corpus/`. Then: 57 | ```sh 58 | tree-crasher-regex corpus/ -- python3 $PWD/rx.py 59 | ``` 60 | 61 | ### rustc 62 | 63 | tree-crasher has found many bugs in rustc. Here's how it was done! The special 64 | `@@` symbol on the command line gets replaced by the file generated by 65 | tree-crasher. 66 | 67 | ```sh 68 | tree-crasher-rust \ 69 | --interesting-stderr "(?m)^error: internal compiler error:" \ 70 | corpus \ 71 | -- \ 72 | rustc +nightly --crate-type=lib --emit=mir -Zmir-opt-level=4 @@.rs 73 | ``` 74 | 75 | (The regex syntax is that of the 76 | [regex crate](https://docs.rs/regex/latest/regex/).) 77 | 78 | ### More examples 79 | 80 | See [the documentation][doc] for more examples. 81 | 82 | ## Bugs found 83 | 84 | tree-crasher uses [tree-splicer][tree-splicer] to generate test cases, see the 85 | list of bugs found in that project's README. 86 | 87 | If you find a bug with tree-crasher, please let me know! One great way to do so 88 | would be to submit a PR to tree-splicer to add it to the README. 89 | 90 | ## Supported languages 91 | 92 | tree-crasher supports 9+ languages, see [the documentation][doc] for details. 93 | 94 | ## Documentation 95 | 96 | Documentation is available [online][doc] or in `./doc`. 97 | 98 | [doc]: https://langston-barrett.github.io/tree-crasher/ 99 | [radamsa]: https://gitlab.com/akihe/radamsa 100 | [tree-sitter]: https://tree-sitter.github.io/tree-sitter/ 101 | [tree-splicer]: https://github.com/langston-barrett/tree-splicer 102 | [treereduce]: https://github.com/langston-barrett/treereduce 103 | -------------------------------------------------------------------------------- /crates/tree-crasher-c/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-c" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-c = "0.20" -------------------------------------------------------------------------------- /crates/tree-crasher-c/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_c::language(), tree_sitter_c::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-css/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-css" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-css = "0.19" -------------------------------------------------------------------------------- /crates/tree-crasher-css/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_css::language(), tree_sitter_css::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-html/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-html" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-html = "0.20" -------------------------------------------------------------------------------- /crates/tree-crasher-html/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_html::language(), tree_sitter_html::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-javascript/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-javascript" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-javascript = "0.20" 17 | -------------------------------------------------------------------------------- /crates/tree-crasher-javascript/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main( 5 | tree_sitter_javascript::language(), 6 | tree_sitter_javascript::NODE_TYPES, 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /crates/tree-crasher-nix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-nix" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["William Woodruff "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-nix = "0.0.1" 17 | -------------------------------------------------------------------------------- /crates/tree-crasher-nix/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_nix::language(), tree_sitter_nix::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-python" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-python = "0.20" -------------------------------------------------------------------------------- /crates/tree-crasher-python/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main( 5 | tree_sitter_python::language(), 6 | tree_sitter_python::NODE_TYPES, 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /crates/tree-crasher-regex/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-regex" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-regex = { git = "https://github.com/tree-sitter/tree-sitter-regex" } -------------------------------------------------------------------------------- /crates/tree-crasher-regex/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_regex::language(), tree_sitter_regex::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-ruby/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-ruby" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-ruby = "0.20" -------------------------------------------------------------------------------- /crates/tree-crasher-ruby/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_ruby::language(), tree_sitter_ruby::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-rust" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-rust = "0.20" 17 | -------------------------------------------------------------------------------- /crates/tree-crasher-rust/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_rust::language(), tree_sitter_rust::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-solidity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-solidity" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-solidity = "1.2" 17 | -------------------------------------------------------------------------------- /crates/tree-crasher-solidity/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main( 5 | tree_sitter_solidity::language(), 6 | tree_sitter_solidity::NODE_TYPES, 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /crates/tree-crasher-sql/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-sql" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-sql = { git = "https://github.com/langston-barrett/tree-sitter-sql" } -------------------------------------------------------------------------------- /crates/tree-crasher-sql/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main(tree_sitter_sql::language(), tree_sitter_sql::NODE_TYPES) 5 | } 6 | -------------------------------------------------------------------------------- /crates/tree-crasher-typescript/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher-typescript" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = "1" 15 | tree-crasher = { version = "0.4.0", path = "../tree-crasher" } 16 | tree-sitter-typescript = "0.20" -------------------------------------------------------------------------------- /crates/tree-crasher-typescript/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | fn main() -> Result<()> { 4 | tree_crasher::main( 5 | tree_sitter_typescript::language_typescript(), 6 | tree_sitter_typescript::TYPESCRIPT_NODE_TYPES, 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /crates/tree-crasher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-crasher" 3 | version = "0.4.0" 4 | edition = "2021" 5 | description = "Easy-to-use grammar-based black-box fuzzer" 6 | keywords = ["black-box", "fuzzer", "grammar-based"] 7 | authors = ["Langston Barrett "] 8 | license = "MIT" 9 | readme = "../../README.md" 10 | homepage = "https://github.com/langston-barrett/tree-crasher" 11 | repository = "https://github.com/langston-barrett/tree-crasher" 12 | 13 | [dependencies] 14 | anyhow = { version = "1", optional = false } 15 | clap-verbosity-flag = { version = "3", optional = false } 16 | clap = { version = "4", features = ["derive"], optional = false } 17 | nu-ansi-term = { version = "0.50", optional = false } 18 | num_cpus = { version = "1", optional = false } 19 | rand = "0.8" 20 | regex = "1" 21 | treereduce = "0.3.1" 22 | tree-sitter = "0.20" 23 | tree-splicer = "0.5" 24 | radamsa-sys = { version = "0.1", optional = true } 25 | 26 | [features] 27 | default = [] 28 | radamsa = ["dep:radamsa-sys"] 29 | -------------------------------------------------------------------------------- /crates/tree-crasher/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs; 3 | use std::os::unix::process::ExitStatusExt; 4 | use std::path::PathBuf; 5 | use std::time::Duration; 6 | use std::time::Instant; 7 | 8 | use anyhow::{Context, Result}; 9 | use clap::Parser; 10 | use clap_verbosity_flag::{InfoLevel, Verbosity}; 11 | use rand::Rng; 12 | use regex::Regex; 13 | use tree_sitter::Language; 14 | use tree_sitter::Tree; 15 | use tree_splicer::splice::{Config, Splicer}; 16 | use treereduce::Check; 17 | use treereduce::CmdCheck; 18 | 19 | /// An easy-to-use grammar-based black-box fuzzer 20 | #[derive(Clone, Debug, clap::Parser)] 21 | #[command(author, version, about, long_about = None)] 22 | pub struct Args { 23 | /// Percent of "chaotic" mutations - may introduce syntax errors 24 | #[arg(help_heading = "Mutation options", short, long, default_value_t = 5)] 25 | pub chaos: u8, 26 | 27 | /// Percent of deletion mutations - the rest are splices 28 | #[arg(help_heading = "Mutation options", long, default_value_t = 5)] 29 | pub deletions: u8, 30 | 31 | /// Approximate maximum file size to produce (bytes); default = 1MiB 32 | #[arg(help_heading = "Mutation options", long, default_value_t = 1048576)] 33 | pub max_size: usize, 34 | 35 | /// Number of mutations per test 36 | #[arg(help_heading = "Mutation options", short, long, default_value_t = 16)] 37 | pub mutations: usize, 38 | 39 | /// Use Radamsa for mutations; ignore all other mutation options 40 | #[cfg(feature = "radamsa")] 41 | #[arg(help_heading = "Mutation options", short, long)] 42 | pub radamsa: bool, 43 | 44 | /// Run a single thread and show stdout, stderr of target 45 | #[arg(short, long)] 46 | pub debug: bool, 47 | 48 | /// Exit code to consider interesting 49 | #[arg(help_heading = "Interestingness check options", 50 | long, default_values_t = Vec::::new(), value_name = "CODE")] 51 | interesting_exit_code: Vec, 52 | 53 | /// Regex to match interesting stdout 54 | #[arg( 55 | help_heading = "Interestingness check options", 56 | long, 57 | value_name = "REGEX" 58 | )] 59 | interesting_stdout: Option, 60 | 61 | /// Regex to match interesting stderr 62 | #[arg( 63 | help_heading = "Interestingness check options", 64 | long, 65 | value_name = "REGEX" 66 | )] 67 | interesting_stderr: Option, 68 | 69 | /// Regex to match *uninteresting* stdout, overrides interesting regex 70 | #[arg( 71 | help_heading = "Interestingness check options", 72 | long, 73 | value_name = "REGEX", 74 | requires = "interesting_stdout" 75 | )] 76 | uninteresting_stdout: Option, 77 | 78 | /// Regex to match *uninteresting* stderr, overrides interesting regex 79 | #[arg( 80 | help_heading = "Interestingness check options", 81 | long, 82 | value_name = "REGEX", 83 | requires = "interesting_stderr" 84 | )] 85 | uninteresting_stderr: Option, 86 | 87 | /// Number of threads 88 | #[arg(short, long, default_value_t = num_cpus::get())] 89 | pub jobs: usize, 90 | 91 | /// Directory to output to 92 | #[arg(short, long, default_value_os = "tree-crasher.out")] 93 | pub output: PathBuf, 94 | 95 | /// Seed 96 | #[arg(short, long, default_value_t = 0)] 97 | pub seed: u64, 98 | 99 | /// Timeout (ms) 100 | #[arg(long, default_value_t = 500)] 101 | pub timeout: u64, 102 | 103 | #[clap(flatten)] 104 | verbose: Verbosity, 105 | 106 | /// Input files 107 | #[arg(value_name = "DIR", required = true)] 108 | pub files: String, 109 | 110 | /// Interestingness check; fed test case on stdin or via '@@' file 111 | #[arg(value_name = "CMD", required = true, num_args = 1..)] 112 | pub check: Vec, 113 | } 114 | 115 | fn read_file(file: &PathBuf) -> Result { 116 | fs::read_to_string(file).with_context(|| format!("Failed to read file {}", file.display())) 117 | } 118 | 119 | fn parse(language: Language, code: &str) -> Result { 120 | let mut parser = tree_sitter::Parser::new(); 121 | parser 122 | .set_language(language) 123 | .context("Failed to set tree-sitter parser language")?; 124 | parser.parse(code, None).context("Failed to parse code") 125 | } 126 | 127 | #[allow(clippy::too_many_arguments)] 128 | fn make_check( 129 | debug: bool, 130 | timeout: Duration, 131 | check: Vec, 132 | mut interesting_exit_codes: Vec, 133 | interesting_stdout: Option, 134 | interesting_stderr: Option, 135 | uninteresting_stdout: Option, 136 | uninteresting_stderr: Option, 137 | ) -> Result { 138 | if check.is_empty() { 139 | eprintln!("Internal error: empty interestingness check!"); 140 | std::process::exit(1); 141 | } 142 | let mut argv: Vec<_> = check.iter().collect(); 143 | let cmd = argv[0]; 144 | argv.remove(0); 145 | let stdout_regex = match &interesting_stdout { 146 | Some(r) => Some(Regex::new(r).context("Invalid interesting stdout regex")?), 147 | None => None, 148 | }; 149 | let stderr_regex = match &interesting_stderr { 150 | Some(r) => Some(Regex::new(r).context("Invalid interesting stderr regex")?), 151 | None => None, 152 | }; 153 | let un_stdout_regex = match &uninteresting_stdout { 154 | Some(r) => Some(Regex::new(r).context("Invalid uninteresting stdout regex")?), 155 | None => None, 156 | }; 157 | let un_stderr_regex = match &uninteresting_stderr { 158 | Some(r) => Some(Regex::new(r).context("Invalid uninteresting stderr regex")?), 159 | None => None, 160 | }; 161 | interesting_exit_codes.extend(128..256); 162 | Ok(CmdCheck::new( 163 | cmd.to_string(), 164 | argv.iter().map(|s| s.to_string()).collect(), 165 | interesting_exit_codes, 166 | None, 167 | stdout_regex, 168 | stderr_regex, 169 | un_stdout_regex, 170 | un_stderr_regex, 171 | debug, 172 | debug, 173 | Some(timeout), 174 | )) 175 | } 176 | 177 | const BATCH: usize = 100_000; // not all materialized at once 178 | 179 | fn check( 180 | language: Language, 181 | node_types: &treereduce::NodeTypes, 182 | chk: &CmdCheck, 183 | inp: &[u8], 184 | ) -> i32 { 185 | let state = match chk.start(inp) { 186 | Ok(s) => s, 187 | Err(e) => { 188 | eprintln!("Problem when running target: {e}"); 189 | return -1; 190 | } 191 | }; 192 | let (interesting, status, stdout, stderr) = chk.wait_with_output(state).unwrap(); 193 | let code = status.and_then(|s| s.code()).unwrap_or(-1); 194 | let sig = status.and_then(|s| s.signal()); 195 | if interesting || sig.is_some() { 196 | if let Some(s) = sig { 197 | if s == 6 { 198 | return code; 199 | } 200 | eprintln!("signal {s}!"); 201 | } else { 202 | eprintln!("interesting!"); 203 | } 204 | let mut rng = rand::thread_rng(); 205 | let i = rng.gen_range(0..10192); 206 | fs::write(format!("tree-crasher-{i}.out"), inp).unwrap(); 207 | fs::write(format!("tree-crasher-{i}.stdout"), stdout).unwrap(); 208 | fs::write(format!("tree-crasher-{i}.stderr"), stderr).unwrap(); 209 | let tree = parse(language, &String::from_utf8_lossy(inp)).unwrap(); 210 | match treereduce::treereduce_multi_pass( 211 | language, 212 | node_types, 213 | treereduce::Original::new(tree, inp.to_vec()), 214 | &treereduce::Config { 215 | check: chk.clone(), 216 | delete_non_optional: true, 217 | jobs: 1, 218 | min_reduction: 2, 219 | replacements: HashMap::new(), 220 | }, 221 | Some(8), 222 | ) { 223 | Err(e) => eprintln!("Failed to reduce! {e}"), 224 | Ok((reduced, _)) => { 225 | fs::write(format!("tree-crasher-{i}.reduced.out"), reduced.text).unwrap(); 226 | } 227 | } 228 | } 229 | code 230 | } 231 | 232 | // TODO: print executions/sec 233 | fn job( 234 | language: Language, 235 | // HACK: there should be another crate that deals with this... 236 | node_types1: &treereduce::NodeTypes, 237 | node_types2: &tree_splicer::node_types::NodeTypes, 238 | args: &Args, 239 | files: &HashMap, Tree)>, 240 | chk: CmdCheck, 241 | ) { 242 | if files.is_empty() { 243 | eprintln!("No files provided."); 244 | return; 245 | } 246 | #[cfg(feature = "radamsa")] 247 | if args.radamsa { 248 | unsafe { radamsa_sys::radamsa_init() }; 249 | let mut rng = rand::thread_rng(); 250 | let file_bytes: Vec<_> = files.values().map(|(bytes, _tree)| bytes).collect(); 251 | loop { 252 | const MAX_SIZE: usize = 4096; 253 | // TODO: Mutate in-place 254 | let mut input: Vec = file_bytes 255 | .get(rng.gen_range(0..files.len())) 256 | .unwrap() 257 | .to_vec(); 258 | let mut mutant = vec![0u8; MAX_SIZE]; 259 | let out_len = unsafe { 260 | radamsa_sys::radamsa( 261 | input.as_mut_ptr(), 262 | input.len(), 263 | mutant.as_mut_ptr(), 264 | MAX_SIZE, 265 | 0, 266 | ) 267 | }; 268 | assert!(out_len <= MAX_SIZE); 269 | mutant.truncate(out_len); 270 | check(language, node_types1, &chk, &mutant); 271 | } 272 | } 273 | loop { 274 | let config = Config { 275 | chaos: args.chaos, 276 | deletions: args.deletions, 277 | language, 278 | // intra_splices: 10, 279 | inter_splices: args.mutations, 280 | node_types: node_types2.clone(), 281 | max_size: args.max_size, 282 | reparse: usize::MAX, 283 | seed: args.seed, 284 | }; 285 | let start = Instant::now(); 286 | let mut execs = 0; 287 | for (i, out) in Splicer::new(config, files).enumerate() { 288 | if i == BATCH { 289 | break; 290 | } 291 | let _code = check(language, node_types1, &chk, &out); 292 | execs += 1; 293 | let secs = start.elapsed().as_secs(); 294 | if execs % 10_000 == 0 { 295 | println!("execs/sec: {}", execs / secs); 296 | } 297 | } 298 | } 299 | } 300 | 301 | // TODO: graceful exit 302 | pub fn main(language: Language, node_types_json_str: &'static str) -> Result<()> { 303 | let args = Args::parse(); 304 | debug_assert!(args.interesting_stdout.is_some() || args.uninteresting_stdout.is_none()); 305 | debug_assert!(args.interesting_stderr.is_some() || args.uninteresting_stderr.is_none()); 306 | 307 | if args.debug { 308 | eprintln!("Loading testcases..."); 309 | } 310 | let mut files = HashMap::new(); 311 | // TODO error messages 312 | for entry in fs::read_dir(&args.files) 313 | .with_context(|| format!("When reading tests from {}", args.files))? 314 | { 315 | let entry = entry?; 316 | let path = entry.path(); 317 | if let Ok(s) = read_file(&path) { 318 | let tree = parse(language, &s)?; 319 | files.insert(String::from(path.to_string_lossy()), (s.into_bytes(), tree)); 320 | } 321 | } 322 | let chk = make_check( 323 | args.debug, 324 | Duration::from_millis(args.timeout), 325 | args.check.clone(), 326 | args.interesting_exit_code.clone(), 327 | args.interesting_stdout.clone(), 328 | args.interesting_stderr.clone(), 329 | args.uninteresting_stdout.clone(), 330 | args.uninteresting_stderr.clone(), 331 | )?; 332 | let node_types1 = treereduce::NodeTypes::new(node_types_json_str).unwrap(); 333 | let node_types2 = tree_splicer::node_types::NodeTypes::new(node_types_json_str).unwrap(); 334 | 335 | if args.debug { 336 | eprintln!("Spawning threads..."); 337 | } 338 | #[cfg(not(feature = "radamsa"))] 339 | let jobs = if args.debug { 1 } else { args.jobs }; 340 | #[cfg(feature = "radamsa")] 341 | let jobs = if args.debug { 342 | if args.jobs != 1 { 343 | eprintln!("[WARN] Radamsa can only be used with one thread."); 344 | } 345 | 1 346 | } else { 347 | args.jobs 348 | }; 349 | std::thread::scope(|s| { 350 | for _ in 0..jobs { 351 | s.spawn(|| { 352 | job( 353 | language, 354 | &node_types1, 355 | &node_types2, 356 | &args, 357 | &files, 358 | chk.clone(), 359 | ) 360 | }); 361 | } 362 | }); 363 | 364 | Ok(()) 365 | } 366 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | book/ 2 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | View the documentation online at 2 | -------------------------------------------------------------------------------- /doc/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Overview](./overview.md) 4 | 5 | # User documentation 6 | 7 | - [Installation](./install.md) 8 | - [Usage](./usage.md) 9 | - [Contributing](./contributing.md) 10 | 11 | # Developer documentation 12 | 13 | - [Development](./dev.md) 14 | -------------------------------------------------------------------------------- /doc/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Langston Barrett"] 3 | language = "en" 4 | multilingual = false 5 | src = "." 6 | title = "tree-crasher" 7 | 8 | [output.html] 9 | git-repository-url = "https://github.com/langston-barrett/tree-crasher/tree/main/doc" 10 | edit-url-template = "https://github.com/langston-barrett/tree-crasher/edit/main/doc/{path}" 11 | -------------------------------------------------------------------------------- /doc/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in tree-crasher! We welcome and appreciate all kinds of 4 | contributions. Please feel free to file and issue or open a pull request. 5 | 6 | You may want to take a look at: 7 | 8 | - The [developer guide](dev.md) 9 | - The [issues labeled "good first issue"][good] 10 | - The [issues labeled "help wanted"][help] 11 | 12 | If you have questions, please ask them on the [discussions page][discuss]! 13 | 14 | [discuss]: https://github.com/langston-barrett/tree-crasher/discussions 15 | [good]: https://github.com/langston-barrett/tree-crasher/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22 16 | [help]: https://github.com/langston-barrett/tree-crasher/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22 17 | -------------------------------------------------------------------------------- /doc/dev.md: -------------------------------------------------------------------------------- 1 | # Developer's guide 2 | 3 | ## Build 4 | 5 | To install from source, you'll need to install Rust and [Cargo][cargo]. Follow 6 | the instructions on the [Rust installation page][install-rust]. Then, get 7 | the source: 8 | 9 | ```bash 10 | git clone https://github.com/langston-barrett/tree-crasher 11 | cd tree-crasher 12 | ``` 13 | 14 | Finally, build everything: 15 | 16 | ```bash 17 | cargo build --release 18 | ``` 19 | 20 | You can find binaries in `target/release`. Run tests with `cargo test`. 21 | 22 | [cargo]: https://doc.rust-lang.org/cargo/ 23 | [install-rust]: https://www.rust-lang.org/tools/install 24 | 25 | ## Docs 26 | 27 | HTML documentation can be built with [mdBook][mdbook]: 28 | 29 | ```sh 30 | cd doc 31 | mdbook build 32 | ``` 33 | 34 | [mdbook]: https://rust-lang.github.io/mdBook/ 35 | 36 | ## Format 37 | 38 | All code should be formatted with [rustfmt][rustfmt]. You can install rustfmt 39 | with [rustup][rustup] like so: 40 | 41 | ```sh 42 | rustup component add rustfmt 43 | ``` 44 | 45 | and then run it like this: 46 | 47 | ```sh 48 | cargo fmt --all 49 | ``` 50 | 51 | [rustfmt]: https://rust-lang.github.io/rustfmt 52 | [rustup]: https://rustup.rs/ 53 | 54 | ## Lint 55 | 56 | All code should pass [Clippy][clippy]. You can install Clippy with rustup 57 | like so: 58 | 59 | ```sh 60 | rustup component add clippy 61 | ``` 62 | 63 | and then run it like this: 64 | 65 | ```sh 66 | cargo clippy --workspace -- --deny warnings 67 | ``` 68 | 69 | [clippy]: https://doc.rust-lang.org/stable/clippy/ 70 | 71 | ## Release 72 | 73 | - Create branch with a name starting with `release` 74 | - Update `CHANGELOG.md` 75 | - Update the version numbers in `./crates/**/Cargo.toml` 76 | 77 | ```sh 78 | find crates/ -type f -name "*.toml" -print0 | \ 79 | xargs -0 sed -E -i 's/^version = "U.V.W"$/version = "X.Y.Z"/' 80 | ``` 81 | 82 | - Run `cargo build --release` 83 | - Commit all changes and push the release branch 84 | - Check that CI was successful on the release branch 85 | - Merge the release branch to `main` 86 | - `git checkout main && git pull origin && git tag -a vX.Y.Z -m vX.Y.Z && git push --tags` 87 | - Verify that the release artifacts work as intended 88 | - Release the pre-release created by CI 89 | - Check that the crates were properly uploaded to crates.io 90 | 91 | ## Warnings 92 | 93 | Certain warnings are disallowed in the CI build. You can reproduce the behavior 94 | of the CI build by running `cargo check`, `cargo build`, or `cargo test` like 95 | so: 96 | 97 | ```sh 98 | env RUSTFLAGS="@$PWD/rustc-flags" cargo check 99 | ``` 100 | 101 | Using a flag file for this purpose achieves several objectives: 102 | 103 | - It frictionlessly allows code with warnings during local development 104 | - It makes it easy to reproduce the CI build process locally 105 | - It makes it easy to maintain the list of warnings 106 | - It [maintains forward-compatibility][anti-pat] with future rustc warnings 107 | - It ensures the flags are consistent across all crates in the project 108 | 109 | This flag file rejects all `rustc` warnings by default, as well as a subset of 110 | [allowed-by-default lints][allowed-by-default]. The goal is to balance 111 | high-quality, maintainable code with not annoying developers. 112 | 113 | To allow a lint in one spot, use: 114 | 115 | ```rust 116 | #[allow(name_of_lint)] 117 | ``` 118 | 119 | To enable these warnings on a semi-permanent basis, create a [Cargo 120 | configuration file][cargo-conf]: 121 | 122 | ```sh 123 | mkdir .cargo 124 | printf "[build]\nrustflags = [\"@${PWD}/rustc-flags\"]\n" > .cargo/config.toml 125 | ``` 126 | 127 | [allowed-by-default]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html 128 | [anti-pat]: https://rust-unofficial.github.io/patterns/anti_patterns/deny-warnings.html#denywarnings 129 | -------------------------------------------------------------------------------- /doc/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Pre-compiled binaries 4 | 5 | Pre-compiled binaries are available on the [releases page][releases]. 6 | 7 | ### Fetching binaries with cURL 8 | 9 | You can download binaries with `curl` like so (replace `X.Y.Z` with a real 10 | version number, `LANG` with a supported language, and `TARGET` with your OS): 11 | ```sh 12 | curl -sSL https://github.com/langston-barrett/tree-crasher/releases/download/vX.Y.Z/tree-crasher-LANG_TARGET -o tree-crasher-LANG 13 | ``` 14 | 15 | ## Build from source 16 | 17 | To install from source, you'll need to install Rust and [Cargo][cargo]. Follow 18 | the instructions on the [Rust installation page][install-rust]. 19 | 20 | [install-rust]: https://www.rust-lang.org/tools/install 21 | 22 | ### From a release on crates.io 23 | 24 | You can build a released version from [crates.io]. To install the latest 25 | release of tree-crasher for the language ``, run: 26 | 27 | ```sh 28 | cargo install tree-crasher- 29 | ``` 30 | 31 | This will automatically download the source from [crates.io], build it, and 32 | install it in Cargo's global binary directory (`~/.cargo/bin/` by default). 33 | 34 | ### From the latest unreleased version on Github 35 | 36 | To build and install the very latest unreleased version, run: 37 | 38 | ```sh 39 | cargo install --git https://github.com/langston-barrett/tree-crasher.git tree-crasher-LANG 40 | ``` 41 | 42 | ### From a local checkout 43 | 44 | See the [developer's guide](dev.md). 45 | 46 | ### Uninstalling 47 | 48 | To uninstall, run `cargo uninstall tree-crasher-`. 49 | 50 | [cargo]: https://doc.rust-lang.org/cargo/ 51 | [crates.io]: https://crates.io/ 52 | [releases]: https://github.com/langston-barrett/tree-crasher/releases 53 | [rustup]: https://rustup.rs/ 54 | -------------------------------------------------------------------------------- /doc/overview.md: -------------------------------------------------------------------------------- 1 | # tree-crasher 2 | 3 | tree-crasher is an easy-to-use grammar-based black-box fuzzer. It parses a 4 | number of input files using [tree-sitter][tree-sitter] grammars, and produces 5 | new files formed by splicing together their ASTs. 6 | 7 | tree-crasher aims to occupy a different niche from more advanced grammar-based 8 | fuzzers like Gramatron, Nautilus, and Grammarinator. Rather than achieve 9 | maximal coverage and bug-finding through complete, hand-written grammars and 10 | complex techniques like coverage-based feedback, tree-crasher aims to achieve 11 | maximal ease-of-use by using off-the-shelf tree-sitter grammars and not 12 | requiring any instrumentation (nor even source code) for the target. In short, 13 | tree-crasher wants to be the [Radamsa][radamsa] of grammar-based fuzzing. 14 | 15 | tree-sitter grammars are resistant to syntax errors. Therefore, tree-crasher 16 | can even mutate syntactically-invalid inputs! You can also use tree-crasher 17 | with an incomplete grammar. 18 | 19 | tree-crasher uses [treereduce][treereduce] to automatically minimize generated 20 | test-cases. 21 | 22 | ## Examples 23 | 24 | See the [usage docs](usage.md). 25 | 26 | ## Bugs found 27 | 28 | tree-crasher uses [tree-splicer][tree-splicer] to generate test cases, see the 29 | list of bugs found in that project's README. 30 | 31 | If you find a bug with tree-crasher, please let me know! One great way to do so 32 | would be to submit a PR to tree-splicer to add it to the README. 33 | 34 | ## Supported languages 35 | 36 | tree-crasher currently ships pre-built executables for the following languages: 37 | 38 | - [C](./crates/tree-crasher-c) 39 | - [CSS](./crates/tree-crasher-css) 40 | - [JavaScript](./crates/tree-crasher-javascript) 41 | - [Regex](./crates/tree-crasher-regex) 42 | - [Rust](./crates/tree-crasher-rust) 43 | - [SQL](./crates/tree-crasher-sql) 44 | - [TypeScript](./crates/tree-crasher-typescript) 45 | 46 | Additionally, the following fuzzers can be built from source or installed via 47 | crates.io: 48 | 49 | - [HTML](./crates/tree-crasher-html) 50 | - [Ruby](./crates/tree-crasher-ruby) 51 | 52 | Languages are very easy to add, so file an issue or a PR if you want a new one! 53 | 54 | ## How it works 55 | 56 | tree-crasher is mostly a thin wrapper around [tree-splicer][tree-splicer] that 57 | runs it in parallel. When "interesting" test cases are found, they're handed 58 | off to [treereduce][treereduce]. 59 | 60 | [radamsa]: https://gitlab.com/akihe/radamsa 61 | [tree-sitter]: https://tree-sitter.github.io/tree-sitter/ 62 | [tree-splicer]: https://github.com/langston-barrett/tree-splicer 63 | [treereduce]: https://github.com/langston-barrett/treereduce -------------------------------------------------------------------------------- /doc/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | The inputs to tree-crasher are a corpus of files and a command to run. By 4 | default, tree-crasher passes inputs to the command on stdin, but will replace 5 | the special symbol `@@` with a filename as seen in the examples above. 6 | 7 | tree-crasher saves inputs that match a set of conditions. By default the only 8 | condition is that the target receives an unhandled signal (e.g., a segfault). 9 | Extra conditions may be added with the `--interesting*` flags, see `--help`. 10 | 11 | tree-crasher does not exit gracefully at the moment; just send SIGINT (ctrl-c) 12 | when you're done fuzzing. 13 | 14 | ## Examples 15 | 16 | When reading these examples, keep in mind that fuzzing can cause unpredictable 17 | behaviors. Always fuzz in a VM or Docker container with a memory limit, no 18 | network access, and no important files. 19 | 20 | ### JavaScript interpreters 21 | 22 | Obtain a collection of JavaScript files and put them in `corpus/` (for example, 23 | using [this script](../scripts/corpora/js.sh)). Then here's how to fuzz 24 | [JerryScript][jerryscript] and [Boa][boa]: 25 | 26 | ```sh 27 | tree-crasher-javascript corpus/ jerry 28 | tree-crasher-javascript corpus/ boa 29 | ``` 30 | 31 | (By default, tree-crasher passes input to the target on stdin.) 32 | 33 | [boa]: https://github.com/boa-dev/boa 34 | [jerryscript]: https://github.com/jerryscript-project/jerryscript 35 | 36 | ### Python's regex engine 37 | 38 | Write `rx.py` like so: 39 | ```python 40 | import re 41 | import sys 42 | try: 43 | s = sys.stdin.read() 44 | r = re.compile(s) 45 | print(r.match(s)) 46 | except: 47 | pass 48 | ``` 49 | 50 | Put some sample regular expressions in `corpus/`. Then: 51 | ```sh 52 | tree-crasher-regex corpus/ -- python3 $PWD/rx.py 53 | ``` 54 | 55 | ### rustc 56 | 57 | tree-crasher has found many bugs in rustc. Here's how it was done! The special 58 | `@@` symbol on the command line gets replaced by the file generated by 59 | tree-crasher. 60 | 61 | ```sh 62 | tree-crasher-rust \ 63 | --interesting-stderr "(?m)^error: internal compiler error:" \ 64 | corpus \ 65 | -- \ 66 | rustc +nightly --crate-type=lib --emit=mir -Zmir-opt-level=4 @@.rs 67 | ``` 68 | 69 | (The regex syntax is that of the 70 | [regex crate](https://docs.rs/regex/latest/regex/).) 71 | 72 | Here's how to limit the amount of memory taken by tree-crasher and rustc using 73 | `systemd-run`, and drop network access using `unshare`: 74 | 75 | ```sh 76 | systemd-run --scope -p MemoryMax=16G -p MemorySwapMax=0B --user \ 77 | unshare -Umn \ 78 | tree-crasher-rust \ 79 | --interesting-stderr "(?m)^error: internal compiler error:" \ 80 | corpus \ 81 | -- \ 82 | rustc +nightly --crate-type=lib --emit=mir -Zmir-opt-level=4 @@.rs 83 | ``` 84 | 85 | ### SQL databases 86 | 87 | Obtain a collection of SQL files (for example, using 88 | [this script](./scripts/corpora/sql.sh)). Then here's how to fuzz DuckDB: 89 | 90 | ```sh 91 | tree-crasher-sql --interesting-stderr "INTERNAL Error" corpus/ -- duckdb 92 | ``` 93 | 94 | Sometimes, you keep running into the same bug and would like to stop reporting 95 | it. For that, you can use `--uninteresting-stderr`: 96 | ```sh 97 | tree-crasher-sql \ 98 | --interesting-stderr "INTERNAL Error" \ 99 | --uninteresting-stderr "INTERNAL Error.+HyperLogLog::ComputeHashes" \ 100 | corpus \ 101 | -- \ 102 | duckdb 103 | ``` 104 | 105 | ## Even more examples 106 | 107 | Clang (frontend) (see also [this script](../scripts/corpora/c.sh)): 108 | 109 | ```sh 110 | tree-crasher-c corpus/ --interesting-stderr "(?m)^PLEASE " -- clang -c -O0 -o /dev/null -emit-llvm -Xclang -disable-llvm-passes @@.c 111 | ``` 112 | 113 | [`deno fmt`](https://deno.land/manual@v1.31.3/tools/formatter): 114 | ```sh 115 | tree-crasher-javascript corpus/ -- deno fmt @@.js 116 | ``` 117 | 118 | [ClickHouse](https://github.com/ClickHouse/ClickHouse): 119 | ```sh 120 | tree-crasher-sql corpus/ -- clickhouse local 121 | ``` 122 | 123 | -------------------------------------------------------------------------------- /rustc-flags: -------------------------------------------------------------------------------- 1 | --deny=absolute_paths_not_starting_with_crate 2 | --deny=dead_code 3 | --deny=elided_lifetimes_in_paths 4 | --deny=explicit_outlives_requirements 5 | --deny=keyword_idents 6 | --deny=let_underscore_drop 7 | --deny=macro_use_extern_crate 8 | --deny=meta_variable_misuse 9 | --deny=missing_abi 10 | --deny=missing_debug_implementations 11 | --deny=non_ascii_idents 12 | --deny=noop_method_call 13 | --deny=pointer_structural_match 14 | --deny=rust_2021_incompatible_closure_captures 15 | --deny=rust_2021_incompatible_or_patterns 16 | --deny=rust_2021_prefixes_incompatible_syntax 17 | --deny=rust_2021_prelude_collisions 18 | --deny=single_use_lifetimes 19 | --deny=trivial_casts 20 | --deny=trivial_numeric_casts 21 | --deny=unreachable_pub 22 | --deny=unsafe_op_in_unsafe_fn 23 | --deny=unstable_features 24 | --deny=unused_extern_crates 25 | --deny=unused_import_braces 26 | --deny=unused_lifetimes 27 | --deny=unused_macro_rules 28 | --deny=unused_qualifications 29 | --deny=variant_size_differences 30 | --deny=warnings 31 | -------------------------------------------------------------------------------- /scripts/corpora/c.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | repos=( 6 | "gcc-mirror/gcc" 7 | "llvm/llvm-project" 8 | ) 9 | mkdir -p c 10 | for repo in "${repos[@]}"; do 11 | base=$(basename "${repo}") 12 | if ! [[ -d "${base}" ]]; then 13 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 14 | fi 15 | for f in $(find "${base}" -type f -name "*.c"); do 16 | echo "${f}" 17 | cp "${f}" c/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 18 | done 19 | done 20 | -------------------------------------------------------------------------------- /scripts/corpora/css.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | repos=( 6 | "twbs/bootstrap" 7 | "web-platform-tests/wpt" 8 | ) 9 | mkdir -p css 10 | for repo in "${repos[@]}"; do 11 | base=$(basename "${repo}") 12 | if ! [[ -d "${base}" ]]; then 13 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 14 | fi 15 | for f in $(find "${base}" -type f -name "*.css"); do 16 | echo "${f}" 17 | cp "${f}" css/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 18 | done 19 | done 20 | -------------------------------------------------------------------------------- /scripts/corpora/js.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | repos=( 6 | "tc39/test262" 7 | "jquery/esprima" 8 | "v8/v8" 9 | "mozilla/gecko-dev" 10 | "svaarala/duktape" 11 | "Samsung/escargot" 12 | "jerryscript-project/jerryscript" 13 | "chakra-core/ChakraCore" 14 | "boa-dev/boa" 15 | "cesanta/elk" 16 | "Starlight-JS/starlight" 17 | "denoland/deno" 18 | "facebook/hermes" 19 | ) 20 | mkdir -p js 21 | for repo in "${repos[@]}"; do 22 | base=$(basename "${repo}") 23 | if ! [[ -d "${base}" ]]; then 24 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 25 | fi 26 | for f in $(find "${base}" -type f -name "*.js"); do 27 | echo "${f}" 28 | cp "${f}" js/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 29 | done 30 | done 31 | -------------------------------------------------------------------------------- /scripts/corpora/python.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | repos=( 6 | "python/cpython" 7 | "mozillazg/pypy" 8 | ) 9 | mkdir -p python 10 | for repo in "${repos[@]}"; do 11 | base=$(basename "${repo}") 12 | if ! [[ -d "${base}" ]]; then 13 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 14 | fi 15 | for f in $(find "${base}" -type f -name "*.py"); do 16 | echo "${f}" 17 | cp "${f}" python/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 18 | done 19 | done 20 | -------------------------------------------------------------------------------- /scripts/corpora/ruby.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | repos=( 6 | "rails/rails" 7 | "sparklemotion/nokogiri" 8 | "Homebrew/brew" 9 | ) 10 | mkdir -p ruby 11 | for repo in "${repos[@]}"; do 12 | base=$(basename "${repo}") 13 | if ! [[ -d "${base}" ]]; then 14 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 15 | fi 16 | for f in $(find "${base}" -type f -name "*.rb"); do 17 | echo "${f}" 18 | cp "${f}" ruby/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 19 | done 20 | done 21 | -------------------------------------------------------------------------------- /scripts/corpora/sol.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | repos=( 6 | "ethereum/solidity" 7 | ) 8 | mkdir -p sol 9 | for repo in "${repos[@]}"; do 10 | base=$(basename "${repo}") 11 | if ! [[ -d "${base}" ]]; then 12 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 13 | fi 14 | for f in $(find "${base}" -type f -name "*.sol"); do 15 | echo "${f}" 16 | cp "${f}" sol/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 17 | done 18 | done 19 | -------------------------------------------------------------------------------- /scripts/corpora/sql.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | shopt -s globstar 6 | shopt -s nullglob 7 | 8 | repos=( 9 | "ClickHouse/ClickHouse" 10 | "cockroachdb/cockroach" 11 | "duckdb/duckdb" 12 | "MonetDB/MonetDB" 13 | "postgres/postgres" 14 | "sqlite/sqlite" 15 | ) 16 | mkdir -p sql 17 | for repo in "${repos[@]}"; do 18 | base=$(basename "${repo}") 19 | if ! [[ -d "${base}" ]]; then 20 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 21 | fi 22 | pushd "${base}" 23 | if [[ "${base}" != ClickHouse ]]; then 24 | git submodule update --init 25 | fi 26 | popd 27 | for f in ./"${base}"/**/*.sql; do 28 | cp "${f}" sql/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 29 | done 30 | done 31 | 32 | for f in sql/*.sql; do 33 | if file "${f}" | grep -E '(ISO-8859|Non-ISO|: data)' > /dev/null 2>&1; then 34 | rm "${f}" 35 | fi 36 | done 37 | -------------------------------------------------------------------------------- /scripts/corpora/ts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | repos=( 6 | "angular/angular" 7 | "denoland/deno" 8 | "microsoft/TypeScript" 9 | "microsoft/vscode" 10 | "swc-project/swc" 11 | ) 12 | mkdir -p ts 13 | for repo in "${repos[@]}"; do 14 | base=$(basename "${repo}") 15 | if ! [[ -d "${base}" ]]; then 16 | git clone --jobs 4 --depth 1 "https://github.com/${repo}" 17 | fi 18 | for f in $(find "${base}" -type f -name "*.ts"); do 19 | echo "${f}" 20 | cp "${f}" ts/"${base}-$(sha256sum "${f}" | head -c 5)-$(basename "${f}")" 21 | done 22 | done --------------------------------------------------------------------------------