{"id":51015054,"url":"https://github.com/securityronin/segb-forensic","last_synced_at":"2026-06-21T09:02:38.079Z","repository":{"id":364892203,"uuid":"1269620325","full_name":"SecurityRonin/segb-forensic","owner":"SecurityRonin","description":"Apple SEGB (Biome) forensic analyzer + reader — decode SEGB v1/v2 records and flag CRC-mismatch, deletion-residue, and timestamp-order anomalies as graded findings. Panic-free, no unsafe.","archived":false,"fork":false,"pushed_at":"2026-06-15T00:34:40.000Z","size":76,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T02:13:29.708Z","etag":null,"topics":["apple","biome","dfir","digital-forensics","forensics","incident-response","macos","parser","rust","segb"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SecurityRonin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T23:54:33.000Z","updated_at":"2026-06-15T00:37:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/SecurityRonin/segb-forensic","commit_stats":null,"previous_names":["securityronin/segb-core"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/SecurityRonin/segb-forensic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fsegb-forensic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fsegb-forensic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fsegb-forensic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fsegb-forensic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SecurityRonin","download_url":"https://codeload.github.com/SecurityRonin/segb-forensic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fsegb-forensic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34603640,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple","biome","dfir","digital-forensics","forensics","incident-response","macos","parser","rust","segb"],"created_at":"2026-06-21T09:02:33.139Z","updated_at":"2026-06-21T09:02:38.072Z","avatar_url":"https://github.com/SecurityRonin.png","language":"Rust","funding_links":["https://github.com/sponsors/h4x0r"],"categories":[],"sub_categories":[],"readme":"# segb-forensic\n\n[![segb-core](https://img.shields.io/crates/v/segb-core.svg?label=segb-core)](https://crates.io/crates/segb-core)\n[![segb-forensic](https://img.shields.io/crates/v/segb-forensic.svg?label=segb-forensic)](https://crates.io/crates/segb-forensic)\n[![Docs.rs](https://img.shields.io/docsrs/segb-core?label=docs.rs)](https://docs.rs/segb-core)\n[![Rust 1.81+](https://img.shields.io/badge/rust-1.81%2B-orange.svg)](https://www.rust-lang.org)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE)\n[![Sponsor](https://img.shields.io/badge/sponsor-h4x0r-ea4aaa?logo=github-sponsors)](https://github.com/sponsors/h4x0r)\n\n[![CI](https://github.com/SecurityRonin/segb-forensic/actions/workflows/ci.yml/badge.svg)](https://github.com/SecurityRonin/segb-forensic/actions/workflows/ci.yml)\n[![Coverage](https://img.shields.io/badge/coverage-100%25%20lines-brightgreen.svg)](https://github.com/SecurityRonin/segb-forensic/actions/workflows/ci.yml)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n[![Security advisories](https://img.shields.io/badge/security-cargo--deny-informational.svg)](deny.toml)\n\n**Apple SEGB (Biome) forensics for Rust — a panic-free reader that decodes the state-tagged, CRC-protected records of macOS/iOS user-activity streams, and a graded anomaly analyzer that flags the CRC mismatches and out-of-order or missing timestamps a casual read would miss.**\n\nSEGB is the container the **Biome** subsystem uses to log user activity on macOS and iOS — Safari history events, app launches, micro-location visits, the `App.MenuItem` selection stream, and more. Each stream is an append-ordered log of records carrying a state flag, one or two timestamps, and a raw protobuf payload. Two crates, one workspace:\n\n- **[`segb-core`](https://crates.io/crates/segb-core)** — the reader: auto-detects SEGB **v1** and **v2**, decodes every record (state, Cocoa→Unix timestamp, payload, stored vs computed CRC-32), and ships a minimal protobuf field walker plus an `App.MenuItem` decoder, over any `Read + Seek` source. No `unsafe`, no C bindings.\n- **[`segb-forensic`](https://crates.io/crates/segb-forensic)** — the analyzer: turns those records into severity-graded [`forensicnomicon::report::Finding`](https://crates.io/crates/forensicnomicon)s, so a Biome stream's anomalies aggregate uniformly with every other artifact layer.\n\n## Audit a SEGB stream in 30 seconds\n\n```toml\n[dependencies]\nsegb-forensic = \"0.1\"   # pulls in segb-core\n```\n\n```rust\nuse std::io::Cursor;\nuse forensicnomicon::report::{Observation, Source};\n\nlet records = segb::read_segb(\u0026mut Cursor::new(bytes))?;\nlet src = Source { analyzer: \"segb-forensic\".into(), scope: \"SEGB\".into(), version: None };\n\nfor anomaly in segb_forensic::audit(\u0026records) {\n    // each Anomaly -\u003e a canonical forensicnomicon Finding\n    let finding = anomaly.to_finding(src.clone());\n    println!(\"{} ({:?}): {}\", finding.code, finding.severity, finding.note);\n}\n# Ok::\u003c(), segb::SegbError\u003e(())\n```\n\n### Anomaly codes\n\nSEGB streams are append-ordered logs of state-tagged, CRC-protected records — a structure that makes a small set of tampering / corruption signals precise.\n\n| Code | Severity | Meaning |\n|---|---|---|\n| `SEGB-CRC-MISMATCH` | High | a `Written` record's payload CRC-32 ≠ stored CRC — corruption or a post-write edit |\n| `SEGB-TIMESTAMP-OUT-OF-ORDER` | Medium | a `Written` record older than a preceding one — append order broken (clock change / reordering) |\n| `SEGB-TIMESTAMP-MISSING` | Low | a `Written` record with no finite timestamp |\n\nFindings apply to **`Written`** (live) records only. `Deleted`/`Unknown` records are the normal lifecycle of a Biome append-log — their payloads are wiped, so their CRC mismatches by construction; the ccl-segb reference validates CRC for `Written` records only, and so do we (zero false positives on a real benign stream).\n\nFindings are **observations, never verdicts** — the analyst concludes.\n\n## Just need the reader?\n\n`segb-core` stands alone. Decode every record and walk the `App.MenuItem` payload without pulling in the analyzer:\n\n```toml\n[dependencies]\nsegb-core = \"0.1\"\n```\n\n```rust\nuse std::fs::File;\nuse std::io::BufReader;\nuse segb::{read_segb, menuitem::decode_app_menu_item};\n\nlet f = File::open(\"/path/to/App.MenuItem/local\")?;\nlet mut r = BufReader::new(f);\nfor record in read_segb(\u0026mut r)? {\n    let item = decode_app_menu_item(record.payload(), record.timestamp_unix())?;\n    println!(\"{:?} selected {:?}\", item.application, item.menu_item);\n}\n# Ok::\u003c(), segb::SegbError\u003e(())\n```\n\n`read_segb()` rewinds the stream and detects the variant automatically:\n\n| Variant | Magic location | Header | Alignment |\n|---------|----------------|--------|-----------|\n| SEGB v1 | last 4 bytes of the 56-byte header | 56 bytes | 8 bytes |\n| SEGB v2 | first 4 bytes of the 32-byte header | 32 bytes | 4 bytes |\n\n## The two-crate split\n\n| Crate | Role | Depends on | Emits |\n|---|---|---|---|\n| [`segb-core`](https://crates.io/crates/segb-core) | reader / decoder | `thiserror` | `SegbRecord` (state, timestamp, payload, CRC) |\n| [`segb-forensic`](https://crates.io/crates/segb-forensic) | anomaly analyzer | `segb-core`, `forensicnomicon` | graded `Finding`s |\n\nThe reader stays pure — it decodes bytes and makes no judgments. All *forensic meaning* lives in the analyzer, which is a side-effect-free function of already-decoded records. That separation is why `segb-core` is useful on its own and why `segb-forensic` drops straight into a fleet `Report` next to every other analyzer.\n\n## Trust but verify\n\nSEGB files are untrusted, attacker-controllable input, so the crates are hardened by construction:\n\n- **`#![forbid(unsafe_code)]`** across the whole workspace — no `unsafe`, anywhere.\n- **Panic-free** — every length, offset, trailer entry, and protobuf varint is bounds-checked before use; a crafted length field cannot drive an out-of-bounds read or an allocation bomb. Malformed input surfaces as a typed `SegbError`, never a silent default.\n- **Fuzzed** — `cargo-fuzz` targets cover the v1 and v2 containers, the protobuf walker, the `App.MenuItem` decoder, and the full `read_segb` → `audit` pipeline; the invariant is \"must not panic.\"\n- **Validated against real Apple data** — every SEGB file in Josh Hickman's public iOS 17.3 image (139 v1 + 262 v2 = **401 files**) reconciles record-for-record with the [`ccl-segb`](https://github.com/cclgroupltd/ccl-segb) reference oracle: **401 PASS / 0 MISMATCH**. See [`docs/validation.md`](docs/validation.md).\n\n## References\n\n- **ccl-segb** (Alex Caithness / CCL Solutions) — the byte-layout reference: \u003chttps://github.com/cclgroupltd/ccl-segb\u003e\n- **Unit 42 research** (Palo Alto Networks, 2026): \u003chttps://unit42.paloaltonetworks.com/new-macos-artifact-discovered/\u003e\n\n---\n\n[Privacy Policy](https://securityronin.github.io/segb-forensic/privacy/) · [Terms of Service](https://securityronin.github.io/segb-forensic/terms/) · © 2026 Security Ronin Ltd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fsegb-forensic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsecurityronin%2Fsegb-forensic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fsegb-forensic/lists"}