preparing for v1.100.0

This commit is contained in:
Mick Grove 2026-05-18 13:03:16 -07:00
commit 54d9fc7ecd
11 changed files with 740 additions and 783 deletions

View file

@ -132,7 +132,7 @@ jobs:
toolchain: ${{ env.RUST_TOOLCHAIN }} toolchain: ${{ env.RUST_TOOLCHAIN }}
- name: Set up MSYS2 - name: Set up MSYS2
uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1
with: with:
msystem: ${{ matrix.msystem }} msystem: ${{ matrix.msystem }}
update: true update: true

View file

@ -274,7 +274,7 @@ jobs:
toolchain: ${{ env.RUST_TOOLCHAIN }} toolchain: ${{ env.RUST_TOOLCHAIN }}
- name: Set up MSYS2 - name: Set up MSYS2
uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1
with: with:
msystem: ${{ matrix.msystem }} msystem: ${{ matrix.msystem }}
update: true update: true

769
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -148,15 +148,13 @@ flate2 = "1.1"
thousands = "0.2.0" thousands = "0.2.0"
crossbeam-skiplist = "0.1.3" crossbeam-skiplist = "0.1.3"
tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"] } tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"] }
mongodb = { version = "3.4", default-features = false, features = ["rustls-tls", "aws-auth", "compat-3-0-0", "dns-resolver"] } mongodb = { git = "https://github.com/mongodb/mongo-rust-driver", rev = "bdddefc50c4794d51d10b944320d42c6eb216b04", default-features = false, features = ["rustls-tls", "aws-auth", "compat-3-0-0", "dns-resolver"] }
mysql_async = { version = "0.36.2", default-features = false, features = ["default-rustls"] } mysql_async = { version = "0.36.2", default-features = false, features = ["default-rustls"] }
aws-config = { version = "1.8.14", default-features = false, features = ["default-https-client", "rt-tokio", "credentials-process", "sso"] } aws-config = { version = "1.8.14", default-features = false, features = ["default-https-client", "rt-tokio", "credentials-process", "sso"] }
aws-credential-types = "1.2.12" aws-credential-types = "1.2.12"
aws-sdk-sts = { version = "1.98.0", default-features = false, features = ["default-https-client", "rt-tokio"] } aws-sdk-sts = { version = "1.98.0", default-features = false, features = ["default-https-client", "rt-tokio"] }
aws-types = "1.3.12" aws-types = "1.3.12"
parking_lot = "0.12.5" parking_lot = "0.12.5"
octorust = "0.10.0"
reqwest-middleware-octorust = { package = "reqwest-middleware", version = "0.4.2" }
tracing-subscriber = {version = "0.3.22", features = ["env-filter"] } tracing-subscriber = {version = "0.3.22", features = ["env-filter"] }
tracing-core = "0.1.35" tracing-core = "0.1.35"
aws-smithy-http-client = "1.1.10" aws-smithy-http-client = "1.1.10"
@ -252,7 +250,7 @@ proptest = "1.9.0"
[profile.release] [profile.release]
debug = false debug = false
strip = true strip = true
opt-level = "s" #3 # Maximum optimization for performance opt-level = "s" # Optimize for smaller binary size over speed
lto = true # Enable Link Time Optimization lto = true # Enable Link Time Optimization
codegen-units = 1 # Optimize for size but slower compilation codegen-units = 1 # Optimize for size but slower compilation
panic = "abort" # Remove unwind tables for panics panic = "abort" # Remove unwind tables for panics

View file

