Skip to content

Commit 3fd9417

Browse files
authored
Merge pull request #24 from saorsa-labs/security-hardening-and-test-coverage
feat: security hardening and comprehensive test coverage
2 parents d34f126 + 773e47a commit 3fd9417

18 files changed

Lines changed: 2547 additions & 189 deletions

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ quickcheck = "1.0"
142142
quickcheck_macros = "1.0"
143143
approx = "0.5"
144144
tokio-test = "0.4"
145+
semver = "1.0"
145146

146147
# All features are now enabled by default - no feature flags needed
147148

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Key design decisions are documented in [docs/adr/](docs/adr/):
3434

3535
## Features
3636

37-
- **P2P NAT Traversal**: True peer-to-peer connectivity with automatic NAT traversal (ant-quic 0.10.0+)
37+
- **P2P NAT Traversal**: True peer-to-peer connectivity with automatic NAT traversal (ant-quic 0.21.x)
3838
- **DHT (Distributed Hash Table)**: Peer phonebook and routing with adaptive scoring and geographic awareness
3939
- **Placement System**: Intelligent shard placement with EigenTrust integration
4040
- **QUIC Transport**: High-performance networking with ant-quic
@@ -52,7 +52,7 @@ Add this to your `Cargo.toml`:
5252

5353
```toml
5454
[dependencies]
55-
saorsa-core = "0.5.0"
55+
saorsa-core = "0.11.0"
5656
```
5757

