Files
clawhdf5/research/06-security-hardening.md
T
ClawHDF5 PlannerandClaude Sonnet 4.6 14db35aa74 research: ClawHDF5 deep-dive — architecture, performance, robustness, security
Seven research briefs covering the full mission scope:
01 — Architecture overview (crate map, format coverage, agent modules)
02 — Roadmap status and strategic gaps (distribution, MPI-IO, encryption)
03 — HDF5 ecosystem and cutting-edge developments (HDF5 2.0, Blosc2, ANN trends)
04 — Performance optimizations (10 opportunities, prioritized)
05 — Robustness enhancements (fuzzing gaps, bounds audit, WAL, KG cycle guard)
06 — Security hardening (encryption, signing, embedding poisoning, JNI safety)
07 — Synthesis and 15 actionable next steps with INT-NN task markers

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-08-12 11:25:53 +00:00

224 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Security Audit & Hardening Recommendations
*Research brief — generated 2026-08-12*
---
## 1. Threat Model
ClawHDF5 operates in two distinct threat environments:
**Environment A — Untrusted HDF5 files**: A user opens an HDF5 file from an untrusted source (downloaded file, network stream, user upload). The format parser must not crash, OOM, or execute arbitrary code.
**Environment B — Agent memory under adversarial input**: An AI agent writes memories sourced from external tool output, web content, or multi-agent messages. An adversary may attempt to poison the memory store by injecting crafted content.
**Out of scope (by design):** Network security (ClawHDF5 is a file-based library with no built-in networking). Authentication and access control at the OS level.
---
## 2. Current Security Posture
### 2.1 What's Already Done (Strong)
| Control | Implementation | Coverage |
|---------|----------------|----------|
| **Decompression output bound** | `MAX_DECOMPRESS_SIZE` in `filters.rs` | Deflate, LZ4, Zstd, Pcodec |
| **Allocation guards before alloc** | Length-prefix caps before `Vec::with_capacity` calls | WAL (`MAX_WAL_FIELD_LEN` = 64 MiB), VDS mapping parser, H5S decoder |
| **Arithmetic overflow guards** | `ensure_len` helper; `checked_add` / `checked_mul` in critical paths | `chunked_read.rs`, `btree_v1.rs`, `local_heap.rs`, scale-offset, N-Bit |
| **Recursion depth guard** | Depth counter on cyclic B-tree traversal; N-Bit type tree cap | `btree_v1.rs`, `filters.rs` |
| **WAL entry integrity** | CRC32 trailer per entry (WAL_VERSION 2) — bit-flip stops replay cleanly | `clawhdf5-agent::wal` |
| **Content hashing** | FNV-1a for memory chunks (anomaly detection); SHA-256 for provenance attributes | `provenance.rs` |
| **Injection pattern detection** | 15 patterns in `anomaly.rs` | Prompt injection, role impersonation, etc. |
| **Write rate limiting** | `anomaly.rs` rate limiter | Flood attacks on memory store |
| **Source isolation** | Per-`MemorySource` sub-stores | User vs System vs Tool source separation |
| **Android JNI safety** | Bounds-check on `embedding_len`; null pointer rejection | `clawhdf5-android` JNI functions |
| **PyO3 safety** | pyo3/numpy 0.29 (clears two RUSTSEC advisories) | Python bindings |
| **Fuzz coverage** | `fuzz_filter_pipeline`, `fuzz_dataset_read` | Filter pipeline; dataset read paths |
### 2.2 Documented Limitations
The CHANGELOG explicitly documents:
> "The integrity hashes in `clawhdf5-agent::provenance` (FNV-1a) and `clawhdf5-format::provenance` (SHA-256) are unkeyed and detect only accidental corruption, not tampering — doc-only change, no behavior change."
This is an important honesty note: the current provenance system is **not** a tamper-detection mechanism.
---
## 3. Security Gaps & Recommendations
### 3.1 Missing: Encryption at Rest (HIGH PRIORITY)
**Gap:** There is no encryption for the HDF5 file or WAL. A `.brain` file or `agent_memory.h5` containing personal data, credentials mentioned in conversation, or proprietary knowledge is stored in plaintext.
**Attack scenario:** An attacker with filesystem access to the `.h5` file (e.g., via a directory traversal vulnerability in an app using ClawHDF5, or physical access to a laptop) can read all agent memories.
**Recommendation:**
Implement an `encryption` feature using `aes-gcm` (from the `aes-gcm` crate — pure Rust, audited):
```rust
// Proposed API addition to MemoryConfig:
pub struct MemoryConfig {
// ... existing fields ...
pub encryption_key: Option<[u8; 32]>, // AES-256-GCM key
}
```
Implementation approach:
1. Store a random 96-bit nonce per HDF5 chunk alongside the chunk data.
2. Encrypt each chunk's decompressed data with AES-256-GCM before writing; decrypt on read.
3. Encrypt WAL entries with the same key.
4. Store a key-derivation salt in the file header; derive the working key from a user passphrase via Argon2id.
5. The HDF5 file is still structurally valid (h5py can open it and see dataset shapes) but all data values are ciphertext — this is a deliberate tradeoff (vs encrypting the entire file as a blob).
**Alternative:** Encrypt the entire `.h5` file as a blob using AES-256-CTR with a random IV stored in a plaintext header. Simpler but loses partial-decryption ability.
**Effort estimate:** 23 weeks. The `aes-gcm` and `argon2` crates are well-audited and integrate cleanly into Rust.
---
### 3.2 Missing: Tamper Detection / Signing (HIGH PRIORITY for `.brain` files)
**Gap:** The SHA-256 provenance attributes detect accidental corruption but not intentional tampering. An adversary who can write to the `.h5` file can update both the data and the SHA-256 hash.
**Attack scenario:** A compromised `.brain` file is distributed from ClawBrainHub. A user downloads it, trusting the provenance hashes, but the hashes have been re-computed over poisoned data.
**Recommendation:**
1. **Ed25519 signatures**: Add an `[package] signing_key` field to `MemoryConfig`. When signing is enabled, compute an Ed25519 signature over the dataset contents + SHA-256 provenance hash and store it as an HDF5 attribute. Verify on open.
2. **ClawBrainHub trust chain**: The registry should sign `.brain` files with a registry key. ClawHDF5 should ship a `clawhdf5-cli verify` command that checks the registry signature.
**Crates:** `ed25519-dalek` (pure Rust, widely audited).
**Effort estimate:** 12 weeks for basic file signing. ClawBrainHub registry integration is a separate effort.
---
### 3.3 Incomplete: Embedding-Space Poisoning Detection (MEDIUM PRIORITY)
**Gap:** The 15 injection patterns in `anomaly.rs` detect text-level injection attempts (e.g., "Ignore previous instructions"). They do not detect **embedding-space poisoning** — adversarially crafted embeddings that are semantically close to arbitrary queries in vector space but contain malicious text.
**Attack scenario (from MemoryGraft paper):** A tool output contains text that, when embedded, produces a vector close to "user preferences" in the embedding space. Future queries for "user preferences" retrieve the poisoned memory instead of genuine ones.
**Recommendation:**
1. **Embedding anomaly detection**: Compute the distribution of embeddings in the store (mean + covariance). Flag new embeddings whose Mahalanobis distance from the distribution centroid exceeds a threshold. This is a statistical outlier detector.
2. **Cluster consistency check**: After every write batch, verify that the new embedding does not shift the cluster assignment of nearby memories by more than a configurable fraction.
3. **Source-aware embedding validation**: Embeddings from untrusted sources (e.g., `MemorySource::Tool`) should be quarantined and require explicit promotion to the main store.
**Effort estimate:** 12 weeks for Mahalanobis detection; 23 weeks for cluster consistency.
---
### 3.4 Incomplete: Timestamp Integrity (MEDIUM PRIORITY)
**Gap:** Memory timestamps are stored in the HDF5 file as plain `f64` values. The WAL CRC32 detects accidental bit-flips but not intentional timestamp manipulation by an adversary who writes to the HDF5 file.
**Attack scenario:** An adversary modifies timestamps in the HDF5 file to make recent poisoned memories appear old (and thus trusted by the temporal re-ranking component) or to make old poisoned memories appear recent.
**Recommendation:**
1. **Signed timestamps**: When file signing is enabled (see 3.2), include timestamps in the signed data.
2. **Monotonic timestamp enforcement**: In the write path, reject any attempt to write a timestamp older than the last written timestamp in the same source channel. The WAL's append-only nature already provides this for WAL entries; extend it to the HDF5 dataset.
---
### 3.5 JNI Thread Safety (MEDIUM PRIORITY)
**Gap:** The Android JNI functions operate on a raw `*mut HDF5Memory` handle with no synchronization. The handle is cast from a `jlong` and used as `&mut HDF5Memory`.
**Attack scenario:** Two Java threads call JNI functions on the same handle simultaneously → data race → undefined behavior in unsafe Rust.
**Recommendation:**
Wrap the `HDF5Memory` handle in a `Mutex<HDF5Memory>` and store the `Mutex` in a `Box` (as is standard for JNI handle storage):
```rust
// Current:
let memory = unsafe { &mut *(handle as *mut HDF5Memory) };
// Recommended:
let locked = unsafe { &*(handle as *const Mutex<HDF5Memory>) };
let mut memory = locked.lock().unwrap();
```
**Effort estimate:** 12 days. Low risk, high impact for multi-threaded Android use.
---
### 3.6 Media Reference Sandboxing (MEDIUM PRIORITY)
**Gap:** `MediaRef::Path` stores filesystem paths from arbitrary sources (including adversarial memory content). If the agent resolves these paths, a crafted `../../../etc/passwd` path could expose sensitive files.
**Recommendation:**
1. **Allowlist-based path validation**: The agent should only resolve `MediaRef::Path` entries that are within a configured `media_sandbox_dir`.
2. **Canonicalization before resolution**: Always call `std::fs::canonicalize` before resolving a path, then check it is within the sandbox.
3. **URL scheme allowlist**: `MediaRef::Url` should only allow `https://` by default. Reject `file://`, `data:`, `javascript:`, etc.
---
### 3.7 SZIP FFI Safety (LOW PRIORITY)
**Gap:** The `szip` feature introduces `libaec` C FFI. Incorrect FFI arguments (wrong `chunk_size`, mismatched `bits_per_sample`) could cause the C library to write past the allocated output buffer.
**Recommendation:**
1. The current implementation validates `cd.len() >= 5` and checks `bits_per_sample > 0 && <= 32`. Add a check that `chunk_size` is non-zero and does not exceed a maximum (e.g., 512 MiB).
2. Consider wrapping the `aec_buffer_decode` call in `std::panic::catch_unwind` (if the C library signals errors via signals, not return codes — verify with libaec docs).
3. Add a fuzz target (`fuzz_szip_decompress`) when the `szip` feature is enabled.
---
### 3.8 Denial of Service: Adversarial HDF5 Files (LOW PRIORITY — partially mitigated)
**Current mitigations:** `MAX_DECOMPRESS_SIZE`, allocation guards, recursion depth caps, `H5S_MAX_RANK` cap. These collectively address the most dangerous DoS vectors.
**Remaining gaps:**
1. **Large group with many dense links**: A group with millions of links in the v2 B-tree will take O(N) memory to iterate. Add a cap (`MAX_LINKS_PER_GROUP`) that returns an error rather than allocating unboundedly.
2. **Very long string attributes**: The `local_heap.rs` fixes guard overflow arithmetic but there is no explicit cap on total string heap size. Add `MAX_STRING_HEAP_BYTES`.
3. **Deeply nested compound types**: The N-Bit type tree recursion is now capped (CHANGELOG), but compound types can also be nested arbitrarily. Verify compound type recursion depth is capped.
---
## 4. Dependency Security
### 4.1 RUSTSEC Advisories
The pyo3/numpy bump (0.28 → 0.29) cleared two RUSTSEC advisories. Recommended:
- Add `cargo-audit` to CI: `cargo audit --deny warnings` after every dependency update.
- Pin a `cargo-audit` version in CI to prevent false positives from advisory DB updates.
### 4.2 Supply Chain
| Dependency | Risk Level | Notes |
|------------|------------|-------|
| `libaec-sys` / libaec (SZIP) | Medium | C FFI; optional. Pin to a specific libaec version in the sys crate. |
| `system-zlib` / zlib-ng | Medium | C FFI; optional. Default path uses zlib-ng. Consider migrating to `zlib-rs`. |
| `wgpu` (GPU) | Low | Pure Rust + GPU driver ABI. Well-maintained. |
| `pyo3` 0.29 | Low | Recently updated; audit at each bump. |
| `tokio` (`async` feature) | Low | Well-audited, widely used. |
### 4.3 `cargo-deny` Configuration
Add `deny.toml` at workspace root to enforce:
- No duplicate dependencies at different semver versions
- No `unmaintained` crates in the dependency tree
- No licenses incompatible with MIT
---
## 5. Security Roadmap (Prioritized)
| Item | Priority | Effort | Impact |
|------|----------|--------|--------|
| AES-256-GCM encryption at rest | HIGH | 23 weeks | Confidentiality for `.brain` / sensitive memories |
| Ed25519 file signing | HIGH | 12 weeks | Tamper detection for distributed `.brain` files |
| JNI `Mutex` wrapping | MEDIUM | 12 days | UB prevention on multi-threaded Android |
| `cargo-audit` in CI | MEDIUM | 1 day | Continuous dependency advisory monitoring |
| `cargo-deny` configuration | LOW | 1 day | Dependency hygiene |
| Media reference sandboxing | MEDIUM | 1 week | Path traversal prevention |
| Embedding-space anomaly detection | MEDIUM | 23 weeks | Poisoning resistance beyond text patterns |
| Monotonic timestamp enforcement | MEDIUM | 35 days | Temporal poisoning resistance |
| Overflow-checks = true in release | HIGH | 1 hour | Defense in depth for format parsing |
| WAL commit marker for atomic rotation | MEDIUM | 1 week | Consistency guarantee on crash during flush |
| SZIP fuzz target | LOW | 1 day | C FFI boundary hardening |