Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
525
vendor/ruvector/examples/prime-radiant/docs/SECURITY_AUDIT.md
vendored
Normal file
525
vendor/ruvector/examples/prime-radiant/docs/SECURITY_AUDIT.md
vendored
Normal file
@@ -0,0 +1,525 @@
|
||||
# Prime-Radiant Security Audit Report
|
||||
|
||||
**Audit Date:** 2026-01-22
|
||||
**Auditor:** V3 Security Architect
|
||||
**Crate:** prime-radiant (Coherence Engine)
|
||||
**Scope:** Memory safety, input validation, cryptographic concerns, WASM security, dependencies, code quality
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The Prime-Radiant coherence engine demonstrates **strong security fundamentals** with several notable strengths:
|
||||
- `#![deny(unsafe_code)]` enforced crate-wide
|
||||
- Parameterized SQL queries preventing SQL injection
|
||||
- Proper use of Result types throughout public APIs
|
||||
- Well-defined error types with thiserror
|
||||
|
||||
However, **17 security issues** were identified across the following categories:
|
||||
|
||||
| Severity | Count | Description |
|
||||
|----------|-------|-------------|
|
||||
| HIGH | 3 | Input validation gaps, panic-on-invalid-input |
|
||||
| MEDIUM | 8 | Numerical stability, resource exhaustion potential |
|
||||
| LOW | 4 | Code quality improvements, hardening recommendations |
|
||||
| INFO | 2 | Best practice recommendations |
|
||||
|
||||
---
|
||||
|
||||
## 1. Memory Safety Analysis
|
||||
|
||||
### 1.1 Unsafe Code Status: PASS
|
||||
|
||||
The crate explicitly denies unsafe code:
|
||||
```rust
|
||||
// /crates/prime-radiant/src/lib.rs:143
|
||||
#![deny(unsafe_code)]
|
||||
```
|
||||
|
||||
This is excellent and enforced at compile time. No unsafe blocks exist in the codebase.
|
||||
|
||||
### 1.2 Buffer Operations: MOSTLY SAFE
|
||||
|
||||
**SIMD Vector Operations** (`src/simd/vectors.rs`):
|
||||
- Uses `debug_assert!` for length checks (lines 50, 196-197, 286, 369-371)
|
||||
- These assertions only fire in debug mode; release builds skip validation
|
||||
|
||||
**FINDING [MED-1]: Release-Mode Bounds Check Missing**
|
||||
```rust
|
||||
// src/simd/vectors.rs:49-50
|
||||
pub fn dot_product_simd(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len(), "Vectors must have equal length");
|
||||
// In release mode, mismatched lengths cause undefined behavior
|
||||
```
|
||||
|
||||
**Recommendation:** Replace `debug_assert!` with proper Result-returning validation for public APIs.
|
||||
|
||||
### 1.3 GPU Buffer Operations: SAFE
|
||||
|
||||
Buffer management (`src/gpu/buffer.rs`) properly validates:
|
||||
- Buffer size limits (line 516): `if size > super::MAX_BUFFER_SIZE`
|
||||
- Buffer size mismatches (line 182-187): Returns `GpuError::BufferSizeMismatch`
|
||||
- Pool capacity limits (line 555): Enforces `max_pool_size`
|
||||
|
||||
---
|
||||
|
||||
## 2. Input Validation Analysis
|
||||
|
||||
### 2.1 Graph Size Limits: PARTIAL
|
||||
|
||||
**FINDING [HIGH-1]: No Maximum Graph Size Limit**
|
||||
|
||||
The `SheafGraph` (`src/substrate/graph.rs`) allows unbounded growth:
|
||||
```rust
|
||||
pub fn add_node(&self, node: SheafNode) -> NodeId {
|
||||
// No limit on node count
|
||||
self.nodes.insert(id, node);
|
||||
```
|
||||
|
||||
**DoS Risk:** An attacker could exhaust memory by adding unlimited nodes/edges.
|
||||
|
||||
**Recommendation:** Add configurable limits:
|
||||
```rust
|
||||
pub struct GraphLimits {
|
||||
pub max_nodes: usize, // Default: 1_000_000
|
||||
pub max_edges: usize, // Default: 10_000_000
|
||||
pub max_state_dim: usize, // Default: 65536
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Matrix Dimension Validation: PARTIAL
|
||||
|
||||
**FINDING [MED-2]: Large Matrix Allocation Without Bounds**
|
||||
|
||||
`RestrictionMap::identity()` allocates `dim * dim` without upper bound:
|
||||
```rust
|
||||
// src/coherence/engine.rs:214-225
|
||||
pub fn identity(dim: usize) -> Self {
|
||||
let mut matrix = vec![0.0; dim * dim]; // Unbounded!
|
||||
```
|
||||
|
||||
With `dim = 2^16`, this allocates 16GB.
|
||||
|
||||
**Recommendation:** Add dimension caps (suggested: 65536 for matrices).
|
||||
|
||||
### 2.3 File Path Validation: SAFE
|
||||
|
||||
PostgreSQL storage (`src/storage/postgres.rs`) uses parameterized queries:
|
||||
```rust
|
||||
// Line 362-377 - properly parameterized
|
||||
sqlx::query("INSERT INTO node_states (node_id, state, dimension, updated_at) VALUES ($1, $2, $3, NOW())")
|
||||
.bind(node_id)
|
||||
.bind(state)
|
||||
```
|
||||
|
||||
File storage (`src/storage/file.rs`) constructs paths but does not sanitize for traversal:
|
||||
|
||||
**FINDING [MED-3]: Potential Path Traversal in FileStorage**
|
||||
```rust
|
||||
// src/storage/file.rs:279-281
|
||||
fn node_path(&self, node_id: &str) -> PathBuf {
|
||||
let ext = if self.format == StorageFormat::Json { "json" } else { "bin" };
|
||||
self.root.join("nodes").join(format!("{}.{}", node_id, ext))
|
||||
}
|
||||
```
|
||||
|
||||
If `node_id = "../../../etc/passwd"`, this creates a traversal vector.
|
||||
|
||||
**Recommendation:** Validate node_id contains only alphanumeric, dash, underscore characters.
|
||||
|
||||
### 2.4 Signal Validation: EXISTS
|
||||
|
||||
The `SignalValidator` (`src/signal/validation.rs`) provides:
|
||||
- Maximum payload size validation (default 1MB)
|
||||
- Signal type allowlisting
|
||||
- Source non-empty validation
|
||||
|
||||
This is good but could be expanded.
|
||||
|
||||
---
|
||||
|
||||
## 3. Numerical Stability Analysis
|
||||
|
||||
### 3.1 NaN/Infinity Handling: INCOMPLETE
|
||||
|
||||
**FINDING [MED-4]: No NaN Checks on Input States**
|
||||
|
||||
State vectors accept NaN/Infinity without validation:
|
||||
```rust
|
||||
// src/substrate/node.rs
|
||||
pub fn update_state_from_slice(&mut self, new_state: &[f32]) {
|
||||
self.state = StateVector::from_slice(new_state);
|
||||
// No NaN check
|
||||
```
|
||||
|
||||
NaN propagates through all coherence computations silently.
|
||||
|
||||
**Locations using special float values:**
|
||||
- `src/hyperbolic/mod.rs:217`: `f32::MAX` for min_depth
|
||||
- `src/mincut/metrics.rs:55`: `f64::INFINITY` for min_cut_value
|
||||
- `src/attention/moe.rs:199`: `f32::NEG_INFINITY` for max logit
|
||||
- `src/ruvllm_integration/confidence.rs:376-379`: NaN for error states
|
||||
|
||||
**Recommendation:** Add validation helper:
|
||||
```rust
|
||||
pub fn validate_state(state: &[f32]) -> Result<(), ValidationError> {
|
||||
if state.iter().any(|x| x.is_nan() || x.is_infinite()) {
|
||||
return Err(ValidationError::InvalidFloat);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Division Safety: PARTIAL
|
||||
|
||||
Cosine similarity (`src/storage/postgres.rs:861-875`) properly handles zero norms:
|
||||
```rust
|
||||
if norm_a == 0.0 || norm_b == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
```
|
||||
|
||||
However, other locations may divide without checking.
|
||||
|
||||
---
|
||||
|
||||
## 4. Cryptographic Analysis
|
||||
|
||||
### 4.1 Random Number Generation: MIXED
|
||||
|
||||
**Good (Deterministic Seeds):**
|
||||
```rust
|
||||
// src/coherence/engine.rs:248-249
|
||||
use rand::{Rng, SeedableRng};
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
|
||||
```
|
||||
|
||||
This is appropriate for reproducible restriction maps.
|
||||
|
||||
**FINDING [MED-5]: Non-Cryptographic RNG for Node IDs**
|
||||
```rust
|
||||
// src/substrate/node.rs:48-49
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
```
|
||||
|
||||
`thread_rng()` is not cryptographically secure. While likely used for test data, if node IDs need unpredictability, use `OsRng` or `getrandom`.
|
||||
|
||||
### 4.2 Hash Functions: GOOD
|
||||
|
||||
The crate uses `blake3` for WAL checksums (`src/storage/file.rs:51-52`):
|
||||
```rust
|
||||
let checksum = *blake3::hash(&op_bytes).as_bytes();
|
||||
```
|
||||
|
||||
Blake3 is cryptographically strong and appropriate.
|
||||
|
||||
### 4.3 No Hardcoded Secrets: PASS
|
||||
|
||||
Searched codebase for hardcoded credentials, API keys, passwords - none found.
|
||||
|
||||
---
|
||||
|
||||
## 5. WASM-Specific Security
|
||||
|
||||
### 5.1 Memory Isolation: HANDLED BY WASM RUNTIME
|
||||
|
||||
The tiles module uses 256 WASM tiles. WASM provides:
|
||||
- Linear memory isolation
|
||||
- Control flow integrity
|
||||
- Type safety at boundaries
|
||||
|
||||
### 5.2 Data Cleanup: NOT EXPLICITLY HANDLED
|
||||
|
||||
**FINDING [LOW-1]: No Explicit Memory Zeroization**
|
||||
|
||||
Sensitive data in WASM memory (e.g., state vectors) is not explicitly zeroed after use. While WASM memory is isolated per instance, zeroing before deallocation is defense-in-depth.
|
||||
|
||||
**Recommendation:** For sensitive operations, use `zeroize` crate.
|
||||
|
||||
### 5.3 JS Boundary Error Handling: GOOD
|
||||
|
||||
The GPU module returns proper `GpuResult<T>` types across all boundaries.
|
||||
|
||||
---
|
||||
|
||||
## 6. Dependency Analysis
|
||||
|
||||
### 6.1 Cargo.toml Dependencies
|
||||
|
||||
Based on `/crates/prime-radiant/Cargo.toml`:
|
||||
|
||||
| Dependency | Version | Known CVEs | Status |
|
||||
|------------|---------|------------|--------|
|
||||
| blake3 | 1.5 | None | OK |
|
||||
| bytemuck | 1.21 | None | OK |
|
||||
| chrono | 0.4 | None (0.4.35+) | OK |
|
||||
| dashmap | 6.0 | None | OK |
|
||||
| parking_lot | 0.12 | None | OK |
|
||||
| rayon | 1.10 | None | OK |
|
||||
| serde | 1.0 | None | OK |
|
||||
| sqlx | 0.8 | None | OK |
|
||||
| thiserror | 2.0 | None | OK |
|
||||
| uuid | 1.10 | None | OK |
|
||||
| wgpu | 22.1 | None | OK |
|
||||
| wide | 0.7 | None | OK |
|
||||
| bincode | 2.0.0-rc.3 | None | OK (RC) |
|
||||
|
||||
**FINDING [LOW-2]: Using Release Candidate Dependency**
|
||||
`bincode = "2.0.0-rc.3"` is a release candidate. Consider pinning to stable when available.
|
||||
|
||||
### 6.2 Minimal Dependency Surface: GOOD
|
||||
|
||||
The crate uses feature flags to minimize attack surface:
|
||||
```toml
|
||||
[features]
|
||||
default = []
|
||||
postgres = ["sqlx/postgres"]
|
||||
gpu = ["wgpu"]
|
||||
simd = []
|
||||
parallel = ["rayon"]
|
||||
```
|
||||
|
||||
Only required features are compiled.
|
||||
|
||||
---
|
||||
|
||||
## 7. Code Quality Issues
|
||||
|
||||
### 7.1 Panic-Inducing Code
|
||||
|
||||
**FINDING [HIGH-2]: panic! in Library Code**
|
||||
```rust
|
||||
// src/distributed/adapter.rs:340
|
||||
panic!("Wrong command type");
|
||||
```
|
||||
|
||||
Library code should never panic; use Result instead.
|
||||
|
||||
**FINDING [HIGH-3]: unwrap() in Non-Test Code**
|
||||
```rust
|
||||
// src/governance/witness.rs:564
|
||||
self.head.as_ref().unwrap()
|
||||
```
|
||||
|
||||
This can panic if `head` is `None`.
|
||||
|
||||
**FINDING [MED-6]: expect() in Builders Without Validation**
|
||||
```rust
|
||||
// src/substrate/node.rs:454
|
||||
let state = self.state.expect("State vector is required");
|
||||
```
|
||||
|
||||
Builder pattern should return `Result<T, BuilderError>` instead of panicking.
|
||||
|
||||
### 7.2 Incomplete Error Propagation
|
||||
|
||||
Some locations use `.unwrap()` in test code (acceptable) but several are in production paths. Full list of production unwrap() calls:
|
||||
|
||||
1. `src/storage/file.rs:49` - WAL entry creation (partially justified)
|
||||
2. `src/simd/vectors.rs:499` - SIMD array conversion
|
||||
3. `src/simd/matrix.rs:390` - SIMD array conversion
|
||||
4. `src/simd/energy.rs:523` - SIMD array conversion
|
||||
5. `src/governance/witness.rs:564` - Head access
|
||||
|
||||
### 7.3 Timing Attack Considerations
|
||||
|
||||
**FINDING [MED-7]: Non-Constant-Time Comparisons**
|
||||
|
||||
Hash comparisons in WAL verification use standard equality:
|
||||
```rust
|
||||
// src/storage/file.rs:63
|
||||
fn verify(&self) -> bool {
|
||||
self.checksum == *blake3::hash(&bytes).as_bytes()
|
||||
}
|
||||
```
|
||||
|
||||
For security-critical hash comparisons, use constant-time comparison to prevent timing attacks:
|
||||
```rust
|
||||
use subtle::ConstantTimeEq;
|
||||
self.checksum.ct_eq(&hash).into()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Recommendations Summary
|
||||
|
||||
### Critical (Address Immediately)
|
||||
|
||||
| ID | Issue | File | Line | Fix |
|
||||
|----|-------|------|------|-----|
|
||||
| HIGH-1 | No graph size limits | substrate/graph.rs | 312 | Add `GraphLimits` config |
|
||||
| HIGH-2 | panic! in library | distributed/adapter.rs | 340 | Return Result |
|
||||
| HIGH-3 | unwrap() on Option | governance/witness.rs | 564 | Return Result |
|
||||
|
||||
### High Priority (Address in Phase 1)
|
||||
|
||||
| ID | Issue | File | Fix |
|
||||
|----|-------|------|-----|
|
||||
| MED-1 | Release-mode bounds | simd/vectors.rs | Add runtime validation |
|
||||
| MED-2 | Unbounded matrix allocation | coherence/engine.rs | Add dimension cap |
|
||||
| MED-3 | Path traversal potential | storage/file.rs | Validate node_id |
|
||||
| MED-4 | No NaN/Inf validation | substrate/node.rs | Add float validation |
|
||||
|
||||
### Medium Priority (Address in Phase 2)
|
||||
|
||||
| ID | Issue | File | Fix |
|
||||
|----|-------|------|-----|
|
||||
| MED-5 | Non-crypto RNG | substrate/node.rs | Document or use OsRng |
|
||||
| MED-6 | expect() in builders | substrate/*.rs | Return Result |
|
||||
| MED-7 | Timing attacks | storage/file.rs | Use constant-time |
|
||||
|
||||
### Low Priority (Best Practices)
|
||||
|
||||
| ID | Issue | Fix |
|
||||
|----|-------|-----|
|
||||
| LOW-1 | No memory zeroization | Use `zeroize` for sensitive data |
|
||||
| LOW-2 | RC dependency | Pin bincode to stable when available |
|
||||
|
||||
---
|
||||
|
||||
## 9. Production Deployment Recommendations
|
||||
|
||||
### 9.1 Resource Limits
|
||||
|
||||
Configure these limits before production deployment:
|
||||
|
||||
```rust
|
||||
let config = CoherenceConfig {
|
||||
max_nodes: 1_000_000,
|
||||
max_edges: 10_000_000,
|
||||
max_state_dimension: 4096,
|
||||
max_matrix_dimension: 8192,
|
||||
max_payload_size: 10 * 1024 * 1024, // 10MB
|
||||
max_concurrent_computations: 100,
|
||||
};
|
||||
```
|
||||
|
||||
### 9.2 Input Validation Layer
|
||||
|
||||
Add a validation middleware for all external inputs:
|
||||
|
||||
```rust
|
||||
pub struct SecureInputValidator {
|
||||
pub max_state_dim: usize,
|
||||
pub max_node_id_len: usize,
|
||||
pub allowed_id_chars: Regex,
|
||||
}
|
||||
|
||||
impl SecureInputValidator {
|
||||
pub fn validate_node_id(&self, id: &str) -> Result<(), ValidationError> {
|
||||
if id.len() > self.max_node_id_len {
|
||||
return Err(ValidationError::IdTooLong);
|
||||
}
|
||||
if !self.allowed_id_chars.is_match(id) {
|
||||
return Err(ValidationError::InvalidIdChars);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_state(&self, state: &[f32]) -> Result<(), ValidationError> {
|
||||
if state.len() > self.max_state_dim {
|
||||
return Err(ValidationError::StateTooLarge);
|
||||
}
|
||||
if state.iter().any(|x| x.is_nan() || x.is_infinite()) {
|
||||
return Err(ValidationError::InvalidFloat);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 Monitoring
|
||||
|
||||
Add these security-relevant metrics:
|
||||
- Graph size (nodes, edges)
|
||||
- Failed validation attempts
|
||||
- Memory usage per operation
|
||||
- Unusual pattern detection (rapid adds, large states)
|
||||
|
||||
### 9.4 Rate Limiting
|
||||
|
||||
Implement rate limiting for:
|
||||
- Node/edge additions per client
|
||||
- Energy computation requests
|
||||
- File storage operations
|
||||
|
||||
---
|
||||
|
||||
## 10. Compliance Notes
|
||||
|
||||
### 10.1 Rust Security Best Practices
|
||||
|
||||
| Practice | Status |
|
||||
|----------|--------|
|
||||
| No unsafe code | PASS |
|
||||
| Proper error types | PASS |
|
||||
| Result over panic | PARTIAL |
|
||||
| Input validation | PARTIAL |
|
||||
| Dependency management | PASS |
|
||||
|
||||
### 10.2 OWASP Considerations
|
||||
|
||||
| Risk | Mitigation Status |
|
||||
|------|-------------------|
|
||||
| Injection | PASS (parameterized SQL) |
|
||||
| Broken Auth | N/A (no auth in crate) |
|
||||
| Sensitive Data | PARTIAL (no zeroization) |
|
||||
| XXE | N/A (no XML) |
|
||||
| Access Control | N/A (application layer) |
|
||||
| Misconfig | PARTIAL (needs limits) |
|
||||
| XSS | N/A (no web output) |
|
||||
| Deserialization | PASS (serde/bincode safe) |
|
||||
| Logging | PARTIAL (needs audit logs) |
|
||||
| SSRF | N/A |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Files Audited
|
||||
|
||||
```
|
||||
src/
|
||||
├── lib.rs
|
||||
├── error.rs
|
||||
├── coherence/engine.rs
|
||||
├── distributed/adapter.rs
|
||||
├── governance/
|
||||
│ ├── mod.rs
|
||||
│ ├── witness.rs
|
||||
│ ├── lineage.rs
|
||||
│ └── repository.rs
|
||||
├── gpu/
|
||||
│ ├── mod.rs
|
||||
│ └── buffer.rs
|
||||
├── hyperbolic/
|
||||
│ ├── mod.rs
|
||||
│ ├── adapter.rs
|
||||
│ └── energy.rs
|
||||
├── simd/
|
||||
│ ├── mod.rs
|
||||
│ ├── vectors.rs
|
||||
│ ├── matrix.rs
|
||||
│ └── energy.rs
|
||||
├── signal/
|
||||
│ ├── mod.rs
|
||||
│ ├── validation.rs
|
||||
│ └── ingestion.rs
|
||||
├── storage/
|
||||
│ ├── mod.rs
|
||||
│ ├── file.rs
|
||||
│ └── postgres.rs
|
||||
├── substrate/
|
||||
│ ├── graph.rs
|
||||
│ ├── node.rs
|
||||
│ ├── edge.rs
|
||||
│ └── restriction.rs
|
||||
└── tiles/
|
||||
├── mod.rs
|
||||
├── adapter.rs
|
||||
└── coordinator.rs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Report Generated:** 2026-01-22
|
||||
**Next Audit Recommended:** 2026-04-22 (quarterly)
|
||||
Reference in New Issue
Block a user