Skip to content

Security: knitli/cui-registry

SECURITY.md

Security model for cui-registry

The cui-registry-* crates in this workspace are consumed at build time by Marque (and any other Rust project doing codegen from CUI registry data). If tampered registry data slips into that path, codegen produces backdoored Rust that mishandles CUI category validation, markings, or dissemination controls. The blast radius is broad, so the crate is hardened on multiple layers — and because this workspace (unlike ism-data) automates re-vendoring monthly, that automation is itself in the threat model. monthly-vendor.yml only ever opens a PR; a human merging it is the sole trigger for tagging and release (see vendor-pr-merged.yml), so a compromised or wrong upstream fetch stops at an open PR rather than a shipped crate.

Threat model

# Threat Mitigated by
1 Files in the cargo cache (or crates.io download cache) are tampered with locally before consumer build. build.rs re-hashes every file in data/ against the baked manifest at consumer compile time. Mismatch ⇒ build fails.
2 The consumer's cargo fetch is MITM'd. Git consumers: pin to a specific commit SHA, not a movable tag; commit Cargo.lock. crates.io consumers: crates.io itself is TLS + content-addressed by the registry index.
3 The cui-registry repo is force-pushed to with a malicious commit (knitli GitHub compromise). Consumer: pin to commit SHA or a specific crates.io version, review diffs before bumping. Maintainers: sign release tags, protect the default branch.
4 monthly-vendor.yml's automated fetch is served tampered content (compromised dodcui.mil, DNS hijack, or a poisoned Wayback Machine capture). Human-gated. cargo check/cargo test (which exercise build.rs's own integrity check) must pass before a PR is opened — but that only proves internal consistency, not that the content itself is legitimate. monthly-vendor.yml stops at opening a PR; it never commits to main, tags, or releases. vendor-pr-merged.yml only tags + releases once a human merges that PR, so a bad vendor requires an explicit human approval to ship, and is cheap to reject or revert either way. If you'd rather fully automate the commit/tag/release step with no human gate, see the alternative documented at the top of monthly-vendor.yml.
5 A future build.rs/vendor.py change introduces a malicious build-time or fetch-time side effect (network call beyond dodcui.mil/Wayback, file write outside OUT_DIR, arbitrary code execution during the monthly workflow). Consumer: review PRs before bumping. build.rs does only file reads inside its own data/. Maintainers: review any change to tools/vendor*.py or the workflows with extra scrutiny — that's exactly where an automated-release supply-chain attack would land.
6 Consumer uses stale registry data; new CUI categories go unrecognized. monthly-vendor.yml re-vendors automatically; the vendored change-log.yaml lets consumers see what changed and when.
7 Compile-time RCE via a malicious dependency. Minimal deps — only sha2 from RustCrypto, declared once in [workspace.dependencies].

What the workspace guarantees

  • Every file under crates/dod/data/ has its SHA-256 recorded in data/_provenance/manifest.txt.
  • build.rs (always-on by default) refuses to compile if any file's hash doesn't match the manifest.
  • The manifest is itself hashed: manifest.sha256 carries the digest, and MANIFEST_DIGEST in src/lib.rs carries the same value. A tampered on-disk manifest also fails the build.
  • The crate API surface is small: path helpers and two integrity functions. No I/O on path-helper calls, no lazy initialization, no global state.
  • Every automated re-vendor records, per source page, the fetch strategy (direct vs. Wayback), the resolved Wayback timestamp when applicable, and a SHA-256 of the raw fetched HTML, in vendor/sources.json — so a disputed vendor can be traced back to exactly what was fetched from where and when.

What the workspace does NOT guarantee

  • That www.dodcui.mil's published content is itself correct or authoritative at the moment of fetch. The workspace proves that what's on your disk matches what was vendored; it cannot prove the vendored content matches DoD's actual intent, especially for a Wayback-fallback fetch (see threat #4).
  • That a malicious actor with commit access to this repo, or write access to the monthly-vendor.yml/release.yml workflows, cannot publish tampered crates. Defenses there are operational: signed tags, protected branches, mandatory review on workflow/tooling changes.

Recommended consumer hardening (Marque)

  1. Pin to commit SHA or an exact crates.io version, not a movable tag.

  2. Commit Cargo.lock.

  3. Re-run verify_integrity() in CI:

    #[test]
    fn registry_intact() {
        cui_registry_dod::verify_integrity().expect("CUI registry data intact");
    }
  4. Don't disable verify-on-build lightly.

  5. Audit MANIFEST_DIGEST changes when bumping versions. A re-vendor with no real upstream change should reproduce the same category text; if the diff looks unrelated to what change-log.yaml/the commit message describes, stop and investigate before adopting the new version.

  6. Compile in a sandboxed environment when bumping. A compromised build.rs can run arbitrary code during cargo build.

Recommended workspace-maintenance hardening

  1. Sign release tags (git tag -s vX.Y.Z).
  2. Protect the default branch for changes to tools/vendor*.py, .github/workflows/monthly-vendor.yml, .github/workflows/vendor-pr-merged.yml, and crates/dod/build.rs — these are exactly where a supply-chain attack on the automation itself would land. automated-vendor-labeled PRs (data + manifest + provenance only) are lower-risk and diff-visible, but still require at least one human merge before anything tags or releases — don't enable auto-merge for that label.
  3. CI rejects manifest skewintegrity.yml runs tools/rederive_manifest.py and fails on any drift.
  4. Use cargo vet or cargo crev for sha2.
  5. Avoid adding new dependencies unless widely audited.

Reporting issues

Report security issues privately to security@knitli.com. Do not file public GitHub issues for vulnerabilities. We aim to acknowledge within 72 hours and to ship a fix for confirmed issues within 14 days.

There aren't any published security advisories