@ -235,7 +235,7 @@ impl BlobId {
pub fn new(input: &[u8]) -> Self { pub fn new(input: &[u8]) -> Self {
const CHUNK: usize = 64 * 1024; // 64KB from start and end const CHUNK: usize = 64 * 1024; // 64KB from start and end
let mut hasher = Sha1::new(); let mut hasher = Sha1::new();
hasher.update(format!("blob {}\0", input.len()).as_bytes()); update_git_blob_header(&mut hasher, input.len());
if input.len() <= CHUNK * 2 { if input.len() <= CHUNK * 2 {
hasher.update(input); hasher.update(input);
} else { } else {
@ -249,7 +249,7 @@ impl BlobId {
/// Computes a `BlobId` from the complete bytes (no truncation). /// Computes a `BlobId` from the complete bytes (no truncation).
pub fn compute_from_bytes(bytes: &[u8]) -> Self { pub fn compute_from_bytes(bytes: &[u8]) -> Self {
let mut hasher = Sha1::new(); let mut hasher = Sha1::new();
hasher.update(format!("blob {}\0", bytes.len()).as_bytes()); update_git_blob_header(&mut hasher, bytes.len());
hasher.update(bytes); hasher.update(bytes);
let digest: [u8; 20] = hasher.finalize().into(); let digest: [u8; 20] = hasher.finalize().into();
BlobId(digest) BlobId(digest)
@ -277,6 +277,27 @@ impl BlobId {
} }
} }
fn update_git_blob_header(hasher: &mut Sha1, len: usize) {
let mut digits = [0u8; 20];
let mut n = len;
let mut i = digits.len();
if n == 0 {
i -= 1;
digits[i] = b'0';
} else {
while n > 0 {
i -= 1;
digits[i] = b'0' + (n % 10) as u8;
n /= 10;
}
}
hasher.update(b"blob ");
hasher.update(&digits[i..]);
hasher.update(b"\0");
}
impl<'de> Deserialize<'de> for BlobId { impl<'de> Deserialize<'de> for BlobId {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> { fn deserialize<D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
struct Vis; struct Vis;

View file

@ -187,7 +187,7 @@ p256 = { version = "0.13.2", optional = true }
ed25519-dalek = { version = "2.2", features = ["pkcs8"], optional = true } ed25519-dalek = { version = "2.2", features = ["pkcs8"], optional = true }
hex = { workspace = true, optional = true } hex = { workspace = true, optional = true }
url = { version = "2.5.7", optional = true } url = { version = "2.5.7", optional = true }
mongodb = { version = "3.4", default-features = false, features = ["rustls-tls", "aws-auth", "compat-3-0-0", "dns-resolver"], optional = true } mongodb = { git = "https://github.com/mongodb/mongo-rust-driver", rev = "bdddefc50c4794d51d10b944320d42c6eb216b04", default-features = false, features = ["rustls-tls", "aws-auth", "compat-3-0-0", "dns-resolver"], optional = true }
mysql_async = { version = "0.36.2", default-features = false, features = ["default-rustls"], optional = true } mysql_async = { version = "0.36.2", default-features = false, features = ["default-rustls"], optional = true }
tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"], optional = true } tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"], optional = true }
tokio-postgres-rustls = { version = "0.13.0", optional = true } tokio-postgres-rustls = { version = "0.13.0", optional = true }

392
fuzz/Cargo.lock generated
View file

@ -123,9 +123,9 @@ dependencies = [
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "1.8.2" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
dependencies = [ dependencies = [
"rustversion", "rustversion",
] ]
@ -172,6 +172,15 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "block-buffer"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
dependencies = [
"hybrid-array",
]
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.12.1" version = "1.12.1"
@ -332,6 +341,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "cmov"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.5" version = "1.0.5"
@ -360,6 +375,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.7" version = "0.8.7"
@ -447,6 +468,24 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "crypto-common"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
dependencies = [
"hybrid-array",
]
[[package]]
name = "ctutils"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
dependencies = [
"cmov",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "6.1.0" version = "6.1.0"
@ -494,9 +533,20 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [ dependencies = [
"block-buffer", "block-buffer 0.10.4",
"crypto-common", "crypto-common 0.1.7",
"subtle", ]
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
"const-oid",
"crypto-common 0.2.1",
"ctutils",
] ]
[[package]] [[package]]
@ -718,9 +768,9 @@ dependencies = [
[[package]] [[package]]
name = "gix" name = "gix"
version = "0.81.0" version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0473c64d9ccbcfb9953a133b47c8b9a335b87ac6c52b983ee4b03d49000b0f3f" checksum = "6ce52001b946a6249d5d0d3011df0a042ac3f8a4d013460db6476577b0b9c567"
dependencies = [ dependencies = [
"gix-actor", "gix-actor",
"gix-archive", "gix-archive",
@ -777,22 +827,21 @@ dependencies = [
[[package]] [[package]]
name = "gix-actor" name = "gix-actor"
version = "0.40.0" version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e5e5b518339d5e6718af108fd064d4e9ba33caf728cf487352873d76411df35" checksum = "272916673b83714734b15d4ef3c8b5f1ccddb15fea8ff548430b97c1ab7b7ed8"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-date", "gix-date",
"gix-error", "gix-error",
"serde", "serde",
"winnow",
] ]
[[package]] [[package]]
name = "gix-archive" name = "gix-archive"
version = "0.30.0" version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "651c99be11aac9b303483193ae50b45eb6e094da4f5ed797019b03948f51aad6" checksum = "9a20ec244b733338d4cb60e5e05eac700dab7fcc689647b1d1daa9396b119342"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-date", "gix-date",
@ -803,9 +852,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-attributes" name = "gix-attributes"
version = "0.31.0" version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c233d6eaa098c0ca5ce03236fd7a96e27f1abe72fad74b46003fbd11fe49563c" checksum = "fe17c5a1c0b6f2ef1476aa1d3222ea50cdff67608016613a58bfc3e078046000"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-glob", "gix-glob",
@ -821,18 +870,18 @@ dependencies = [
[[package]] [[package]]
name = "gix-bitmap" name = "gix-bitmap"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7add20f40d060db8c9b1314d499bac6ed7480f33eb113ce3e1cf5d6ff85d989" checksum = "1ecbfc77ec6852294e341ecc305a490b59f2813e6ca42d79efda5099dcab1894"
dependencies = [ dependencies = [
"gix-error", "gix-error",
] ]
[[package]] [[package]]
name = "gix-blame" name = "gix-blame"
version = "0.11.0" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77aaf9f7348f4da3ebfbfbbc35fa0d07155d98377856198dde6f695fd648705" checksum = "14dab9a942ab54a9661ded7397c3bf927274e7afa94494db0d75cfcbde02ca0a"
dependencies = [ dependencies = [
"gix-commitgraph", "gix-commitgraph",
"gix-date", "gix-date",
@ -850,18 +899,18 @@ dependencies = [
[[package]] [[package]]
name = "gix-chunk" name = "gix-chunk"
version = "0.7.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1096b6608fbe5d27fb4984e20f992b4e76fb8c613f6acb87d07c5831b53a6959" checksum = "edf288be9b60fe7231de03771faa292be1493d84786f68727e33ad1f91764320"
dependencies = [ dependencies = [
"gix-error", "gix-error",
] ]
[[package]] [[package]]
name = "gix-command" name = "gix-command"
version = "0.8.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b849c65a609f50d02f8a2774fe371650b3384a743c79c2a070ce0da49b7fb7da" checksum = "86335306511abe43d75c866d4b1f3d90932fe202edcd43e1314036333e7384d8"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-path", "gix-path",
@ -872,9 +921,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-commitgraph" name = "gix-commitgraph"
version = "0.35.0" version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3196655fd1443f3c58a48c114aa480be3e4e87b393d7292daaa0d543862eb445" checksum = "fe3b5aa0f24e19028c261d229aeeedafcaaa52ebd71021cc15184620fc9d32eb"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-chunk", "gix-chunk",
@ -887,9 +936,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-config" name = "gix-config"
version = "0.54.0" version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08939b4c4ed7a663d0e64be9e1e9bdf23a1fb4fcee1febdf449f12229542e50d" checksum = "8c01848aebd21c67f6ba41f1de8efd46ae96df21f001954a3c9e1517e514d410"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-config-value", "gix-config-value",
@ -898,18 +947,16 @@ dependencies = [
"gix-path", "gix-path",
"gix-ref", "gix-ref",
"gix-sec", "gix-sec",
"memchr",
"smallvec", "smallvec",
"thiserror 2.0.18", "thiserror 2.0.18",
"unicode-bom", "unicode-bom",
"winnow",
] ]
[[package]] [[package]]
name = "gix-config-value" name = "gix-config-value"
version = "0.17.1" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "441a300bc3645a1f45cba495b9175f90f47256ce43f2ee161da0031e3ac77c92" checksum = "13b39ed39ee4c10a3b157f9fb94bac8098d9f8e56201f0cf7dee6c187416c4b2"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bstr", "bstr",
@ -920,9 +967,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-credentials" name = "gix-credentials"
version = "0.37.1" version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b2a34b8715e3bbd514f3d1705f5d51c4b250e5bfe506b9fb60b133c85c93d9" checksum = "65ca11598b70811d7b16ff90945a6e57dfe521e85b744e51636965fe39cc8f60"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-command", "gix-command",
@ -939,9 +986,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-date" name = "gix-date"
version = "0.15.1" version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39acf819aa9fee65e4838a2eec5cb2506e47ebb89e02a5ab9918196e491571ea" checksum = "b94cdae4eb4b0f4136e3d9b3aa2d2cd03cfb5bb9b636b31263aea2df86d41543"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-error", "gix-error",
@ -953,31 +1000,30 @@ dependencies = [
[[package]] [[package]]
name = "gix-diff" name = "gix-diff"
version = "0.61.0" version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88f3b3475e5d3877d7c30c40827cc2441936ce890efc226e5ba4afe3a7ae33f0" checksum = "dc08e0fa1a91ff5f24affeab052f198056645e1de004910bde7b82b50ea5982a"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-command", "gix-command",
"gix-filter", "gix-filter",
"gix-fs", "gix-fs",
"gix-hash", "gix-hash",
"gix-imara-diff",
"gix-object", "gix-object",
"gix-path", "gix-path",
"gix-tempfile", "gix-tempfile",
"gix-trace", "gix-trace",
"gix-traverse", "gix-traverse",
"gix-worktree", "gix-worktree",
"imara-diff 0.1.8",
"imara-diff 0.2.0",
"thiserror 2.0.18", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "gix-dir" name = "gix-dir"
version = "0.23.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da4604a360988f0ba8efe6f90093ca5a844f4a7f8e1a3dcda501ec44e600ea9" checksum = "32a0fc06e9e1e430cbf0a313666976d90f822f461a6525320427aa9b8af5236c"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-discover", "gix-discover",
@ -995,9 +1041,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-discover" name = "gix-discover"
version = "0.49.0" version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c65bd3330fe0cb9d40d875bf862fd5e8ad6fa4164ddbc4842fbeb889c3f0b2c6" checksum = "17852e6a501e688a1702b24ebe5b3761d4719455bc869fd29f38b0b859bcad34"
dependencies = [ dependencies = [
"bstr", "bstr",
"dunce", "dunce",
@ -1010,18 +1056,18 @@ dependencies = [
[[package]] [[package]]
name = "gix-error" name = "gix-error"
version = "0.2.1" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e86d01da904d4a9265def43bd42a18c5e6dc7000a73af512946ba14579c9fbd" checksum = "e207b971746ab724fccdfced2e4e19e854744611904a0195d3aa8fda8a110613"
dependencies = [ dependencies = [
"bstr", "bstr",
] ]
[[package]] [[package]]
name = "gix-features" name = "gix-features"
version = "0.46.2" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "752493cd4b1d5eaaa0138a7493f65c96863fefa990fc021e0e519579e389ab20" checksum = "af375693ad5333d0a2c66b4c5b2cbe9ccc38e34f8e8bf24e4ae42c12307fdc4f"
dependencies = [ dependencies = [
"bytes", "bytes",
"crc32fast", "crc32fast",
@ -1038,9 +1084,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-filter" name = "gix-filter"
version = "0.28.0" version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d37598282a6566da6fb52667570c7fe0aedcb122ac886724a9e62a2180523e35" checksum = "dac917dbe9653c9b615d248db91907a365bd779750c9e1b457a9d9fdeece3a08"
dependencies = [ dependencies = [
"bstr", "bstr",
"encoding_rs", "encoding_rs",
@ -1059,9 +1105,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-fs" name = "gix-fs"
version = "0.19.2" version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a964b4aec683eb0bacb87533defa80805bb4768056371a47ab38b00a2d377b72" checksum = "1e1967daac9848757c47c2aef0c57bcadc1a897347f559778249bf286a536c86"
dependencies = [ dependencies = [
"bstr", "bstr",
"fastrand", "fastrand",
@ -1073,9 +1119,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-glob" name = "gix-glob"
version = "0.24.0" version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03e6cd88cc0dc1eafa1fddac0fb719e4e74b6ea58dd016e71125fde4a326bee" checksum = "08bf29249a069bf2507f5964f80997f37b134d320ea348d66527726b9be2c38c"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bstr", "bstr",
@ -1086,9 +1132,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-hash" name = "gix-hash"
version = "0.23.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fb896a02d9ab96fa518475a5f30ad3952010f801a8de5840f633f4a6b985dfb" checksum = "bcf70d1e252337eed16360f8b8ebb71865ece58eab7954b39ce38b420de703d2"
dependencies = [ dependencies = [
"faster-hex", "faster-hex",
"gix-features", "gix-features",
@ -1099,9 +1145,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-hashtable" name = "gix-hashtable"
version = "0.13.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2664216fc5e89b51e756a4a3ac676315602ce2dac07acf1da959a22038d69b33" checksum = "d33b455e07b3c16d3b2eeebc7b38d2dafcbf8a653de1138ef55d4c2a1fd0b08b"
dependencies = [ dependencies = [
"gix-hash", "gix-hash",
"hashbrown 0.16.1", "hashbrown 0.16.1",
@ -1110,9 +1156,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-ignore" name = "gix-ignore"
version = "0.19.1" version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09f915dcf6911e3027537166d34e13f0fe101ed12225178d2ae29cd1272cff26" checksum = "6bb13fbbeeafee943e52b61fcc88dfddf6a452fcaf0c4d0cdc8f218fa25bbec5"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-glob", "gix-glob",
@ -1123,10 +1169,20 @@ dependencies = [
] ]
[[package]] [[package]]
name = "gix-index" name = "gix-imara-diff"
version = "0.49.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bae54ab14e4e74d5dda60b82ea7afad7c8eb3be68283d6d5f29bd2e6d47fff7" checksum = "39eb0623e15e4cb83c02ce6a959e48fadd1ae3b715b36b5acc01816e01388c82"
dependencies = [
"bstr",
"hashbrown 0.16.1",
]
[[package]]
name = "gix-index"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54c3ef97ad08121e4327a6226bd63fed6b9e3c6b976d48bddd4356d9d41191db"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bstr", "bstr",
@ -1153,9 +1209,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-lock" name = "gix-lock"
version = "21.0.1" version = "23.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbe09cf05ba7c679bba189acc29eeea137f643e7fff1b5dff879dfd45248be31" checksum = "09b3bc074e5723027b482dcd9ab99d95804a53742f6de812d0172fbba4a186c1"
dependencies = [ dependencies = [
"gix-tempfile", "gix-tempfile",
"gix-utils", "gix-utils",
@ -1164,9 +1220,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-mailmap" name = "gix-mailmap"
version = "0.32.0" version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7b4818da522786ec7e32a00884ee8fc40fa4c215c3997c0b15f7b62684d1199" checksum = "023d3a6561cbebe45b89e0764d48928ad970667076f16fa5889e6f86d8432086"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-actor", "gix-actor",
@ -1177,9 +1233,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-merge" name = "gix-merge"
version = "0.14.0" version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4606747466512d22c2dffc019142e1941238f543987ea51353c938cca80c500" checksum = "74bbcdcc52b70a32f0a151b024dff9d0fcf56ee48f00d9503e735af9d99ea881"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-command", "gix-command",
@ -1187,6 +1243,7 @@ dependencies = [
"gix-filter", "gix-filter",
"gix-fs", "gix-fs",
"gix-hash", "gix-hash",
"gix-imara-diff",
"gix-index", "gix-index",
"gix-object", "gix-object",
"gix-path", "gix-path",
@ -1196,16 +1253,15 @@ dependencies = [
"gix-tempfile", "gix-tempfile",
"gix-trace", "gix-trace",
"gix-worktree", "gix-worktree",
"imara-diff 0.1.8",
"nonempty", "nonempty",
"thiserror 2.0.18", "thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "gix-negotiate" name = "gix-negotiate"
version = "0.29.0" version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea064c7595eea08fdd01c70748af747d9acc40f727b61f4c8a2145a5c5fc28c" checksum = "103d42bfade1b8a96ca5005933127bdad461ce588d92422b2c2daa3ff20d780c"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"gix-commitgraph", "gix-commitgraph",
@ -1217,9 +1273,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-object" name = "gix-object"
version = "0.58.0" version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cafb802bb688a7c1e69ef965612ff5ff859f046bfb616377e4a0ba4c01e43d47" checksum = "a38075a95d7cc5df8afd38e72c617026c1456952207a4120a7f55a3fbf93b4d7"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-actor", "gix-actor",
@ -1227,21 +1283,19 @@ dependencies = [
"gix-features", "gix-features",
"gix-hash", "gix-hash",
"gix-hashtable", "gix-hashtable",
"gix-path",
"gix-utils", "gix-utils",
"gix-validate", "gix-validate",
"itoa", "itoa",
"serde", "serde",
"smallvec", "smallvec",
"thiserror 2.0.18", "thiserror 2.0.18",
"winnow",
] ]
[[package]] [[package]]
name = "gix-odb" name = "gix-odb"
version = "0.78.0" version = "0.80.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24833ae9323b4f7079575fb9f961cf9c414b0afbec428a536ab8e7dd93bc002b" checksum = "aeeda12a9663120418735ecdc1250d06eeab0be75700e47b3402a981331716ba"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"gix-features", "gix-features",
@ -1252,6 +1306,7 @@ dependencies = [
"gix-pack", "gix-pack",
"gix-path", "gix-path",
"gix-quote", "gix-quote",
"memmap2",
"parking_lot", "parking_lot",
"serde", "serde",
"tempfile", "tempfile",
@ -1260,9 +1315,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-pack" name = "gix-pack"
version = "0.68.0" version = "0.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3484119cd19859d7d7639413c27e192478fa354d3f4ff5f7e3c041e8040f0f4" checksum = "daf02e6f5c8f07a069c9ea5245f40d9b14856ada4086091dc99941b49002b4fa"
dependencies = [ dependencies = [
"clru", "clru",
"gix-chunk", "gix-chunk",
@ -1280,9 +1335,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-packetline" name = "gix-packetline"
version = "0.21.2" version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be19313dcdb7dff75a3ce2f99be00878458295bcc3b6c7f0005591597573345c" checksum = "362246df440ee691699f0664cbf7006a6ece477db6734222be95e4198e5656e6"
dependencies = [ dependencies = [
"bstr", "bstr",
"faster-hex", "faster-hex",
@ -1292,9 +1347,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-path" name = "gix-path"
version = "0.11.2" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09c31d4373bda7fab9eb01822927b55185a378d6e1bf737e0a54c743ad806658" checksum = "671a6059e8a4c1b7f406e24716499cefa3926e060876fb1959ef225efeee346e"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-trace", "gix-trace",
@ -1304,9 +1359,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-pathspec" name = "gix-pathspec"
version = "0.16.1" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f89611f13544ca5ebeb68a502673814ef57200df60c24a61c2ce7b96f612f08b" checksum = "2a84a4f083dd70fb49f4377e13afa6d90df2daaa1c705c49d6ff1331fc7e8855"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bstr", "bstr",
@ -1319,9 +1374,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-prompt" name = "gix-prompt"
version = "0.14.1" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f61f6264e1f6c5a951531fe127722c7522bc02ebda80c4528286bda4642055f" checksum = "e041a626c64cb69e4117fcdf80da8d0e454fba3b1f420412792d191f52251aee"
dependencies = [ dependencies = [
"gix-command", "gix-command",
"gix-config-value", "gix-config-value",
@ -1332,9 +1387,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-protocol" name = "gix-protocol"
version = "0.59.0" version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f38666350736b5877c79f57ddae02bde07a4ce186d889adc391e831cddcbe76" checksum = "aa4bee82db63ec635996b96efae71cf467c155fa3f34a556184373224a26c4fd"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-date", "gix-date",
@ -1348,14 +1403,13 @@ dependencies = [
"nonempty", "nonempty",
"serde", "serde",
"thiserror 2.0.18", "thiserror 2.0.18",
"winnow",
] ]
[[package]] [[package]]
name = "gix-quote" name = "gix-quote"
version = "0.7.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68533db71259c8776dd4e770d2b7b98696213ecdc1f5c9e3507119e274e0c578" checksum = "6e97b73791a64bc0fa7dd2c5b3e551136115f97750b876ed1c952c7a7dbaf8be"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-error", "gix-error",
@ -1364,9 +1418,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-ref" name = "gix-ref"
version = "0.61.0" version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2159978abb99b7027c8579d15211e262ef0ef2594d5cecb3334fbcbdfe2997c" checksum = "d8ba9cc15f558b274c99349b83130f5ec83459660828fde9718bbbb43a726167"
dependencies = [ dependencies = [
"gix-actor", "gix-actor",
"gix-features", "gix-features",
@ -1381,14 +1435,13 @@ dependencies = [
"memmap2", "memmap2",
"serde", "serde",
"thiserror 2.0.18", "thiserror 2.0.18",
"winnow",
] ]
[[package]] [[package]]
name = "gix-refspec" name = "gix-refspec"
version = "0.39.0" version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc806ee13f437428f8a1ba4c72ecfaa3f20e14f5f0d4c2bc17d0b33e794aa6ac" checksum = "61755b27d57edc8940a1b1593c8c61548ca8e4c02da1ed8d5bfeda9eb2a6b761"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-error", "gix-error",
@ -1402,9 +1455,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-revision" name = "gix-revision"
version = "0.43.0" version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c08f1ec5d1e6a524f8ba291c41f0ccaef64e48ed0e8cf790b3461cae45f6d3d" checksum = "1fb5288fac706d3ea3e4e2ba9ec38b78743b8c02f422e18cb342299cfd6ab7e8"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bstr", "bstr",
@ -1421,9 +1474,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-revwalk" name = "gix-revwalk"
version = "0.29.0" version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4b2b87772b21ca449249e86d32febadba5cba32b0fcce804ab9cefc6f2111c" checksum = "313813706b073a12ff7f9b2896bf3e6504cdac7cfbc97b1920114724705069f0"
dependencies = [ dependencies = [
"gix-commitgraph", "gix-commitgraph",
"gix-date", "gix-date",
@ -1437,9 +1490,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-sec" name = "gix-sec"
version = "0.13.2" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf82ae037de9c62850ce67beaa92ec8e3e17785ea307cdde7618edc215603b4f" checksum = "f5a3a2d3e504a238136751e646a6c028252286a0ea64ea9974bf0498633407c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"gix-path", "gix-path",
@ -1450,9 +1503,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-shallow" name = "gix-shallow"
version = "0.10.0" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf60711c9083b2364b3fac8a352444af76b17201f3682fdebe74fa66d89a772" checksum = "29187305521bfacf4aefd284ab28dbfa9fb74abd39a5e63dd313b1baa5808c27"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-hash", "gix-hash",
@ -1464,9 +1517,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-status" name = "gix-status"
version = "0.28.0" version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d6c598e3fdbc352fba1c5ba7e709e69402fafbc44d9295edad2e3c4738996b" checksum = "68c6d2a8c521ffa205fe7e268c82e6d1378ba37cd826ca10ab6129fdc29a4b65"
dependencies = [ dependencies = [
"bstr", "bstr",
"filetime", "filetime",
@ -1487,9 +1540,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-submodule" name = "gix-submodule"
version = "0.28.0" version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce5c3929c5e6821f651d35e8420f72fea3cfafe9fc1e928a61e718b462c72a5" checksum = "9fd5fc8692890bd71a596e540fd4c364f8460eaa82c4eaaedebde6e1e3eb4d91"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-config", "gix-config",
@ -1502,9 +1555,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-tempfile" name = "gix-tempfile"
version = "21.0.1" version = "23.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d9ab2c89fe4bfd4f1d8700aa4516534c170d8a21ae2c554167374607c2eaf16" checksum = "691ea1e31435c7e7d4d04705ec9d1c0d9482c46b2acf512bc723939d8f0af7fb"
dependencies = [ dependencies = [
"dashmap", "dashmap",
"gix-fs", "gix-fs",
@ -1515,15 +1568,15 @@ dependencies = [
[[package]] [[package]]
name = "gix-trace" name = "gix-trace"
version = "0.1.18" version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f69a13643b8437d4ca6845e08143e847a36ca82903eed13303475d0ae8b162e0" checksum = "6f23569e55f2ffaf958617353b9734a7d52a7c19c439eeaa5e3efc217fd2270e"
[[package]] [[package]]
name = "gix-transport" name = "gix-transport"
version = "0.55.1" version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a521e39c6235ce63ed6c001e2dd79818c830b82c3b7b59247ee7b229c39ec9bb" checksum = "ffd6a5c676b92d4ead5f5a2b2935024415dec69edc997b6090ca9cac010a3018"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-command", "gix-command",
@ -1538,9 +1591,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-traverse" name = "gix-traverse"
version = "0.55.0" version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963dc2afcdb611092aa587c3f9365e749ac0a0892ff27662dbc75f26c953fbec" checksum = "a14b7052c0786676c03e71fcfde7d7f0f8e8316e642b5cec6bb3998719b2ce5c"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"gix-commitgraph", "gix-commitgraph",
@ -1555,9 +1608,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-url" name = "gix-url"
version = "0.35.2" version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d28e8af3d42581190da884f013caf254d2fd4d6ab102408f08d21bfa11de6c8d" checksum = "35842d099e813f6f6bba529e88d4670572149c3df79b7a412952259887721ece"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-path", "gix-path",
@ -1568,9 +1621,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-utils" name = "gix-utils"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "befcdbdfb1238d2854591f760a48711bed85e72d80a10e8f2f93f656746ef7c5" checksum = "4e477b4f07a6e8da4ba791c53c858102959703c60d70f199932010d5b94adb2c"
dependencies = [ dependencies = [
"bstr", "bstr",
"fastrand", "fastrand",
@ -1579,18 +1632,18 @@ dependencies = [
[[package]] [[package]]
name = "gix-validate" name = "gix-validate"
version = "0.11.0" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec1eff98d91941f47766367cba1be746bab662bad761d9891ae6f7882f7840b" checksum = "e26ac2602b43eadfdca0560b81d3341944162a3c9f64ccdeef8fc501ad80dad5"
dependencies = [ dependencies = [
"bstr", "bstr",
] ]
[[package]] [[package]]
name = "gix-worktree" name = "gix-worktree"
version = "0.50.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6bd5830cbc43c9c00918b826467d2afad685b195cb82329cde2b2d116d2c578" checksum = "d69955eb5e2910832f88d041964b809eee01dadd579237e0b55efec58fd406fd"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-attributes", "gix-attributes",
@ -1607,9 +1660,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-worktree-state" name = "gix-worktree-state"
version = "0.28.0" version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644a1681f96e1be43c2a8384337d9d220e7624f50db54beda70997052aebf707" checksum = "8a96dccbcf9e8fe0291c55f06e08da93ebb2e691c1311276f541eefcc6d70800"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-features", "gix-features",
@ -1625,9 +1678,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-worktree-stream" name = "gix-worktree-stream"
version = "0.30.0" version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24e3fb70a1f650a5cec7d5b8d10d6d6fe86daf3cf15bde08ba0c70988a2932c3" checksum = "9a8444b8ed4662e1a0c97f3eceda29630001a1bbb2632201e50312623e594213"
dependencies = [ dependencies = [
"gix-attributes", "gix-attributes",
"gix-error", "gix-error",
@ -1748,11 +1801,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hmac" name = "hmac"
version = "0.12.1" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
dependencies = [ dependencies = [
"digest", "digest 0.11.3",
] ]
[[package]] [[package]]
@ -1783,6 +1836,15 @@ dependencies = [
"libm", "libm",
] ]
[[package]]
name = "hybrid-array"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
dependencies = [
"typenum",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.65" version = "0.1.65"
@ -1829,25 +1891,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "imara-diff"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2"
dependencies = [
"hashbrown 0.15.5",
]
[[package]]
name = "imara-diff"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f01d462f766df78ab820dd06f5eb700233c51f0f4c2e846520eaf4ba6aa5c5c"
dependencies = [
"hashbrown 0.15.5",
"memchr",
]
[[package]] [[package]]
name = "include_dir" name = "include_dir"
version = "0.7.4" version = "0.7.4"
@ -2008,7 +2051,7 @@ dependencies = [
"schemars", "schemars",
"serde", "serde",
"serde_json", "serde_json",
"sha1", "sha1 0.11.0",
"smallvec", "smallvec",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokei", "tokei",
@ -2032,6 +2075,7 @@ dependencies = [
"base32", "base32",
"base64", "base64",
"crc32fast", "crc32fast",
"hex",
"hmac", "hmac",
"ignore", "ignore",
"include_dir", "include_dir",
@ -2045,8 +2089,8 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"sha1", "sha1 0.11.0",
"sha2", "sha2 0.11.0",
"thiserror 2.0.18", "thiserror 2.0.18",
"time", "time",
"tracing", "tracing",
@ -2362,7 +2406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220"
dependencies = [ dependencies = [
"pest", "pest",
"sha2", "sha2 0.10.9",
] ]
[[package]] [[package]]
@ -2768,7 +2812,18 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.17", "cpufeatures 0.2.17",
"digest", "digest 0.10.7",
]
[[package]]
name = "sha1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"digest 0.11.3",
] ]
[[package]] [[package]]
@ -2777,8 +2832,8 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423"
dependencies = [ dependencies = [
"digest", "digest 0.10.7",
"sha1", "sha1 0.10.6",
] ]
[[package]] [[package]]
@ -2789,7 +2844,18 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.17", "cpufeatures 0.2.17",
"digest", "digest 0.10.7",
]
[[package]]
name = "sha2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"digest 0.11.3",
] ]
[[package]] [[package]]
@ -2847,12 +2913,6 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.117" version = "2.0.117"
@ -3132,9 +3192,9 @@ dependencies = [
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.19.0" version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]] [[package]]
name = "ucd-trie" name = "ucd-trie"

View file

@ -50,17 +50,17 @@ pub enum CompressedContent {
} }
pub fn is_safe_extract_path(path: &Path) -> bool { pub fn is_safe_extract_path(path: &Path) -> bool {
for (idx, comp) in path.components().enumerate() { if path.is_absolute() {
return false;
}
for comp in path.components() {
match comp { match comp {
// Never allow parent-directory escapes // Never allow parent-directory escapes
Component::ParentDir => return false, Component::ParentDir => return false,
// Leading "C:\" (Windows) or "/" (Unix) is fine; // Archive entry names must always be relative to the extraction root.
// a prefix later in the path would be suspicious. Component::Prefix(_) | Component::RootDir => return false,
Component::Prefix(_) | Component::RootDir if idx == 0 => continue,
// A prefix *inside* the path (e.g. "foo/C:\evil") is unsafe
Component::Prefix(_) => return false,
_ => {} _ => {}
} }
@ -68,6 +68,17 @@ pub fn is_safe_extract_path(path: &Path) -> bool {
true true
} }
fn has_parent_or_embedded_prefix(path: &Path) -> bool {
for (idx, comp) in path.components().enumerate() {
match comp {
Component::ParentDir => return true,
Component::Prefix(_) if idx > 0 => return true,
_ => {}
}
}
false
}
fn is_zip_format(ext: &str) -> bool { fn is_zip_format(ext: &str) -> bool {
ZIP_BASED_FORMATS.iter().any(|z| z == &ext) ZIP_BASED_FORMATS.iter().any(|z| z == &ext)
} }
@ -87,6 +98,10 @@ fn handle_tar_archive_streaming(
let mut entry = entry?; let mut entry = entry?;
if entry.header().entry_type().is_file() { if entry.header().entry_type().is_file() {
let path_in_tar = entry.path()?.to_string_lossy().to_string(); let path_in_tar = entry.path()?.to_string_lossy().to_string();
if !is_safe_extract_path(Path::new(&path_in_tar)) {
tracing::warn!("unsafe tar path: {path_in_tar}");
continue;
}
let logical_path = format!("{}!{}", archive_path.display(), path_in_tar); let logical_path = format!("{}!{}", archive_path.display(), path_in_tar);
let out_path = base_dir.join(&path_in_tar); let out_path = base_dir.join(&path_in_tar);
@ -96,10 +111,6 @@ fn handle_tar_archive_streaming(
continue; continue;
} }
} }
if !is_safe_extract_path(&out_path) {
tracing::warn!("unsafe tar path: {}", out_path.display());
continue;
}
match fs::File::create(&out_path) { match fs::File::create(&out_path) {
Ok(mut out_file) => { Ok(mut out_file) => {
if let Err(e) = std::io::copy(&mut entry, &mut out_file) { if let Err(e) = std::io::copy(&mut entry, &mut out_file) {
@ -239,6 +250,10 @@ fn handle_zip_archive_streaming(
let mut zipped_file = zip.by_index(i)?; let mut zipped_file = zip.by_index(i)?;
if zipped_file.is_file() { if zipped_file.is_file() {
let name_in_zip = zipped_file.name().to_string(); let name_in_zip = zipped_file.name().to_string();
if !is_safe_extract_path(Path::new(&name_in_zip)) {
tracing::warn!("unsafe zip path: {name_in_zip}");
continue;
}
let logical_path = format!("{}!{}", archive_path.display(), name_in_zip); let logical_path = format!("{}!{}", archive_path.display(), name_in_zip);
let out_path = base_dir.join(&name_in_zip); let out_path = base_dir.join(&name_in_zip);
@ -248,10 +263,6 @@ fn handle_zip_archive_streaming(
continue; continue;
} }
} }
if !is_safe_extract_path(&out_path) {
tracing::warn!("unsafe zip path: {}", out_path.display());
continue;
}
match fs::File::create(&out_path) { match fs::File::create(&out_path) {
Ok(mut out_file) => { Ok(mut out_file) => {
let mut limited = (&mut zipped_file).take(MAX_ZIP_ENTRY_DECOMPRESSED_BYTES); let mut limited = (&mut zipped_file).take(MAX_ZIP_ENTRY_DECOMPRESSED_BYTES);
@ -381,7 +392,7 @@ fn handle_asar_archive_in_memory(buffer: &[u8], archive_path: &Path) -> Result<C
/// Validate and open a file for reading, checking for path traversal attacks. /// Validate and open a file for reading, checking for path traversal attacks.
fn safe_open_for_read(path: &Path) -> Result<fs::File> { fn safe_open_for_read(path: &Path) -> Result<fs::File> {
if !is_safe_extract_path(path) { if has_parent_or_embedded_prefix(path) {
anyhow::bail!("unsafe input path during decompression: {}", path.display()); anyhow::bail!("unsafe input path during decompression: {}", path.display());
} }
Ok(fs::File::open(path)?) Ok(fs::File::open(path)?)
@ -389,7 +400,7 @@ fn safe_open_for_read(path: &Path) -> Result<fs::File> {
/// Validate and create a file for writing, checking for path traversal attacks. /// Validate and create a file for writing, checking for path traversal attacks.
fn safe_create_for_write(path: &Path) -> Result<fs::File> { fn safe_create_for_write(path: &Path) -> Result<fs::File> {
if !is_safe_extract_path(path) { if has_parent_or_embedded_prefix(path) {
anyhow::bail!("unsafe output path during decompression: {}", path.display()); anyhow::bail!("unsafe output path during decompression: {}", path.display());
} }
Ok(fs::File::create(path)?) Ok(fs::File::create(path)?)

View file

@ -8,11 +8,6 @@ use std::{
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use octorust::{
Client,
auth::Credentials,
types::{Order, ReposListOrgSort, ReposListOrgType, ReposListUserType},
};
use reqwest::StatusCode; use reqwest::StatusCode;
use serde::Deserialize; use serde::Deserialize;
use serde_json::Value; use serde_json::Value;
@ -33,6 +28,11 @@ struct GitHubRepo {
fork: bool, fork: bool,
} }
#[derive(Deserialize)]
struct GitHubOrg {
login: String,
}
#[derive(Debug)] #[derive(Debug)]
pub struct RepoSpecifiers { pub struct RepoSpecifiers {
pub user: Vec<String>, pub user: Vec<String>,
@ -52,21 +52,20 @@ pub enum RepoType {
Source, Source,
Fork, Fork,
} }
impl From<RepoType> for ReposListUserType { impl RepoType {
fn from(repo_type: RepoType) -> Self { fn user_query_value(&self) -> &'static str {
match repo_type { match self {
RepoType::All => ReposListUserType::All, RepoType::All => "all",
RepoType::Source => ReposListUserType::Owner, RepoType::Source => "owner",
RepoType::Fork => ReposListUserType::Member, RepoType::Fork => "member",
} }
} }
}
impl From<RepoType> for ReposListOrgType { fn org_query_value(&self) -> &'static str {
fn from(repo_type: RepoType) -> Self { match self {
match repo_type { RepoType::All => "all",
RepoType::All => ReposListOrgType::All, RepoType::Source => "sources",
RepoType::Source => ReposListOrgType::Sources, RepoType::Fork => "forks",
RepoType::Fork => ReposListOrgType::Forks,
} }
} }
} }
@ -128,34 +127,13 @@ fn build_exclude_matcher(exclude_repos: &[String]) -> git_host::ExcludeMatcher {
fn should_exclude_repo(clone_url: &str, excludes: &git_host::ExcludeMatcher) -> bool { fn should_exclude_repo(clone_url: &str, excludes: &git_host::ExcludeMatcher) -> bool {
git_host::should_exclude_repo(clone_url, excludes, parse_repo_name_from_url) git_host::should_exclude_repo(clone_url, excludes, parse_repo_name_from_url)
} }
fn create_github_client(github_url: &url::Url, ignore_certs: bool) -> Result<Arc<Client>> { fn create_github_client(ignore_certs: bool) -> Result<Arc<reqwest::Client>> {
// Try personal access token
let credentials = if let Ok(token) = env::var("KF_GITHUB_TOKEN") {
Credentials::Token(token)
} else {
Credentials::Token("".to_string()) // Anonymous access
};
let mut client_builder = reqwest::Client::builder(); let mut client_builder = reqwest::Client::builder();
if ignore_certs { if ignore_certs {
client_builder = client_builder.danger_accept_invalid_certs(ignore_certs); client_builder = client_builder.danger_accept_invalid_certs(ignore_certs);
} }
let reqwest_client = client_builder.build().context("Failed to build HTTP client")?; Ok(Arc::new(client_builder.build().context("Failed to build HTTP client")?))
let http_client = reqwest_middleware_octorust::ClientBuilder::new(reqwest_client).build();
let mut client = Client::custom(
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
credentials,
http_client,
);
// Override host if not using api.github.com
if github_url.host_str() != Some("api.github.com") {
client.with_host_override(github_url.as_str());
}
Ok(Arc::new(client))
} }
fn normalize_api_base(api_url: &Url) -> Url { fn normalize_api_base(api_url: &Url) -> Url {
@ -167,6 +145,77 @@ fn normalize_api_base(api_url: &Url) -> Url {
base base
} }
fn github_token() -> Option<String> {
env::var("KF_GITHUB_TOKEN").ok().filter(|t| !t.is_empty())
}
fn github_get(client: &reqwest::Client, url: Url, token: Option<&str>) -> reqwest::RequestBuilder {
let req = client.get(url).header("User-Agent", GLOBAL_USER_AGENT.as_str());
if let Some(token) = token { req.bearer_auth(token) } else { req }
}
async fn fetch_github_orgs(
client: &reqwest::Client,
api_base: &Url,
token: Option<&str>,
) -> Result<Vec<String>> {
let mut orgs = Vec::new();
let mut page = 1;
loop {
let mut url = api_base.join("organizations").context("Failed to build GitHub orgs URL")?;
url.query_pairs_mut().append_pair("per_page", "100").append_pair("page", &page.to_string());
let resp = github_get(client, url, token).send().await?;
if !resp.status().is_success() {
warn_on_rate_limit("GitHub", resp.status(), "listing organizations");
break;
}
let page_orgs: Vec<GitHubOrg> = resp.json().await?;
if page_orgs.is_empty() {
break;
}
orgs.extend(page_orgs.into_iter().map(|org| org.login));
page += 1;
}
Ok(orgs)
}
async fn fetch_github_repos(
client: &reqwest::Client,
api_base: &Url,
path: &str,
repo_type: &str,
token: Option<&str>,
action: &str,
) -> Result<Vec<GitHubRepo>> {
let mut repos = Vec::new();
let mut page = 1;
loop {
let mut url = api_base.join(path).context("Failed to build GitHub repositories URL")?;
url.query_pairs_mut()
.append_pair("per_page", "100")
.append_pair("page", &page.to_string())
.append_pair("type", repo_type)
.append_pair("sort", "created")
.append_pair("direction", "desc");
let resp = github_get(client, url, token).send().await?;
if !resp.status().is_success() {
warn_on_rate_limit("GitHub", resp.status(), action);
break;
}
let page_repos: Vec<GitHubRepo> = resp.json().await?;
if page_repos.is_empty() {
break;
}
repos.extend(page_repos);
page += 1;
}
Ok(repos)
}
pub async fn enumerate_contributor_repo_urls( pub async fn enumerate_contributor_repo_urls(
repo_url: &GitUrl, repo_url: &GitUrl,
github_api_url: &Url, github_api_url: &Url,
@ -179,7 +228,7 @@ pub async fn enumerate_contributor_repo_urls(
let (_, owner, repo) = parse_repo(repo_url).context("invalid GitHub repo URL")?; let (_, owner, repo) = parse_repo(repo_url).context("invalid GitHub repo URL")?;
let exclude_set = build_exclude_matcher(exclude_repos); let exclude_set = build_exclude_matcher(exclude_repos);
let client = reqwest::Client::builder().danger_accept_invalid_certs(ignore_certs).build()?; let client = reqwest::Client::builder().danger_accept_invalid_certs(ignore_certs).build()?;
let token = env::var("KF_GITHUB_TOKEN").ok().filter(|t| !t.is_empty()); let token = github_token();
let api_base = normalize_api_base(github_api_url); let api_base = normalize_api_base(github_api_url);
let mut contributor_logins = Vec::new(); let mut contributor_logins = Vec::new();
@ -190,11 +239,7 @@ pub async fn enumerate_contributor_repo_urls(
.join(&format!("repos/{owner}/{repo}/contributors")) .join(&format!("repos/{owner}/{repo}/contributors"))
.context("Failed to build GitHub contributors URL")?; .context("Failed to build GitHub contributors URL")?;
url.query_pairs_mut().append_pair("per_page", "100").append_pair("page", &page.to_string()); url.query_pairs_mut().append_pair("per_page", "100").append_pair("page", &page.to_string());
let mut req = client.get(url).header("User-Agent", GLOBAL_USER_AGENT.as_str()); let resp = github_get(&client, url, token.as_deref()).send().await?;
if let Some(token) = token.as_ref() {
req = req.bearer_auth(token);
}
let resp = req.send().await?;
if !resp.status().is_success() { if !resp.status().is_success() {
warn_on_rate_limit("GitHub", resp.status(), "listing contributors"); warn_on_rate_limit("GitHub", resp.status(), "listing contributors");
break; break;
@ -251,11 +296,7 @@ pub async fn enumerate_contributor_repo_urls(
.append_pair("type", "all") .append_pair("type", "all")
.append_pair("sort", "updated") .append_pair("sort", "updated")
.append_pair("direction", "desc"); .append_pair("direction", "desc");
let mut req = client.get(url).header("User-Agent", GLOBAL_USER_AGENT.as_str()); let resp = github_get(&client, url, token.as_deref()).send().await?;
if let Some(token) = token.as_ref() {
req = req.bearer_auth(token);
}
let resp = req.send().await?;
if !resp.status().is_success() { if !resp.status().is_success() {
warn_on_rate_limit("GitHub", resp.status(), "listing user repositories"); warn_on_rate_limit("GitHub", resp.status(), "listing user repositories");
break; break;
@ -351,22 +392,22 @@ pub async fn enumerate_repo_urls(
ignore_certs: bool, ignore_certs: bool,
mut progress: Option<&mut ProgressBar>, mut progress: Option<&mut ProgressBar>,
) -> Result<Vec<String>> { ) -> Result<Vec<String>> {
let client = create_github_client(&github_url, ignore_certs)?; let client = create_github_client(ignore_certs)?;
let mut repo_urls = Vec::new(); let mut repo_urls = Vec::new();
let exclude_set = build_exclude_matcher(&repo_specifiers.exclude_repos); let exclude_set = build_exclude_matcher(&repo_specifiers.exclude_repos);
let user_repo_type: ReposListUserType = repo_specifiers.repo_filter.clone().into(); let api_base = normalize_api_base(&github_url);
let org_repo_type: ReposListOrgType = repo_specifiers.repo_filter.clone().into(); let token = github_token();
for username in &repo_specifiers.user { for username in &repo_specifiers.user {
let repos = client let repos = fetch_github_repos(
.repos() &client,
.list_all_for_user( &api_base,
username, &format!("users/{username}/repos"),
user_repo_type.clone(), repo_specifiers.repo_filter.user_query_value(),
ReposListOrgSort::Created, token.as_deref(),
Order::Desc, "listing user repositories",
) )
.await?; .await?;
repo_urls.extend(repos.body.into_iter().filter_map(|repo| { repo_urls.extend(repos.into_iter().filter_map(|repo| {
let clone_url = repo.clone_url; let clone_url = repo.clone_url;
if should_exclude_repo(&clone_url, &exclude_set) { None } else { Some(clone_url) } if should_exclude_repo(&clone_url, &exclude_set) { None } else { Some(clone_url) }
})); }));
@ -375,24 +416,21 @@ pub async fn enumerate_repo_urls(
} }
} }
let orgs = if repo_specifiers.all_organizations { let orgs = if repo_specifiers.all_organizations {
let mut all_orgs = Vec::new(); fetch_github_orgs(&client, &api_base, token.as_deref()).await?
let org_list = client.orgs().list_all(100).await?;
all_orgs.extend(org_list.body.into_iter().map(|org| org.login));
all_orgs
} else { } else {
repo_specifiers.organization.clone() repo_specifiers.organization.clone()
}; };
for org_name in orgs { for org_name in orgs {
let repos = client let repos = fetch_github_repos(
.repos() &client,
.list_all_for_org( &api_base,
&org_name, &format!("orgs/{org_name}/repos"),
org_repo_type.clone(), repo_specifiers.repo_filter.org_query_value(),
ReposListOrgSort::Created, token.as_deref(),
Order::Desc, "listing organization repositories",
) )
.await?; .await?;
repo_urls.extend(repos.body.into_iter().filter_map(|repo| { repo_urls.extend(repos.into_iter().filter_map(|repo| {
let clone_url = repo.clone_url; let clone_url = repo.clone_url;
if should_exclude_repo(&clone_url, &exclude_set) { None } else { Some(clone_url) } if should_exclude_repo(&clone_url, &exclude_set) { None } else { Some(clone_url) }
})); }));

View file

@ -2319,7 +2319,11 @@ alerts:
}; };
let yaml = super::build_config_yaml(&scan_args, &global_args, init_matches).unwrap(); let yaml = super::build_config_yaml(&scan_args, &global_args, init_matches).unwrap();
let cfg = parse_str(&yaml).expect("emitted YAML must round-trip"); let cfg = parse_str(&yaml).expect("emitted YAML must round-trip");
assert_eq!(cfg.git.github_api_url.as_deref(), Some("https://ghe.corp.example.com/api/v3/"),); assert_eq!(
cfg.git.github_api_url.as_deref(),
Some("https://ghe.corp.example.com/api/v3/"),
"github_api_url must preserve a user-supplied trailing slash",
);
} }
#[test] #[test]

View file

@ -1,4 +1,5 @@
use std::{ use std::{
io::Read,
marker::PhantomData, marker::PhantomData,
path::Path, path::Path,
process::Command, process::Command,
@ -212,12 +213,10 @@ pub fn enumerate_filesystem_inputs(
} }
Ok(entry) => entry, Ok(entry) => entry,
}; };
// Check if this is an archive file // Check if this is an archive file. `blob_path()` covers both filesystem and git
let is_archive = if let Origin::File(file_origin) = &origin.first() { // origins, so archive/binary filtering stays consistent across input modes.
is_compressed_file(&file_origin.path) let is_archive =
} else { origin.first().blob_path().map(is_compressed_file).unwrap_or(false);
false
};
let is_binary = is_binary(&blob.bytes()); let is_binary = is_binary(&blob.bytes());
let should_skip = if is_archive { let should_skip = if is_archive {
// For archives: skip only if --no_extract_archives is true // For archives: skip only if --no_extract_archives is true
@ -542,11 +541,10 @@ fn try_extract_git_blob_archive(
return Ok(None); return Ok(None);
} }
// Carry the original filename through so the decompressor picks the // Use the repo-relative path in reports while staging the blob under its basename so the
// correct format (zip-based, gz, tar, ...). decompress_file_to_temp // decompressor still dispatches on the original extension.
// dispatches on extension, so the extension MUST match the actual let archive_label = blob_path.to_string();
// bytes — using the in-tree filename is the right move. let staged_name = pb.file_name().and_then(|s| s.to_str()).unwrap_or("blob").to_string();
let basename = pb.file_name().and_then(|s| s.to_str()).unwrap_or("blob").to_string();
// ── fast path: ZIP-based archives extract entirely in memory ── // ── fast path: ZIP-based archives extract entirely in memory ──
// //
@ -577,18 +575,18 @@ fn try_extract_git_blob_archive(
return Ok(None); return Ok(None);
} }
if data.len() <= MAX_INMEM_ZIP_ARCHIVE_BYTES { if data.len() <= MAX_INMEM_ZIP_ARCHIVE_BYTES {
return match extract_zip_archive_in_memory(data, &basename) { return match extract_zip_archive_in_memory(data, &archive_label) {
Ok(entries) => Ok(Some(entries)), Ok(entries) => Ok(Some(entries)),
Err(e) => { Err(e) => {
debug!( debug!(
"in-memory zip extract failed for {basename}: {e:#}; falling back to raw scan" "in-memory zip extract failed for {archive_label}: {e:#}; falling back to raw scan"
); );
Ok(None) Ok(None)
} }
}; };
} }
debug!( debug!(
"{basename} is {} bytes (> {} MB cap); falling back to disk streaming extractor", "{archive_label} is {} bytes (> {} MB cap); falling back to disk streaming extractor",
data.len(), data.len(),
MAX_INMEM_ZIP_ARCHIVE_BYTES / (1024 * 1024) MAX_INMEM_ZIP_ARCHIVE_BYTES / (1024 * 1024)
); );
@ -599,7 +597,7 @@ fn try_extract_git_blob_archive(
// and large ZIP-based archives that exceeded the // and large ZIP-based archives that exceeded the
// in-memory cap above. ── // in-memory cap above. ──
let staging = tempfile::tempdir().context("Failed to create staging tempdir for git blob")?; let staging = tempfile::tempdir().context("Failed to create staging tempdir for git blob")?;
let staged_path = staging.path().join(&basename); let staged_path = staging.path().join(&staged_name);
std::fs::write(&staged_path, data) std::fs::write(&staged_path, data)
.with_context(|| format!("Failed to stage blob to {}", staged_path.display()))?; .with_context(|| format!("Failed to stage blob to {}", staged_path.display()))?;
@ -615,11 +613,11 @@ fn try_extract_git_blob_archive(
let strip_logical_prefix = |logical: String| -> String { let strip_logical_prefix = |logical: String| -> String {
// decompress_file_to_temp builds logicals as // decompress_file_to_temp builds logicals as
// `<staged_path>!<entry>`. Replace the staged-path prefix with the // `<staged_path>!<entry>`. Replace the staged-path prefix with the
// real archive basename so report paths look like // real repo-relative archive path so report paths look like
// `aws_leak.apk!res/values/strings.xml`. // `dir/aws_leak.apk!res/values/strings.xml`.
match logical.split_once('!') { match logical.split_once('!') {
Some((_, entry)) => format!("{}!{}", basename, entry), Some((_, entry)) => format!("{}!{}", archive_label, entry),
None => format!("{}!{}", basename, logical), None => format!("{}!{}", archive_label, logical),
} }
}; };
@ -638,10 +636,20 @@ fn try_extract_git_blob_archive(
for (logical, bytes) in files { for (logical, bytes) in files {
if total >= MAX_DISK_PATH_AGGREGATE_BYTES { if total >= MAX_DISK_PATH_AGGREGATE_BYTES {
debug!( debug!(
"{basename} disk-archive aggregate cap of {MAX_DISK_PATH_AGGREGATE_BYTES} bytes reached; truncating remaining entries" "{archive_label} disk-archive aggregate cap of {MAX_DISK_PATH_AGGREGATE_BYTES} bytes reached; truncating remaining entries"
); );
break; break;
} }
let remaining = MAX_DISK_PATH_AGGREGATE_BYTES - total;
if bytes.len() as u64 > remaining {
debug!(
"{archive_label} disk-archive aggregate cap reached while reading {}; truncating entry",
logical
);
let take = remaining as usize;
out.push((strip_logical_prefix(logical), bytes[..take].to_vec()));
break;
}
total += bytes.len() as u64; total += bytes.len() as u64;
out.push((strip_logical_prefix(logical), bytes)); out.push((strip_logical_prefix(logical), bytes));
} }
@ -654,14 +662,38 @@ fn try_extract_git_blob_archive(
for (logical, disk_path) in disk_entries { for (logical, disk_path) in disk_entries {
if total >= MAX_DISK_PATH_AGGREGATE_BYTES { if total >= MAX_DISK_PATH_AGGREGATE_BYTES {
debug!( debug!(
"{basename} disk-archive aggregate cap of {MAX_DISK_PATH_AGGREGATE_BYTES} bytes reached; truncating remaining entries" "{archive_label} disk-archive aggregate cap of {MAX_DISK_PATH_AGGREGATE_BYTES} bytes reached; truncating remaining entries"
); );
break; break;
} }
match std::fs::read(&disk_path) { let remaining = MAX_DISK_PATH_AGGREGATE_BYTES - total;
Ok(bytes) => { let entry_len = match std::fs::metadata(&disk_path) {
Ok(md) => md.len(),
Err(e) => {
debug!("Failed to stat extracted entry {}: {e}", disk_path.display());
continue;
}
};
let file = match std::fs::File::open(&disk_path) {
Ok(file) => file,
Err(e) => {
debug!("Failed to open extracted entry {}: {e}", disk_path.display());
continue;
}
};
let to_read = entry_len.min(remaining);
let mut bytes = Vec::with_capacity(to_read as usize);
match file.take(to_read).read_to_end(&mut bytes) {
Ok(_) => {
total += bytes.len() as u64; total += bytes.len() as u64;
out.push((strip_logical_prefix(logical), bytes)); out.push((strip_logical_prefix(logical), bytes));
if entry_len > remaining {
debug!(
"{archive_label} disk-archive aggregate cap reached while reading {}; truncating entry",
disk_path.display()
);
break;
}
} }
Err(e) => { Err(e) => {
debug!("Failed to read extracted entry {}: {e}", disk_path.display()); debug!("Failed to read extracted entry {}: {e}", disk_path.display());
@ -673,11 +705,9 @@ fn try_extract_git_blob_archive(
// Single-stream decompression (gz/bz2/xz/zlib) gives one logical // Single-stream decompression (gz/bz2/xz/zlib) gives one logical
// payload — present it as a single entry under the archive name. // payload — present it as a single entry under the archive name.
CompressedContent::Raw(bytes) => { CompressedContent::Raw(bytes) => vec![(format!("{}!content", archive_label), bytes)],
vec![(format!("{}!content", basename), bytes)]
}
CompressedContent::RawFile(path) => match std::fs::read(&path) { CompressedContent::RawFile(path) => match std::fs::read(&path) {
Ok(bytes) => vec![(format!("{}!content", basename), bytes)], Ok(bytes) => vec![(format!("{}!content", archive_label), bytes)],
Err(e) => { Err(e) => {
debug!("Failed to read decompressed payload {}: {e}", path.display()); debug!("Failed to read decompressed payload {}: {e}", path.display());
return Ok(None); return Ok(None);