diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6d8e3b..9b8a6d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,167 +6,65 @@ on: pull_request: branches: [main] -env: - CARGO_TERM_COLOR: always - RUSTFLAGS: -D warnings +permissions: + contents: read jobs: - fmt: - name: Format - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - run: cargo fmt --check - - clippy: - name: Clippy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 # v2.7.8 - - run: cargo clippy --all-targets -- -D warnings - - test: - name: Test (${{ matrix.os }}) - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 # v2.7.8 - - run: cargo test - - msrv: - name: MSRV (1.85) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@1.85 - - uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 # v2.7.8 - - run: cargo build - - coverage: - name: Coverage (100% lines) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@stable - with: - components: llvm-tools-preview - - uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 # v2.7.8 - - uses: taiki-e/install-action@59012be0884e296ca2da49b530610e72c49039ad # v2.81.6 - with: - tool: cargo-llvm-cov - # The library's I/O is generic over the reader, so each error closure is - # monomorphized per reader type — making cargo-llvm-cov's region/function - # metrics structurally < 100%. The meaningful, monomorphization-robust - # invariant is that every executable source line is hit at least once. - # - # Exception — panic-free defence in depth: a line carrying a - # `// cov:unreachable` marker is a defensive guard (a `let-else { continue }`, - # a bounds-checked `.get()` fallback, etc.) that is provably unreachable - # under a dominating invariant, kept deliberately to stay panic-free if that - # invariant is ever broken by a future change. Such lines cannot be exercised - # by any test and are exempt. The gate fails on any OTHER zero-hit line. - - name: Generate lcov - run: cargo llvm-cov --lcov --output-path lcov.info --ignore-filename-regex '(^|/)src/(main\.rs|bin/)' - - name: Enforce line coverage (unreachable defensive arms must carry // cov:unreachable) - shell: bash - run: | - fail=0 - while IFS= read -r line; do - if [[ "$line" == SF:* ]]; then - f="${line#SF:}" - elif [[ "$line" =~ ^DA:([0-9]+),0$ ]]; then - n="${BASH_REMATCH[1]}" - src="$(sed -n "${n}p" "$f")" - if [[ "$src" == *cov:unreachable* ]]; then - echo "exempt (// cov:unreachable): $f:$n" - else - echo "::error::Uncovered line $f:$n:$src" - fail=1 - fi - fi - done < lcov.info - if [[ "$fail" -ne 0 ]]; then exit 1; fi - echo "All executable lines covered, or annotated // cov:unreachable." - - deny: - name: cargo-deny - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20 - with: - command: check - - vet: - name: Cargo Vet (supply-chain) - runs-on: ubuntu-latest - # Complements `deny` (known-bad advisories/licenses) with the supply-chain- - # injection layer: every dependency version must be human-source-reviewed or - # covered by an imported aggregate audit set (Google/Mozilla/Bytecode-Alliance/ - # Embark). Config in supply-chain/{config,audits,imports}.toml. - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@stable - - name: Install cargo-vet - uses: taiki-e/install-action@59012be0884e296ca2da49b530610e72c49039ad # v2.81.6 - with: - tool: cargo-vet - - name: Fetch dependencies - run: cargo fetch - - name: Check supply chain - run: cargo vet --locked - - secrets: - name: Secret Scan (gitleaks) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Install gitleaks - run: | - VERSION=$(curl -s https://api.github.com/repos/gitleaks/gitleaks/releases/latest | jq -r '.tag_name[1:]') - curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" \ - | tar xz -C /tmp gitleaks - - name: Run gitleaks - run: /tmp/gitleaks detect --source . - - fuzz: - name: Fuzz targets (nightly) - runs-on: ubuntu-latest - # cargo-fuzz builds with the host nightly; warnings in its own deps must not - # fail the install, so this job does not deny warnings. - env: - RUSTFLAGS: "" - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@nightly - - uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 # v2.7.8 - - run: cargo install cargo-fuzz - - name: Build all fuzz targets - run: cargo +nightly fuzz check - - name: Smoke-fuzz each target (30s) - run: | - for t in boot record attributes runlist index_buffer attribute_list; do - cargo +nightly fuzz run "$t" -- -max_total_time=30 -rss_limit_mb=4096 - done + # fmt · clippy · test (3 OS) · MSRV · cargo-deny · cargo-vet · secret scan · + # fuzz build-check · per-line coverage gate. + # + # No overrides: MSRV derives from rust-version (1.85) and the root fuzz/ crate + # is discovered automatically. + # + # COVERAGE — deliberately `floor`, not the fleet-standard `strict`. This is + # migration debt with a named end state, recorded here rather than hidden. + # + # Adopting fleet-ci widens the coverage SCOPE. The workflow being replaced ran + # `cargo llvm-cov --lib` with no features, so core/src/vfs.rs — the whole `vfs` + # feature adapter — was never compiled during coverage and therefore never + # measured. The old job was named "Coverage (100% lines)"; that number was true + # of what it looked at and silent about a feature-gated module. + # + # At the fleet scope vfs.rs started at 37 uncovered lines. Tests for the + # adapter's refusal and degradation paths took that to 27 (94.18% for the file, + # 99.27% workspace-wide). + # + # The remaining 27 are NOT unreachable, and are not annotated + # `// cov:unreachable` for that reason — that annotation asserts a proven + # invariant, and writing one where the truth is "untested" would be a false + # statement that survives review by looking rigorous. They are reachable error + # branches that need fixtures this repo does not have: + # + # volume_label degradation — needs a volume whose $Volume record or + # $VOLUME_NAME attribute is absent + # .map_err(map_err) arms in — needs records crafted to make read_record or + # read_dir/extents/lookup/ directory_entries fail part-way through a walk + # meta/read_at on an otherwise-mountable volume + # build_meta $SI branches — needs a record with a malformed + # $STANDARD_INFORMATION + # + # `coverage-ignore-regex: 'core/src/vfs\.rs'` was considered and rejected: it + # would disable the gate for all 443 lines of the file to excuse 27, so a later + # regression in the 416 that ARE covered would go unreported. The floor keeps + # every line measured and still fails on a real drop. + # + # 99 is set just below the current 99.27%, so this holds the line rather than + # granting slack. REMOVE WHEN: the fixtures above exist — then drop these two + # inputs and inherit the fleet `strict` gate. + ci: + uses: SecurityRonin/fleet-ci/.github/workflows/rust-ci.yml@f49dff5ddb19b17f69926210abb70f78c14c29e2 + with: + coverage-gate: floor + coverage-floor: 99 + # Repo-specific, so it stays here rather than moving into the shared workflow. docs: name: Docs runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 # v2.7.8 + - uses: dtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060c # master + with: + toolchain: stable + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 - run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps diff --git a/Cargo.lock b/Cargo.lock index a222119..5e90eed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -29,6 +30,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -74,7 +81,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -85,7 +92,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -127,12 +134,6 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "bytecount" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" - [[package]] name = "byteorder" version = "1.5.0" @@ -140,35 +141,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "camino" -version = "1.2.4" +name = "bytes" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" -dependencies = [ - "serde_core", -] - -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", -] +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" @@ -235,15 +211,14 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "console" -version = "0.15.11" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" dependencies = [ "encode_unicode", "libc", - "once_cell", "unicode-width", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -288,6 +263,37 @@ dependencies = [ "memchr", ] +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.19", +] + [[package]] name = "deranged" version = "0.5.8" @@ -307,9 +313,9 @@ dependencies = [ [[package]] name = "dialoguer" -version = "0.10.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96" dependencies = [ "console", "shell-words", @@ -417,16 +423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", + "windows-sys", ] [[package]] @@ -435,6 +432,18 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +[[package]] +name = "faststr" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca7d44d22004409a61c393afb3369c8f7bb74abcae49fe249ee01dcc3002113" +dependencies = [ + "bytes", + "rkyv", + "serde", + "simdutf8", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -451,6 +460,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "forensic-vfs" version = "0.7.0" @@ -513,13 +528,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.17" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "wasi", + "r-efi 5.3.0", + "wasip2", ] [[package]] @@ -530,22 +546,18 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", ] -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "ahash", + "allocator-api2", + "equivalent", + "foldhash", ] [[package]] @@ -605,9 +617,9 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" -version = "0.10.5" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -618,6 +630,42 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "js-sys" version = "0.3.103" @@ -649,11 +697,11 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "lru" -version = "0.9.0" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.16.1", ] [[package]] @@ -673,30 +721,30 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "mft" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cf53faa705fc7f6574f99a34fae16ffa12b6391eabd9f7269377738e583e99" +checksum = "8100af83e59845118eb3912ab6b1228380bab82c3535eba0050944d90c7eb4be" dependencies = [ "anyhow", - "bitflags 1.3.2", + "bitflags 2.13.1", "byteorder", - "chrono", "clap", "csv", "dialoguer", "encoding", "indoc", "itertools", + "jiff", "log", "lru", - "num-derive", + "num-derive 0.4.2", "num-traits", "rand", "serde", "serde_json", "simplelog", - "skeptic", - "thiserror 1.0.69", + "sonic-rs", + "thiserror 2.0.19", "winstructs", ] @@ -710,6 +758,26 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "munge" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e17401f259eba956ca16491461b6e8f72913a0a114e39736ce404410f915a0c" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "ntfs-core" version = "0.9.6" @@ -752,6 +820,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -788,6 +867,21 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "portable-atomic" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -813,14 +907,23 @@ dependencies = [ ] [[package]] -name = "pulldown-cmark" -version = "0.9.6" +name = "ptr_meta" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +checksum = "743da816b98c921cdbe8628ef7381b76f25ecf4da599fc80aca90eae7ef70cc0" dependencies = [ - "bitflags 2.13.1", - "memchr", - "unicase", + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c8d9ca532f185d5d4db7a7c9d51420b452168ea1c2b913953281bd6fe1fcbd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.2", ] [[package]] @@ -832,28 +935,42 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rancor" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b534442d0fcdb55d66f373d9cac6d33b6293a2335bc2136dbd06ce0e87d2572" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" -version = "0.8.7" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -861,11 +978,31 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "ref-cast" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ - "getrandom 0.2.17", + "proc-macro2", + "quote", + "syn 3.0.2", ] [[package]] @@ -897,6 +1034,41 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "rend" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "663ba70707f96e871406fe10d68128412e619b06d1d47cb91c3a4c6501176240" + +[[package]] +name = "rkyv" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9776093b7ca170454ab1406954f7b7d97a57c51dc6c0642957fb2ef25c2d399" +dependencies = [ + "bytes", + "hashbrown 0.17.1", + "indexmap", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c25ef604ac7dd839d44d64648952ea23c97866f124ff671b0ed2cf3ad9bb06e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.2", +] + [[package]] name = "rustix" version = "1.1.4" @@ -907,7 +1079,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -928,25 +1100,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4487a19fd870fa418cd95a218049eeb70514535bf2271715774d97d7caab1ce5" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde" version = "1.0.229" @@ -1008,6 +1161,12 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simplelog" version = "0.12.2" @@ -1020,25 +1179,49 @@ dependencies = [ ] [[package]] -name = "skeptic" -version = "0.13.7" +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "sonic-number" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +checksum = "3775c3390edf958191f1ab1e8c5c188907feebd0f3ce1604cb621f72961dbf32" dependencies = [ - "bytecount", - "cargo_metadata", - "error-chain", - "glob", - "pulldown-cmark", - "tempfile", - "walkdir", + "cfg-if", ] [[package]] -name = "slab" -version = "0.4.12" +name = "sonic-rs" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +checksum = "d971cc77a245ccf1756dbd1a87c3e7f709c0191464096510d43eec056d0f2c4f" +dependencies = [ + "ahash", + "bumpalo", + "bytes", + "cfg-if", + "faststr", + "itoa", + "ref-cast", + "serde", + "simdutf8", + "sonic-number", + "sonic-simd", + "thiserror 2.0.19", + "zmij", +] + +[[package]] +name = "sonic-simd" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99e664ecd2d85a68c87e3c7a3cfe691f647ea9e835de984aba4d54a41f817d4" +dependencies = [ + "cfg-if", +] [[package]] name = "state-history-forensic" @@ -1095,7 +1278,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1180,10 +1363,19 @@ dependencies = [ ] [[package]] -name = "unicase" -version = "2.9.0" +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "unicode-ident" @@ -1203,6 +1395,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1210,21 +1412,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "walkdir" -version = "2.5.0" +name = "wasip2" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "same-file", - "winapi-util", + "wit-bindgen", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasm-bindgen" version = "0.2.126" @@ -1276,7 +1471,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -1338,15 +1533,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -1356,70 +1542,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "winstructs" version = "0.3.2" @@ -1430,13 +1552,19 @@ dependencies = [ "byteorder", "chrono", "log", - "num-derive", + "num-derive 0.3.3", "num-traits", "serde", "serde_json", "thiserror 1.0.69", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "zerocopy" version = "0.8.54" diff --git a/Cargo.toml b/Cargo.toml index c67106d..664cdd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ lznt1 = "0.1" bitflags = "2" chrono = "0.4" regex = "1" -mft = "0.6" +mft = "0.7" [workspace.lints.rust] unsafe_code = "forbid" diff --git a/core/src/vfs.rs b/core/src/vfs.rs index c6a717c..0857b0a 100644 --- a/core/src/vfs.rs +++ b/core/src/vfs.rs @@ -510,8 +510,184 @@ impl FileSystem for NtfsFs { #[cfg(test)] mod tests { - use super::{free_runs, unallocated_runs}; - use forensic_vfs::RunAlloc; + use super::{ + best_file_name, build_meta, entry_of, free_runs, map_err, namespace_rank, stream_name, + unallocated_runs, + }; + use crate::error::NtfsError; + use forensic_vfs::{FileId, RunAlloc, StreamId, VfsError}; + use forensicnomicon::ntfs::filename_namespace; + + // ---- identity and stream translation ------------------------------------- + // + // The happy-path tests run against a real volume, so they only ever pass a + // well-formed NtfsRef and the default stream. These cover the refusals: the + // adapter must reject an identity it cannot address rather than coerce it + // into a plausible-looking record number. + + #[test] + fn entry_of_rejects_a_non_ntfs_identity() { + // An ext4 inode is a different identity domain entirely. Silently reading + // MFT record 42 because the number happens to fit would fabricate a result. + let err = entry_of(FileId::ExtInode { ino: 42, gen: 1 }) + .expect_err("non-NTFS id must be refused"); + match err { + VfsError::Unsupported { layer, scheme } => { + assert_eq!(layer, "ntfs file-id"); + assert!( + scheme.contains("42"), + "the refusal must show the offending value, got {scheme:?}" + ); + } + other => panic!("expected Unsupported, got {other:?}"), + } + } + + #[test] + fn entry_of_accepts_an_ntfs_reference() { + assert_eq!( + entry_of(FileId::NtfsRef { entry: 5, seq: 1 }).expect("NtfsRef is addressable"), + 5 + ); + } + + #[test] + fn stream_name_refuses_a_named_stream_rather_than_reading_the_default() { + // A named-stream id cannot be mapped back to its ADS name. Falling back to + // the default $DATA would return the wrong bytes under a right-looking + // request — the failure mode this refusal exists to prevent. + let err = stream_name(StreamId::Named(7)).expect_err("named stream must be refused"); + assert!( + matches!(err, VfsError::Unsupported { layer, .. } if layer == "ntfs stream"), + "expected an ntfs stream refusal" + ); + } + + #[test] + fn stream_name_maps_the_default_stream_to_none() { + assert_eq!( + stream_name(StreamId::Default).expect("default stream is addressable"), + None + ); + } + + // ---- error translation --------------------------------------------------- + + #[test] + fn map_err_keeps_io_distinct_from_decode() { + // The distinction is load-bearing: an I/O failure means the evidence could + // not be read (a bootstrap problem), while a decode failure means the bytes + // were read and are malformed. Collapsing them would let a failed read + // masquerade as a corrupt filesystem. + let io = map_err(NtfsError::Io(std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "short read", + ))); + assert!( + matches!(io, VfsError::Io { op, .. } if op == "ntfs read"), + "an NtfsError::Io must stay an I/O error" + ); + } + + #[test] + fn map_err_carries_the_original_message_into_decode() { + let decoded = map_err(NtfsError::BadRecordSignature(*b"BAAD")); + match decoded { + VfsError::Decode { layer, detail, .. } => { + assert_eq!(layer, "ntfs"); + assert!( + !detail.is_empty(), + "the original ntfs-core message must survive, not be dropped" + ); + } + other => panic!("expected Decode, got {other:?}"), + } + } + + // ---- name-link ranking --------------------------------------------------- + + #[test] + fn namespace_rank_prefers_the_human_name_over_the_8_3_short_name() { + // A record commonly carries both a Win32 name and a DOS 8.3 name. Ranking + // decides which one an examiner sees, so the ordering is asserted whole + // rather than one arm at a time. + let dos = namespace_rank(filename_namespace::DOS); + assert!( + namespace_rank(filename_namespace::WIN32_AND_DOS) + > namespace_rank(filename_namespace::WIN32) + && namespace_rank(filename_namespace::WIN32) + > namespace_rank(filename_namespace::POSIX) + && namespace_rank(filename_namespace::POSIX) > dos, + "ranking must be WIN32_AND_DOS > WIN32 > POSIX > DOS" + ); + } + + #[test] + fn namespace_rank_treats_an_unknown_namespace_as_least_preferred() { + // An unrecognised namespace byte must never outrank a real Win32 name. + assert!(namespace_rank(0xAB) < namespace_rank(filename_namespace::WIN32)); + } + + // ---- refusing to fabricate on malformed records -------------------------- + + #[test] + fn best_file_name_returns_none_on_a_record_that_does_not_parse() { + // Not a panic and not an invented name: a record whose header will not + // parse yields no name, so the caller cannot present a fabricated one. + assert!(best_file_name(&[0u8; 64]).is_none()); + assert!(best_file_name(&[]).is_none()); + assert!(best_file_name(&[0xFF; 1024]).is_none()); + } + + /// A record whose header parses but whose attribute offset points outside + /// the buffer — the shape a corrupt or carved record actually has. + fn header_valid_attrs_broken() -> Vec { + let mut rec = vec![0u8; 1024]; + rec[..4].copy_from_slice(b"FILE"); + // mft_offsets::FIRST_ATTRIBUTE (0x14): past the end of the record. + rec[0x14..0x16].copy_from_slice(&0xFFF0u16.to_le_bytes()); + rec + } + + #[test] + fn build_meta_on_a_broken_attribute_list_fabricates_no_timestamps() { + // Documents observed behaviour, which is NOT what I first assumed: an + // attribute offset pointing past the record does not produce an error. + // parse_attributes degrades to an empty list, so build_meta returns Ok. + // + // What must hold is that the degradation stays honest — no panic, and no + // invented facts. Every MAC(B) time is None rather than a zeroed + // FILETIME, because "1601-01-01" rendered in a timeline is a claim about + // the evidence that nothing in the record supports. + let meta = build_meta(7, &header_valid_attrs_broken()) + .expect("an empty attribute list degrades rather than erroring"); + assert_eq!(meta.ino, 7); + assert!( + meta.times.born.is_none() + && meta.times.modified.is_none() + && meta.times.changed.is_none() + && meta.times.accessed.is_none(), + "a record whose attributes did not parse must carry no timestamps at all" + ); + assert_eq!(meta.size, 0, "no $DATA means no size, not a guess"); + } + + #[test] + fn best_file_name_yields_none_when_attributes_do_not_parse() { + // Same record shape through the naming path: no name is better than a + // name recovered from an attribute list that did not parse. + assert!(best_file_name(&header_valid_attrs_broken()).is_none()); + } + + #[test] + fn build_meta_surfaces_a_malformed_record_as_an_error() { + // build_meta must not return a default-looking FsMeta for bytes it could + // not parse — an all-zero record is exactly what carving hands it. + assert!( + build_meta(0, &[0u8; 1024]).is_err(), + "a record with no valid header must be an error, not empty metadata" + ); + } #[test] fn free_runs_finds_maximal_zero_bit_runs() { diff --git a/core/tests/vfs_ntfs.rs b/core/tests/vfs_ntfs.rs index cf88c5d..289f750 100644 --- a/core/tests/vfs_ntfs.rs +++ b/core/tests/vfs_ntfs.rs @@ -44,6 +44,19 @@ fn open_real_volume() -> Arc { Arc::new(fs) } +/// The raw `partition.dd` bytes, so a test can mutate a real volume rather than +/// invent one from scratch. +fn raw_volume() -> Vec { + let mut archive = zip::ZipArchive::new(Cursor::new(SAMPLE_ZIP)).expect("open sample zip"); + let mut dd = Vec::new(); + archive + .by_name("SampleTinyNtfsVolume/partition.dd") + .expect("partition.dd present") + .read_to_end(&mut dd) + .expect("read partition.dd"); + dd +} + #[test] fn identity_matches_tsk_geometry() { let fs = open_real_volume(); @@ -365,3 +378,269 @@ fn extents_returns_mft_runs() { let total: u64 = runs.iter().map(|r| r.run.len).sum(); assert_eq!(total, 262_144); } + +// --------------------------------------------------------------------------- +// Refusals on a real volume. +// +// The tests above all address the filesystem correctly, so they only ever walk +// the happy path. These drive the same entry points with an identity from +// another filesystem and with a named stream, on a volume that is genuinely +// mounted — so a refusal here is the adapter's decision, not a failure to open +// the image. +// +// This matters more than coverage arithmetic. `FileId` is a fleet-wide union: +// an ext4 inode, an APFS oid and an NTFS reference are all just integers in a +// struct. If the NTFS adapter coerced `ExtInode { ino: 5 }` into MFT record 5, +// it would return real bytes from the wrong object under a request that looks +// entirely valid — a wrong answer rather than an error. +// --------------------------------------------------------------------------- + +/// An identity belonging to another filesystem, structurally valid but not NTFS. +fn foreign_id() -> FileId { + FileId::ExtInode { ino: 5, gen: 1 } +} + +#[test] +fn every_entry_point_refuses_a_foreign_file_id() { + let fs = open_real_volume(); + let bad = foreign_id(); + + assert!( + fs.read_dir(bad).is_err(), + "read_dir must refuse a non-NTFS id" + ); + assert!(fs.meta(bad).is_err(), "meta must refuse a non-NTFS id"); + assert!( + fs.lookup(bad, b"anything").is_err(), + "lookup must refuse a non-NTFS parent id" + ); + assert!( + fs.extents(bad, StreamId::Default).is_err(), + "extents must refuse a non-NTFS id" + ); + let mut buf = [0u8; 16]; + assert!( + fs.read_at(bad, StreamId::Default, 0, &mut buf).is_err(), + "read_at must refuse a non-NTFS id" + ); +} + +#[test] +fn byte_paths_refuse_a_named_stream_rather_than_serving_the_default() { + // A named-stream id cannot be mapped back to its ADS name. Serving $DATA + // instead would hand back the wrong stream's bytes for an ADS request — + // silently, and with no way for the caller to tell. + let fs = open_real_volume(); + let root = FileId::NtfsRef { entry: 5, seq: 5 }; + + assert!( + fs.extents(root, StreamId::Named(1)).is_err(), + "extents must refuse a named stream" + ); + let mut buf = [0u8; 16]; + assert!( + fs.read_at(root, StreamId::Named(1), 0, &mut buf).is_err(), + "read_at must refuse a named stream" + ); +} + +#[test] +fn a_valid_ntfs_id_still_works_after_the_refusals() { + // Control for the two tests above: they must fail because the identity is + // foreign, not because this volume rejects everything. The root directory + // resolves on the same handle. + let fs = open_real_volume(); + let root = FileId::NtfsRef { entry: 5, seq: 5 }; + assert!( + fs.meta(root).is_ok(), + "the root record must still resolve — otherwise the refusals prove nothing" + ); +} + +#[test] +fn an_out_of_range_record_is_an_error_not_a_panic_or_fabricated_metadata() { + // The identity is well-formed NTFS — it just points past the end of this + // volume's $MFT. That is what a corrupt index entry or a carved reference + // looks like, and it must surface as a typed error on every entry point + // rather than panicking or returning default-looking metadata that an + // examiner would read as fact. + let fs = open_real_volume(); + let far = FileId::NtfsRef { + entry: u64::MAX / 2, + seq: 1, + }; + + assert!( + fs.meta(far).is_err(), + "meta must reject a record past the MFT" + ); + assert!( + fs.read_dir(far).is_err(), + "read_dir must reject a record past the MFT" + ); + assert!( + fs.lookup(far, b"x").is_err(), + "lookup must reject a parent past the MFT" + ); + assert!( + fs.extents(far, StreamId::Default).is_err(), + "extents must reject a record past the MFT" + ); + let mut buf = [0u8; 16]; + assert!( + fs.read_at(far, StreamId::Default, 0, &mut buf).is_err(), + "read_at must reject a record past the MFT" + ); +} + +// --------------------------------------------------------------------------- +// Degradation on a damaged volume (T2 — real image, documented mutation). +// +// These are not synthetic volumes invented wholesale. Each starts from the same +// real partition.dd the tests above validate against TSK, and applies one +// mutation stated in the test. The expected outcome follows from the +// construction — "the image now ends at byte N, so any record stored past N +// cannot be read" — rather than from an expected-answer chosen by the author. +// +// What they assert is the property that matters for a forensic reader: damage +// must surface as a typed error or an absent value. It must never panic, and it +// must never degrade into a confident-looking empty answer, because a report +// cannot tell the difference between "no label" and "could not read the label". +// --------------------------------------------------------------------------- + +/// Truncate the volume to `keep` bytes: the boot sector and the start of $MFT +/// survive, so the filesystem still mounts, but records stored beyond the cut +/// are unreadable. This is what a partial image or a bad-sector run looks like. +fn truncated_volume(keep: usize) -> Option>>> { + let mut dd = raw_volume(); + dd.truncate(keep); + NtfsFs::open(Cursor::new(dd)).ok() +} + +#[test] +fn a_truncated_volume_either_refuses_to_mount_or_degrades_without_panicking() { + // Sweep the cut point across the image. Every outcome is acceptable except a + // panic: NtfsFs::open may reject the volume outright, or it may mount and + // then fail per-record. What must not happen is an unwind, and what must not + // happen is a fabricated answer. + for keep in [512usize, 4096, 65_536, 1 << 20, 3 << 20] { + let Some(fs) = truncated_volume(keep) else { + continue; // refused at mount time — the loud path, also fine + }; + + // A label that cannot be read is None, never a placeholder string. + if let Some(label) = fs.volume_label() { + assert!( + !label.is_empty(), + "an unreadable $Volume must yield None, not an empty label at keep={keep}" + ); + } + + // Walk records well past the surviving bytes. Each entry point must + // return Ok or Err — the assertion is that control returns at all. + for entry in [5u64, 64, 4096, 65_536] { + let id = FileId::NtfsRef { entry, seq: 1 }; + let _ = fs.meta(id); + let _ = fs.read_dir(id); + let _ = fs.lookup(id, b"probe"); + let _ = fs.extents(id, StreamId::Default); + let mut buf = [0u8; 32]; + let _ = fs.read_at(id, StreamId::Default, 0, &mut buf); + } + } +} + +#[test] +fn truncation_actually_removes_readable_records() { + // Control for the sweep above. If every truncation still mounted a fully + // readable volume, the test would pass while exercising nothing — the + // "green over work never performed" failure mode. At least one cut point + // must produce a volume where a record the intact image serves is no longer + // readable. + let intact = open_real_volume(); + let root = FileId::NtfsRef { entry: 5, seq: 5 }; + assert!( + intact.meta(root).is_ok(), + "precondition: the intact volume serves its root record" + ); + + let damaged_somewhere = + [512usize, 4096, 65_536, 1 << 20] + .into_iter() + .any(|keep| match truncated_volume(keep) { + None => true, // refused to mount: damage observed + Some(fs) => (0..4096u64) + .step_by(64) + .any(|entry| fs.meta(FileId::NtfsRef { entry, seq: 1 }).is_err()), + }); + assert!( + damaged_somewhere, + "no truncation produced an unreadable record — the mutation is not biting, \ + so the degradation sweep proves nothing" + ); +} + +/// Offsets of every copy of MFT record `n` in this sample volume, found by +/// scanning rather than assumed. +/// +/// The image carries both $MFT and its $MFTMirr, so record 3 exists twice. A +/// mutation that damages only one copy leaves the reader a good one to fall back +/// on — which is exactly what the first version of this test got wrong, and why +/// the offsets are derived from the image instead of hard-coded to one location. +fn mft_record_offsets(dd: &[u8], n: u32) -> Vec { + let mut out = Vec::new(); + let mut i = 0usize; + while let Some(rel) = dd[i..].windows(4).position(|w| w == b"FILE") { + let at = i + rel; + if at % 512 == 0 && at + 0x30 <= dd.len() { + let recno = + u32::from_le_bytes([dd[at + 0x2C], dd[at + 0x2D], dd[at + 0x2E], dd[at + 0x2F]]); + if recno == n { + out.push(at); + } + } + i = at + 4; + } + out +} + +#[test] +fn an_unreadable_volume_record_yields_no_label_rather_than_a_placeholder() { + // Documented mutation of the real image: overwrite the "FILE" signature on + // EVERY copy of MFT record 3 ($Volume) so its header will not parse, in both + // $MFT and $MFTMirr. $MFT's own record 0 is untouched, so the volume still + // mounts and the only thing lost is the label. + // + // The expected result follows from the construction rather than a chosen + // answer: with no readable $Volume there is no label, so the only honest + // output is None. A placeholder or empty string would be indistinguishable, + // in a report, from a volume genuinely named "". + let mut dd = raw_volume(); + let offsets = mft_record_offsets(&dd, 3); + assert!( + !offsets.is_empty(), + "precondition: the sample volume contains a $Volume record to damage" + ); + for off in &offsets { + dd[*off..*off + 4].copy_from_slice(b"XXXX"); + } + let fs = NtfsFs::open(Cursor::new(dd)).expect("volume still mounts; only $Volume was damaged"); + + assert_eq!( + fs.volume_label(), + None, + "an unparseable $Volume record must produce no label at all" + ); +} + +#[test] +fn the_intact_volume_does_report_a_label() { + // Control for the mutation above. Without it, `volume_label() == None` would + // pass equally well if this volume simply had no label, and the test would + // prove nothing about the damage. + let fs = open_real_volume(); + assert!( + fs.volume_label().is_some(), + "precondition: the intact sample volume carries a label" + ); +} diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 34eb6db..0c24348 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -62,20 +62,8 @@ criteria = "safe-to-deploy" version = "2.13.1" criteria = "safe-to-deploy" -[[exemptions.bytecount]] -version = "0.6.9" -criteria = "safe-to-run" - -[[exemptions.camino]] -version = "1.2.4" -criteria = "safe-to-run" - -[[exemptions.cargo-platform]] -version = "0.1.9" -criteria = "safe-to-run" - -[[exemptions.cargo_metadata]] -version = "0.14.2" +[[exemptions.bytes]] +version = "1.12.1" criteria = "safe-to-run" [[exemptions.cc]] @@ -107,7 +95,7 @@ version = "1.0.5" criteria = "safe-to-run" [[exemptions.console]] -version = "0.15.11" +version = "0.16.4" criteria = "safe-to-run" [[exemptions.crc32fast]] @@ -126,8 +114,20 @@ criteria = "safe-to-run" version = "0.1.13" criteria = "safe-to-run" +[[exemptions.defmt]] +version = "1.1.1" +criteria = "safe-to-run" + +[[exemptions.defmt-macros]] +version = "1.1.1" +criteria = "safe-to-run" + +[[exemptions.defmt-parser]] +version = "1.0.0" +criteria = "safe-to-run" + [[exemptions.dialoguer]] -version = "0.10.4" +version = "0.12.0" criteria = "safe-to-run" [[exemptions.displaydoc]] @@ -174,6 +174,10 @@ criteria = "safe-to-run" version = "2.5.0" criteria = "safe-to-run" +[[exemptions.faststr]] +version = "0.2.34" +criteria = "safe-to-run" + [[exemptions.find-msvc-tools]] version = "0.1.9" criteria = "safe-to-deploy" @@ -195,15 +199,15 @@ version = "0.3.33" criteria = "safe-to-deploy" [[exemptions.getrandom]] -version = "0.2.17" +version = "0.3.4" criteria = "safe-to-run" [[exemptions.getrandom]] version = "0.4.3" criteria = "safe-to-run" -[[exemptions.glob]] -version = "0.3.3" +[[exemptions.hashbrown]] +version = "0.14.5" criteria = "safe-to-run" [[exemptions.hashbrown]] @@ -214,10 +218,6 @@ criteria = "safe-to-run" version = "0.1.65" criteria = "safe-to-deploy" -[[exemptions.indexmap]] -version = "2.14.0" -criteria = "safe-to-run" - [[exemptions.indoc]] version = "2.0.7" criteria = "safe-to-run" @@ -230,6 +230,18 @@ criteria = "safe-to-run" version = "1.0.18" criteria = "safe-to-run" +[[exemptions.jiff]] +version = "0.2.35" +criteria = "safe-to-run" + +[[exemptions.jiff-core]] +version = "0.1.0" +criteria = "safe-to-run" + +[[exemptions.jiff-static]] +version = "0.2.35" +criteria = "safe-to-run" + [[exemptions.js-sys]] version = "0.3.103" criteria = "safe-to-deploy" @@ -247,7 +259,7 @@ version = "0.4.33" criteria = "safe-to-deploy" [[exemptions.lru]] -version = "0.9.0" +version = "0.16.4" criteria = "safe-to-run" [[exemptions.lznt1]] @@ -259,7 +271,15 @@ version = "2.8.3" criteria = "safe-to-deploy" [[exemptions.mft]] -version = "0.6.1" +version = "0.7.0" +criteria = "safe-to-run" + +[[exemptions.munge]] +version = "0.4.7" +criteria = "safe-to-run" + +[[exemptions.munge_macro]] +version = "0.4.7" criteria = "safe-to-run" [[exemptions.num-conv]] @@ -282,24 +302,56 @@ criteria = "safe-to-run" version = "0.2.17" criteria = "safe-to-deploy" +[[exemptions.portable-atomic]] +version = "1.14.0" +criteria = "safe-to-run" + +[[exemptions.portable-atomic-util]] +version = "0.2.7" +criteria = "safe-to-run" + [[exemptions.proc-macro2]] version = "1.0.107" criteria = "safe-to-deploy" -[[exemptions.pulldown-cmark]] -version = "0.9.6" +[[exemptions.ptr_meta]] +version = "0.3.2" criteria = "safe-to-run" -[[exemptions.quote]] -version = "1.0.47" -criteria = "safe-to-deploy" +[[exemptions.ptr_meta_derive]] +version = "0.3.2" +criteria = "safe-to-run" + +[[exemptions.r-efi]] +version = "5.3.0" +criteria = "safe-to-run" [[exemptions.r-efi]] version = "6.0.0" criteria = "safe-to-run" +[[exemptions.rancor]] +version = "0.1.3" +criteria = "safe-to-run" + [[exemptions.rand]] -version = "0.8.7" +version = "0.9.5" +criteria = "safe-to-run" + +[[exemptions.rand_chacha]] +version = "0.9.0" +criteria = "safe-to-run" + +[[exemptions.rand_core]] +version = "0.9.5" +criteria = "safe-to-run" + +[[exemptions.ref-cast]] +version = "1.0.26" +criteria = "safe-to-run" + +[[exemptions.ref-cast-impl]] +version = "1.0.26" criteria = "safe-to-run" [[exemptions.regex]] @@ -314,6 +366,18 @@ criteria = "safe-to-deploy" version = "0.8.11" criteria = "safe-to-deploy" +[[exemptions.rend]] +version = "0.5.4" +criteria = "safe-to-run" + +[[exemptions.rkyv]] +version = "0.8.18" +criteria = "safe-to-run" + +[[exemptions.rkyv_derive]] +version = "0.8.18" +criteria = "safe-to-run" + [[exemptions.rustix]] version = "1.1.4" criteria = "safe-to-run" @@ -326,10 +390,6 @@ criteria = "safe-to-deploy" version = "1.0.23" criteria = "safe-to-run" -[[exemptions.semver]] -version = "1.0.28" -criteria = "safe-to-run" - [[exemptions.serde]] version = "1.0.229" criteria = "safe-to-deploy" @@ -358,18 +418,30 @@ criteria = "safe-to-deploy" version = "0.3.10" criteria = "safe-to-run" -[[exemptions.simplelog]] -version = "0.12.2" +[[exemptions.simdutf8]] +version = "0.1.5" criteria = "safe-to-run" -[[exemptions.skeptic]] -version = "0.13.7" +[[exemptions.simplelog]] +version = "0.12.2" criteria = "safe-to-run" [[exemptions.slab]] version = "0.4.12" criteria = "safe-to-deploy" +[[exemptions.sonic-number]] +version = "0.1.2" +criteria = "safe-to-run" + +[[exemptions.sonic-rs]] +version = "0.5.8" +criteria = "safe-to-run" + +[[exemptions.sonic-simd]] +version = "0.1.4" +criteria = "safe-to-run" + [[exemptions.syn]] version = "2.0.119" criteria = "safe-to-deploy" @@ -406,24 +478,20 @@ criteria = "safe-to-run" version = "0.2.31" criteria = "safe-to-run" -[[exemptions.unicase]] -version = "2.9.0" +[[exemptions.tinyvec]] +version = "1.12.0" criteria = "safe-to-run" [[exemptions.unicode-ident]] version = "1.0.24" criteria = "safe-to-deploy" -[[exemptions.version_check]] -version = "0.9.5" -criteria = "safe-to-run" - -[[exemptions.walkdir]] -version = "2.5.0" +[[exemptions.uuid]] +version = "1.24.0" criteria = "safe-to-run" -[[exemptions.wasi]] -version = "0.11.1+wasi-snapshot-preview1" +[[exemptions.version_check]] +version = "0.9.5" criteria = "safe-to-run" [[exemptions.wasm-bindgen]] @@ -470,50 +538,10 @@ criteria = "safe-to-deploy" version = "0.5.1" criteria = "safe-to-deploy" -[[exemptions.windows-sys]] -version = "0.59.0" -criteria = "safe-to-run" - [[exemptions.windows-sys]] version = "0.61.2" criteria = "safe-to-run" -[[exemptions.windows-targets]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_aarch64_gnullvm]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_aarch64_msvc]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_i686_gnu]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_i686_gnullvm]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_i686_msvc]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_x86_64_gnu]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_x86_64_gnullvm]] -version = "0.52.6" -criteria = "safe-to-run" - -[[exemptions.windows_x86_64_msvc]] -version = "0.52.6" -criteria = "safe-to-run" - [[exemptions.winstructs]] version = "0.3.2" criteria = "safe-to-run" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index d18bf38..472ffa7 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -71,6 +71,18 @@ user-id = 1139 user-login = "Manishearth" user-name = "Manish Goregaokar" +[[publisher.wasip2]] +version = "1.0.4+wasi-0.2.12" +when = "2026-06-12" +user-id = 1 +user-login = "alexcrichton" +user-name = "Alex Crichton" + +[[publisher.wit-bindgen]] +version = "0.57.1" +when = "2026-04-17" +trusted-publisher = "github:bytecodealliance/wit-bindgen" + [[audits.bytecode-alliance.wildcard-audits.arbitrary]] who = "Nick Fitzgerald " criteria = "safe-to-deploy" @@ -94,17 +106,76 @@ start = "2020-01-14" end = "2026-08-21" notes = "I am an author of this crate" +[[audits.bytecode-alliance.wildcard-audits.wasip2]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 1 # Alex Crichton (alexcrichton) +start = "2025-08-10" +end = "2026-08-21" +notes = """ +This is a Bytecode Alliance authored crate. +""" + +[[audits.bytecode-alliance.wildcard-audits.wit-bindgen]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +trusted-publisher = "github:bytecodealliance/wit-bindgen" +start = "2025-08-13" +end = "2027-01-08" +notes = "The Bytecode Alliance is the author of this crate" + [[audits.bytecode-alliance.audits.adler2]] who = "Alex Crichton " criteria = "safe-to-deploy" version = "2.0.0" notes = "Fork of the original `adler` crate, zero unsfae code, works in `no_std`, does what it says on th tin." +[[audits.bytecode-alliance.audits.allocator-api2]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +delta = "0.2.18 -> 0.2.20" +notes = """ +The changes appear to be reasonable updates from Rust's stdlib imported into +`allocator-api2`'s copy of this code. +""" + +[[audits.bytecode-alliance.audits.foldhash]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.1.3" +notes = """ +Only a minor amount of `unsafe` code in this crate related to global per-process +initialization which looks correct to me. +""" + +[[audits.bytecode-alliance.audits.hashbrown]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +delta = "0.14.5 -> 0.15.2" + [[audits.bytecode-alliance.audits.iana-time-zone-haiku]] who = "Dan Gohman " criteria = "safe-to-deploy" version = "0.1.2" +[[audits.bytecode-alliance.audits.itertools]] +who = "Nick Fitzgerald " +criteria = "safe-to-deploy" +delta = "0.10.5 -> 0.12.1" +notes = """ +Minimal `unsafe` usage. Few blocks that existed looked reasonable. Does what it +says on the tin: lots of iterators. +""" + +[[audits.bytecode-alliance.audits.itertools]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.12.1 -> 0.14.0" +notes = """ +Lots of new iterators and shuffling some things around. Some new unsafe code but +it's well-documented and well-tested. Nothing suspicious. +""" + [[audits.bytecode-alliance.audits.miniz_oxide]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -147,6 +218,16 @@ criteria = "safe-to-deploy" version = "0.2.19" notes = "As advertised: a numeric library. The only `unsafe` is from some float-to-int conversions, which seems expected." +[[audits.bytecode-alliance.audits.tinyvec_macros]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.1.0" +notes = """ +This is a trivial crate which only contains a singular macro definition which is +intended to multiplex across the internal representation of a tinyvec, +presumably. This trivially doesn't contain anything bad. +""" + [[audits.embark.audits.utf8parse]] who = "Johan Andersson " criteria = "safe-to-deploy" @@ -221,17 +302,44 @@ delta = "1.0.1 -> 1.0.2" notes = "No changes to any .rs files or Rust code." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.error-chain]] -who = "George Burgess IV " -criteria = "safe-to-run" -version = "0.12.4" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.foldhash]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "0.1.3 -> 0.1.4" +notes = "No changes to safety-relevant code" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.hashbrown]] -who = "Nicholas Bishop " -criteria = "safe-to-run" -version = "0.13.2" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.foldhash]] +who = "Chris Palmer " +criteria = "safe-to-deploy" +delta = "0.1.4 -> 0.1.5" +notes = "No new `unsafe`." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.indexmap]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "2.7.1" +notes = ''' +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'`, `'\bnet\b'` +and there were no hits. + +There is a little bit of `unsafe` Rust code - the audit can be found at +https://chromium-review.googlesource.com/c/chromium/src/+/6187726/2 +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.indexmap]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "2.7.1 -> 2.8.0" +notes = """ +No `unsafe` introduced or affected in: +* `indexmap_with_default!` and `indexset_with_default!` macros +* New `PartialEq` implementations +* `fn slice_eq` in `util.rs` +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" [[audits.google.audits.itertools]] who = "ChromeOS" @@ -245,6 +353,12 @@ criteria = "safe-to-run" version = "0.3.3" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.num-derive]] +who = "George Burgess IV " +criteria = "safe-to-run" +delta = "0.3.3 -> 0.4.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.ppv-lite86]] who = "danakj@chromium.org" criteria = "safe-to-run" @@ -275,23 +389,55 @@ using an undocumented API that `zerocopy` has provided specifically for """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.rand_chacha]] -who = "Android Legacy" -criteria = "safe-to-run" -version = "0.3.1" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.quote]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.0.35" +notes = """ +Grepped for "unsafe", "crypt", "cipher", "fs", "net" - there were no hits +(except for benign "net" hit in tests and "fs" hit in README.md) +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.rand_core]] -who = "Android Legacy" -criteria = "safe-to-run" -version = "0.6.4" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.quote]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.0.35 -> 1.0.36" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.same-file]] -who = "Android Legacy" -criteria = "safe-to-run" -version = "1.0.6" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.quote]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.36 -> 1.0.37" +notes = """ +The delta just 1) inlines/expands `impl ToTokens` that used to be handled via +`primitive!` macro and 2) adds `impl ToTokens` for `CStr` and `CString`. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.quote]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "1.0.37 -> 1.0.38" +notes = "Still no unsafe" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.quote]] +who = "Daniel Cheng " +criteria = "safe-to-deploy" +delta = "1.0.38 -> 1.0.39" +notes = "Only minor changes for clippy lints and documentation." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.quote]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.0.39 -> 1.0.40" +notes = """ +The delta is just a simplification of how `tokens.extend(...)` call is made. +Still no `unsafe` anywhere. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" [[audits.google.audits.strsim]] who = "Ying Hsu " @@ -348,6 +494,18 @@ criteria = "safe-to-deploy" delta = "2.0.0 -> 2.0.1" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.allocator-api2]] +who = "Nicolas Silva " +criteria = "safe-to-deploy" +version = "0.2.18" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.allocator-api2]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "0.2.20 -> 0.2.21" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.deranged]] who = "Alex Franchuk " criteria = "safe-to-deploy" @@ -378,6 +536,43 @@ criteria = "safe-to-deploy" delta = "1.15.0 -> 1.16.0" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.foldhash]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +delta = "0.1.5 -> 0.2.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.hashbrown]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +delta = "0.15.2 -> 0.15.5" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.hashbrown]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +delta = "0.15.5 -> 0.16.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.hashbrown]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +delta = "0.16.0 -> 0.16.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.indexmap]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +delta = "2.8.0 -> 2.11.4" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.indexmap]] +who = "Ben Dean-Kawamura " +criteria = "safe-to-deploy" +delta = "2.11.4 -> 2.14.0" +notes = "Mostly internal refactorings. No new unsafe code." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.powerfmt]] who = "Alex Franchuk " criteria = "safe-to-deploy" @@ -388,6 +583,18 @@ yet, but it's all valid. Otherwise it's a pretty simple crate. """ aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.quote]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "1.0.40 -> 1.0.45" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.audits.quote]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "1.0.45 -> 1.0.47" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + [[audits.mozilla.audits.tempfile]] who = "Chris Martin " criteria = "safe-to-deploy" @@ -401,6 +608,12 @@ criteria = "safe-to-deploy" delta = "3.16.0 -> 3.27.0" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.tinyvec_macros]] +who = "Drew Willcoxon " +criteria = "safe-to-deploy" +delta = "0.1.0 -> 0.1.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.utf8parse]] who = "Nika Layzell " criteria = "safe-to-deploy"