5858
### Basic P2P Node
@@ -108,7 +108,7 @@ tokio::spawn(async move {
108108

109109
### Core Components
110110

111-
1. **Network Layer**: QUIC-based P2P networking with automatic NAT traversal (ant-quic 0.10.0+)
111+
1. **Network Layer**: QUIC-based P2P networking with automatic NAT traversal (ant-quic 0.21.x)
112112
2. **DHT**: S/Kademlia-based peer phonebook with adaptive routing and geographic awareness
113113
3. **Placement System**: Intelligent shard placement with weighted selection algorithms
114114
4. **Identity**: Post‑quantum cryptographic identities with ML‑DSA‑65 signatures (no PoW; no embedded four‑word address)
@@ -387,7 +387,7 @@ For commercial licensing, contact: david@saorsalabs.com
387387
- `tracing` - Logging
388388

389389
### Networking
390-
- `ant-quic` (0.10.0+) - QUIC transport with P2P NAT traversal
390+
- `ant-quic` (0.21.x) - QUIC transport with P2P NAT traversal
391391
- `four-word-networking` - Human-readable addresses
392392

393393
### Cryptography

docs/API.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ Enable optional features in `Cargo.toml`:
484484
485485
```toml
486486
[dependencies]
487-
saorsa-core = { version = "0.5", features = ["metrics"] }
487+
saorsa-core = { version = "0.11", features = ["metrics"] }
488488
```
489489
490490
| Feature | Description |
@@ -515,8 +515,9 @@ spawn(async move {
515515
516516
| saorsa-core | ant-quic | Rust | Features |
517517
|-------------|----------|------|----------|
518-
| 0.5.x | 0.14.x | 1.75+ | Full PQC, unified config |
519-
| 0.4.x | 0.10.x | 1.70+ | NAT traversal |
518+
| 0.11.x | 0.21.x | 1.75+ | Full PQC, placement system, threshold crypto |
519+
| 0.10.x | 0.20.x | 1.75+ | Full PQC, unified config |
520+
| 0.5.x | 0.14.x | 1.75+ | Legacy stable |
520521
521522
---
522523

docs/adr/ADR-002-delegated-transport.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,10 @@ We track ant-quic versions explicitly and test against specific releases:
132132

133133
| saorsa-core | ant-quic | Features |
134134
|-------------|----------|----------|
135+
| 0.11.x | 0.21.x | Full PQC, placement system, threshold crypto |
136+
| 0.10.x | 0.20.x | Full PQC, unified config |
137+
| 0.5.x | 0.14.x | Unified config, PQC integration |
135138
| 0.3.x | 0.10.x | Basic QUIC, NAT traversal |
136-
| 0.4.x | 0.14.x | Unified config, PQC integration |
137-
| Future | 0.15+ | Enhanced relay support |
138139

139140
## Consequences
140141

docs/examples/saorsa-node-trust-integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Add saorsa-core dependency in your `Cargo.toml` with the `adaptive-ml` feature e
99

1010
```toml
1111
[dependencies]
12-
saorsa-core = { version = "0.10", features = ["adaptive-ml"] }
12+
saorsa-core = { version = "0.11.0", features = ["adaptive-ml"] }
1313
```
1414

1515
Note: The `adaptive-ml` feature is required for trust API methods (`report_peer_success`,

docs/trust-signals-api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The trust API requires the `adaptive-ml` feature to be enabled:
1717

1818
```toml
1919
[dependencies]
20-
saorsa-core = { version = "0.10", features = ["adaptive-ml"] }
20+
saorsa-core = { version = "0.11.0", features = ["adaptive-ml"] }
2121
```
2222

2323
## Quick Start

src/adaptive/hyperbolic_greedy.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -617,12 +617,10 @@ fn deterministic_distance(peer1: &PeerId, peer2: &PeerId) -> f64 {
617617
/// It measures distances between peers and optimizes coordinates using gradient descent.
618618
pub async fn embed_snapshot(peers: &[PeerId]) -> Result<Embedding> {
619619
// Create a temporary router for embedding
620-
let local_id = if !peers.is_empty() {
621-
peers[0].clone()
622-
} else {
623-
// Generate a random PeerId
624-
format!("peer_{}", rand::random::<u64>())
625-
};
620+
let local_id = peers
621+
.first()
622+
.cloned()
623+
.unwrap_or_else(|| format!("peer_{}", rand::random::<u64>()));
626624

627625
let router = HyperbolicGreedyRouter::new(local_id);
628626
router.embed_snapshot(peers).await

src/adaptive/storage.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,11 @@ impl ChunkManager {
447447
return Err(anyhow::anyhow!("No chunks provided"));
448448
}
449449

450-
let total_chunks = sorted_chunks[0].metadata.total_chunks;
450+
let total_chunks = sorted_chunks
451+
.first()
452+
.ok_or_else(|| anyhow::anyhow!("No chunks provided for reconstruction"))?
453+
.metadata
454+
.total_chunks;
451455
if sorted_chunks.len() != total_chunks as usize {
452456
return Err(anyhow::anyhow!(
453457
"Missing chunks: have {}, need {}",

src/adaptive/transport.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@
1818
1919
use super::*;
2020
use async_trait::async_trait;
21+
use lru::LruCache;
2122
use std::collections::HashMap;
2223
use std::net::SocketAddr;
24+
use std::num::NonZeroUsize;
2325
use std::sync::Arc;
2426
use tokio::io::{AsyncRead, AsyncWrite};
2527
use tokio::net::{TcpListener, TcpStream};
2628
use tokio::sync::RwLock;
2729
use tracing;
2830

31+
/// Maximum number of cached TCP connections to prevent memory exhaustion
32+
const MAX_TCP_CONNECTIONS: usize = 1_000;
33+
2934
/// Transport protocol types
3035
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3136
pub enum TransportProtocol {
@@ -83,8 +88,8 @@ pub trait TransportConnection: AsyncRead + AsyncWrite + Send + Sync + Unpin {
8388

8489
/// TCP transport implementation
8590
pub struct TcpTransport {
86-
/// Connection pool for reuse
87-
connections: Arc<RwLock<HashMap<SocketAddr, Arc<RwLock<TcpStream>>>>>,
91+
/// Connection pool for reuse (bounded LRU to prevent memory DoS)
92+
connections: Arc<RwLock<LruCache<SocketAddr, Arc<RwLock<TcpStream>>>>>,
8893
}
8994

9095
impl Default for TcpTransport {
@@ -96,7 +101,9 @@ impl Default for TcpTransport {
96101
impl TcpTransport {
97102
pub fn new() -> Self {
98103
Self {
99-
connections: Arc::new(RwLock::new(HashMap::new())),
104+
connections: Arc::new(RwLock::new(LruCache::new(
105+
NonZeroUsize::new(MAX_TCP_CONNECTIONS).unwrap_or(NonZeroUsize::MIN),
106+
))),
100107
}
101108
}
102109
}
@@ -112,10 +119,10 @@ impl Transport for TcpTransport {
112119
}
113120

114121
async fn connect(&self, addr: SocketAddr) -> Result<Box<dyn TransportConnection>> {
115-
// Check connection pool first
122+
// Check connection pool first (use peek to avoid mutating LRU order with read lock)
116123
{
117124
let connections = self.connections.read().await;
118-
if let Some(_conn) = connections.get(&addr) {
125+
if let Some(_conn) = connections.peek(&addr) {
119126
// TODO: Check if connection is still alive
120127
// For now, always create new connection
121128
}
@@ -390,7 +397,11 @@ impl TransportManager {
390397

391398
// For now, just try the first listener
392399
// TODO: Implement proper multiplexing
393-
listeners[0].accept().await
400+
listeners
401+
.first()
402+
.ok_or_else(|| AdaptiveNetworkError::Other("No listeners available".to_string()))?
403+
.accept()
404+
.await
394405
}
395406
}
396407

src/bootstrap/manager.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,14 @@ impl BootstrapManager {
191191
)));
192192
}
193193

194-
let ip = addresses[0].ip();
194+
let ip = addresses
195+
.first()
196+
.ok_or_else(|| {
197+
P2PError::Bootstrap(BootstrapError::InvalidData(
198+
"No addresses provided".to_string().into(),
199+
))
200+
})?
201+
.ip();
195202

196203
// Rate limiting check
197204
self.rate_limiter.check_join_allowed(&ip).map_err(|e| {

0 commit comments

Comments
 (0)