Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"

jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
# Pin to the same toolchain as rust-toolchain.toml (blob-decoder's app/CLI
# is built and tested with the pinned stable). Do not float `stable`.
- uses: dtolnay/rust-toolchain@1.96.0
with:
components: clippy, rustfmt
- run: cargo test --all-features
- run: cargo clippy --all-targets --all-features -- -D warnings
- run: cargo fmt --check

msrv:
# Dedicated MSRV job: the LIBRARY must build on its declared floor (1.88.0,
# forced by plist 1.10 -> time 0.3.53). Builds the lib without default
# features (the downstream-library surface; the CLI's clap is not part of it).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@1.88.0
- name: Build library on MSRV (no default features)
run: cargo build --lib --no-default-features

lean-build:
# Gate: a library consumer doing `default-features = false` must NOT pull in
# clap (a CLI-only dep behind the `cli` feature).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@1.96.0
- name: Lean lib build (no default features)
run: cargo build --no-default-features
- name: Assert clap absent from the lean tree
run: |
cargo tree --no-default-features -e no-dev --prefix none > /tmp/lean-tree.txt
if grep -qE '^clap ' /tmp/lean-tree.txt; then
echo "FAIL: clap present in the lean (no-default-features) build:"
grep -E '^clap ' /tmp/lean-tree.txt
exit 1
fi

coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@1.96.0
with:
components: llvm-tools-preview
- uses: taiki-e/install-action@a402910a723481c4c80d006d75298c796a9c8695 # cargo-llvm-cov
with:
tool: cargo-llvm-cov
# All test targets, all features. The gate requires 100% function coverage
# of the LIBRARY, honoring `// cov:unreachable` markers on provably-dead
# defensive arms; main.rs (the Humble CLI shell) is excluded by the script.
- name: Coverage + 100% function gate
run: |
cargo llvm-cov --all-features --json --output-path cov.json
python3 scripts/coverage-gate.py cov.json
- name: Emit lcov
run: cargo llvm-cov report --lcov --output-path lcov.info
- name: Upload to Codecov
uses: codecov/codecov-action@0f8570b1a125f4937846a11fcfa3bcd548bd8c97 # v4.6.0
with:
files: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false

secrets:
name: Secret Scan (gitleaks)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0
- name: Install gitleaks
run: |
VERSION=8.30.1
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" \
| tar xz -C /tmp gitleaks
- name: Run gitleaks
run: /tmp/gitleaks detect --source . --config .gitleaks.toml

deny:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- name: Install cargo-deny
uses: taiki-e/install-action@a402910a723481c4c80d006d75298c796a9c8695
with:
tool: cargo-deny
- name: Check advisories, licenses, bans, sources
run: cargo deny check

freshness:
# Advisory dependency-freshness gate: fails if the committed Cargo.lock is
# stale relative to the requirements (see CLAUDE.md "Dependency Freshness").
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- run: cargo update --locked
20 changes: 20 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Docs

on:
push:
branches: [main]
paths: ["docs/**", "mkdocs.yml", ".github/workflows/docs.yml"]

permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.x"
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force
35 changes: 35 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Fuzz

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 3 * * 1" # weekly deeper run

env:
CARGO_TERM_COLOR: always

jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
# cargo-fuzz needs nightly (`-Z` flags). rust-toolchain.toml pins a stable
# version that would win over the @nightly channel, so force `cargo +nightly`
# on every fuzz command (see CLAUDE.md "Rust MSRV & Toolchain Policy").
- uses: dtolnay/rust-toolchain@nightly
with:
# ASan needs a dynamically-linked target; the musl-built cargo-fuzz
# otherwise defaults to musl (crt-static) and fails with E0463.
targets: x86_64-unknown-linux-gnu
- uses: taiki-e/install-action@a402910a723481c4c80d006d75298c796a9c8695 # cargo-fuzz
with:
tool: cargo-fuzz
- name: Build fuzz target
run: cargo +nightly fuzz build --target x86_64-unknown-linux-gnu
- name: Smoke-run (no-panic / bounded-memory invariant)
run: |
cargo +nightly fuzz run identify --target x86_64-unknown-linux-gnu \
-- -max_total_time=60 -runs=500000 -rss_limit_mb=2048
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
cov.json
lcov.info

# mkdocs build output (docs.yml builds + deploys in CI; local `mkdocs build` output)
/site

# Claude Code session state — ephemeral, never tracked
/.claude/

Expand Down
12 changes: 12 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# gitleaks configuration for blob-decoder. Extends the bundled default ruleset;
# allowlists generated / build-output trees (all gitignored, never source) so a
# full `gitleaks dir .` stays clean and fast. The pre-commit hook scans only
# staged files regardless.
[extend]
useDefault = true

[allowlist]
description = "Project-specific allowlist"
paths = [
'''(^|/)target/''',
]
21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml
- id: check-merge-conflict

- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
- id: clippy
args: ["--all-targets", "--all-features", "--", "-D", "warnings"]

- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.2
hooks:
- id: gitleaks
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Changelog

All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Initial `blob-decoder` library + `blob-decode` CLI: identify opaque blobs of
unknown type, decode them, and report scored, cited candidates, recursively
unwrapping nested wrappers (base64 → gzip → binary-plist).
- Recognises binary/XML plist, gzip, zlib, Snappy, JSON, UUID, base64, hex,
UTF-16LE, and UTF-8 text, dispatching to `plist`/`flate2`/`snap`/`base64`/
`hex`/`uuid`/`serde_json`.
- Bomb/DoS guards: size-capped decompression and depth-capped recursion via
`Limits`; a `cargo-fuzz` `identify` target for the no-panic invariant.
Loading
Loading