{"id":51595162,"url":"https://github.com/securityronin/protobuf-forensic","last_synced_at":"2026-07-11T18:01:37.330Z","repository":{"id":370810070,"uuid":"1297127534","full_name":"SecurityRonin/protobuf-forensic","owner":"SecurityRonin","description":"Schemaless forensic Protocol Buffers decoder — decode protobuf wire format with no .proto, with field-type heuristics + timeglyph timestamp flagging","archived":false,"fork":false,"pushed_at":"2026-07-11T04:48:26.000Z","size":87,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-11T06:11:37.294Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SecurityRonin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"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-07-11T04:37:34.000Z","updated_at":"2026-07-11T04:46:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/SecurityRonin/protobuf-forensic","commit_stats":null,"previous_names":["securityronin/protobuf-forensic"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/SecurityRonin/protobuf-forensic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fprotobuf-forensic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fprotobuf-forensic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fprotobuf-forensic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fprotobuf-forensic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SecurityRonin","download_url":"https://codeload.github.com/SecurityRonin/protobuf-forensic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fprotobuf-forensic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35370428,"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-07-11T02:00:05.354Z","response_time":104,"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":[],"created_at":"2026-07-11T18:01:35.874Z","updated_at":"2026-07-11T18:01:37.313Z","avatar_url":"https://github.com/SecurityRonin.png","language":"Rust","funding_links":["https://github.com/sponsors/h4x0r"],"categories":[],"sub_categories":[],"readme":"[![Docs](https://img.shields.io/badge/docs-securityronin.github.io-blue.svg)](https://securityronin.github.io/protobuf-forensic/)\n[![CI](https://github.com/SecurityRonin/protobuf-forensic/actions/workflows/ci.yml/badge.svg)](https://github.com/SecurityRonin/protobuf-forensic/actions/workflows/ci.yml)\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[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](#trust-but-verify)\n[![security: cargo-deny](https://img.shields.io/badge/security-cargo--deny-success.svg)](deny.toml)\n\n# protobuf-forensic\n\n**You have a Protocol Buffers blob and no `.proto`. Decode it anyway — the wire\nformat into a field tree, with the values that look like timestamps flagged.**\n\nIn forensics you almost never have the schema. `prost` and `rust-protobuf` are\nschema-*driven* and cannot help. protobuf-forensic decodes the wire format\n**blind**, the way `protoc --decode_raw` and Google's protoscope do, and adds a\nforensic layer on top.\n\n## See it in 30 seconds\n\n```console\n$ protobuf4n6 --hex 0880a0f8fa05120a73657373696f6e2e64621a03089601\n1 [field 1 varint] varint 1600000000\n    time? 2027-10-30T02:00:00Z  exFAT packed timestamp (LOCAL time) (exfat, 100%, via varint)\n    time? 2020-09-13T12:26:40Z  Unix time (seconds) (unix, 100%, via varint)\n2 [field 2 len] string \"session.db\"\n    conf 0.90\n3 [field 3 len] message (1 field(s))\n  3.1 [field 1 varint] varint 150\n```\n\n*(representative timestamp rows shown; values verbatim.)* No schema, no config:\nfield 1 is decoded as a varint and its value flagged as **consistent with**\nseveral timestamp formats (Unix seconds -\u003e 2020-09-13); field 2's length-delimited\npayload is inferred to be the string `session.db`; field 3 is resolved to a\nnested message and walked recursively with a dotted path (`3.1`).\n\nRead from a file or stdin, and pick the output your pipeline wants:\n\n```console\n$ protobuf4n6 record.bin --format protoscope     # protoscope-like\n$ protobuf4n6 record.bin --format jsonl          # one JSON object per field\n$ cat record.bin | protobuf4n6 --format text     # from stdin\n```\n\nInstall (from source, until published):\n\n```console\n$ cargo install --path protobuf4n6\n```\n\n## Why not just decode with a protobuf library?\n\n`prost` / `rust-protobuf` need the `.proto` to generate types. Forensics rarely\nhas it — the blob comes out of a LevelDB value, an app cache, a memory dump. This\ntool decodes the wire format directly:\n\n- **Every field** — `tag = (field_number \u003c\u003c 3) | wire_type`; wire type 0 varint,\n  1 fixed64, 2 length-delimited, 5 fixed32, plus the deprecated groups.\n- **Length-delimited inference** — a `LEN` payload is resolved to a nested\n  **message** (parses cleanly and consumes exactly its bytes), a UTF-8 **string**,\n  or opaque **bytes** — message-first, matching `protoc --decode_raw`. Ambiguous\n  payloads (a message that is also printable, or bytes that also decode as a\n  packed repeated field) are flagged with a confidence and a note.\n- **Timestamp flagging** — every integer / fixed field is run through\n  [`timeglyph`](https://github.com/SecurityRonin/timeglyph); plausible readings\n  are surfaced as scored, cited **candidates** — never a verdict.\n\n## Three crates\n\n- **`protobuf-core`** — the schemaless wire decoder. `decode(\u0026[u8]) -\u003e Vec\u003cField\u003e`.\n  `#![forbid(unsafe_code)]`, panic-free, **zero dependencies**, low MSRV.\n- **`protobuf-forensic`** — the analysis layer: ambiguity scoring + timeglyph\n  timestamp flagging.\n- **`protobuf4n6`** — the CLI (text / JSONL / protoscope).\n\n## Trust, but verify\n\n`protobuf-core` is validated against an **independent oracle**: `protoc --encode`\nproduces real wire bytes from a `.proto`, and `protoc --decode_raw` (the\nreference schemaless decoder) is cross-checked field-by-field against our decode.\nSee [`docs/validation.md`](https://securityronin.github.io/protobuf-forensic/validation/).\n\nEvery read is bounds-checked and panic-free by lint\n(`clippy::unwrap_used`/`expect_used = deny`): a truncated or overlong varint, a\nlying length, or a depth-bomb yields an `Err`, never a panic or an\nover-allocation. Both parsers have a `cargo-fuzz` \"must not panic\" target. The\nworkspace enforces 100 % function coverage.\n\nThe forensic layer is **honest by construction**: timestamp readings are ranked,\ncapped candidates carrying a score and a spec citation — the tool says a value is\n*consistent with* a format, and leaves the conclusion to the analyst.\n\n---\n\n[Privacy Policy](https://securityronin.github.io/protobuf-forensic/privacy/) · [Terms of Service](https://securityronin.github.io/protobuf-forensic/terms/) · © 2026 Security Ronin Ltd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fprotobuf-forensic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsecurityronin%2Fprotobuf-forensic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fprotobuf-forensic/lists"}