rvt 0.4.0

Apache-2.0 clean-room reader for Autodesk Revit files (.rvt, .rfa, .rte, .rft) without Revit: metadata, previews and the embedded schema of 2016-2026 files, and typed elements with measured geometry from Revit 2024 and 2025 projects, exported as IFC4, glTF and plan SVG.
Documentation
# Threat Model

rvt-rs is a parser for a complex, proprietary, adversary-reachable
binary format. This document lists the threats we defend against, the
mitigations we ship, and the ones we explicitly don't cover so callers
can make informed deployment decisions.

## Attacker capabilities assumed

An attacker can:

- Hand-craft a malicious `.rvt` / `.rfa` / `.rte` / `.rft` file.
- Upload that file to a service that uses rvt-rs to parse it
  (SaaS file-scanning, archival ingest, compliance checking,
  openBIM pipelines, etc.).
- Submit crafted files to command-line users who run
  `rvt-analyze file.rfa` without inspecting the input first.

An attacker cannot (outside scope):

- Force rvt-rs to execute code it wasn't compiled with. rvt-rs is
  pure parser — no `eval`, no dynamic loading, no plugin API.
- Tamper with rvt-rs's source code or binary artifacts once
  published (supply-chain attacks are their own threat model; see
  §Supply chain).

## Threats and mitigations

### T1 — Remote code execution via parser bug

**Threat:** A malformed binary triggers a memory-safety bug in the
parser, letting the attacker hijack control flow.

**Mitigations:**

- **Pure safe Rust.** The library crate compiles with
  `#![forbid(unsafe_code)]` on the pure-parser tier (the pyo3
  bindings live in the separate `rvt-py` workspace member — SEC-12 /
  SEC-13 in `TODO.md`). All parsing logic —
  CFB opening, gzip decompression, schema parsing, walker — runs
  with safe-Rust bounds checks.
- **Panic-safe short inputs.** `FieldType::decode`, `find_chunks`,
  `gzip_header_len`, `inflate_at`, and the other byte-level
  primitives handle arbitrarily short / truncated input by
  returning `None` or `Result::Err`, never by panicking or
  indexing out of bounds. Property tests + corpus-driven fuzzing
  (see SEC-14..25) enforce this.
- **No C dependencies.** `flate2` uses `miniz_oxide` (pure Rust) as
  its default backend. `cfb` is pure Rust. No FFI to C parsers.

**Residual risk:** Pyo3 FFI layer in `src/python.rs` is the one
place where safe-Rust forbid doesn't apply (pyo3 macros expand to
unsafe function bodies). We isolate this to `rvt-py` (a separate
crate) post–SEC-12 so the core parser stays forbid-unsafe.

### T2 — Denial-of-service via unbounded memory

**Threat:** A small compressed file expands to gigabytes during
decompression, or a claimed stream size forces a multi-GB
allocation, starving the host of memory.

**Mitigations:**

- **`compression::InflateLimits`.** Every `inflate_at_with_limits`
  call caps output bytes. Default is 256 MiB/call. Legitimate
  files parse; compressed bombs (1 KB → 1 GB zeros) fail with
  `Error::DecompressLimitExceeded`.
- **`reader::OpenLimits`.** `RevitFile::open_with_limits` stats
  the file before reading; refuses >2 GiB by default. Protects
  against a claimed huge file in upload scenarios.
- **`read_stream_with_limit`.** Per-stream cap (default 256 MiB)
  checked against CFB directory entry + enforced during chunked
  read so malformed CFB "stream is huge" claims don't slip
  through.

**Residual risk:** An attacker inside the 256 MiB/stream limit can
still legitimately force 256 MiB allocations. Callers expecting
many small files should compose with their own total-memory cap.

### T3 — Denial-of-service via algorithmic complexity

**Threat:** A crafted file makes the parser iterate an unbounded
number of times (e.g. walker scans all possible entry offsets).

**Mitigations:**

- **`WalkerLimits` (API-11).** Caps max_scan_bytes +
  max_trial_offsets on the brute-force ADocument entry-point
  detector.
- **`find_chunks` bounded.** The gzip-magic scanner over raw bytes
  is linear in input length.
- **`parse_schema` scan cap.** Schema parsing scans at most
  64 KiB from the start of decompressed `Formats/Latest`. Returns
  `scan_was_capped: true` diagnostic if it hits the cap (pending
  API-10).

### T4 — Information disclosure / PII exposure

**Threat:** A user shares a `.rvt` or tool output not realizing it
contains Windows usernames, Autodesk-internal paths, document
GUIDs, or other identifying info.

**Mitigations:**

- **`--redact` flag on every CLI.** Scrubs usernames (C:\Users\X
  → C:\Users\<user>), Autodesk-internal paths (Apollo build
  servers), and project-ID folder names. Replaces with
  placeholder tokens while preserving path shape so claims
  remain verifiable.
- **PII guard CI job.** Scans the repo's committed files for
  PII-shaped patterns on every push. Blocks commits that contain
  them.
- **No embedded user emails, no account info.** The reader
  extracts path + GUID from Revit's own metadata streams; it
  doesn't invent or enrich PII.

### T5 — Supply chain

**Threat:** A transitively-included crate adds a malicious
dependency that the parser pulls in automatically.

**Mitigations:**

- **`cargo-deny check` on every push.** Enforced in CI (`deny`
  job). License allowlist (no GPL/proprietary), advisory deny,
  source allowlist (crates.io only, no arbitrary git).
- **`cargo-audit` on every push.** Enforced in CI (`audit` job).
  Fails CI on known RustSec advisories.
- **`actions/*` SHA-pinned (partial; pending SEC-29).** Common
  GitHub Actions pinned by commit hash to prevent
  tag-retargeting attacks.
- **OIDC-based PyPI publishing.** No long-lived API token in
  repo secrets. Token scope is per-release via Trusted Publisher.

## Out of scope

- **Side-channel timing attacks.** rvt-rs is not a cryptographic
  library. Timing variations on file parsing are not in the
  threat model.
- **Attacks requiring code modification.** We assume the library
  binary is what the crate authors published. Binary tampering
  is a supply-chain problem handled by cargo's own integrity
  mechanisms (checksums, signatures via `cargo sparse`).
- **Bypassing rvt-rs's validation with a second parser.** If
  downstream code re-parses the RVT with a different library and
  that library has bugs, those bugs are out of scope for
  rvt-rs's threat model.

## Reporting vulnerabilities

See [`SECURITY.md`](SECURITY.md) for the private reporting
channel. Do not file public issues for parser-safety bugs until
the fix has shipped in a release.