release: v1.1.3 — security hardening, edition 2024, dependency updates

Security:
- Fix RUSTSEC-2026-0007 (bytes integer overflow)
- Restrict SQLite database file permissions to 0600 on Unix
- Add 100KB size limit to intention descriptions (DoS prevention)
- Redact JSON-RPC payloads from debug logs (data leakage prevention)
- Update SECURITY.md with encryption docs and supported versions

Modernization:
- Upgrade Rust edition 2021 → 2024, MSRV 1.75 → 1.85
- Upgrade actions/checkout@v4 → v5, codecov/codecov-action@v3 → v5
- Update all dependencies to latest compatible versions
- Fix edition 2024 match ergonomics in compression.rs

Clippy fixes:
- Rename from_str → parse_name to avoid shadowing FromStr trait
- Replace .max().min() with .clamp()
- Replace sort_by with sort_by_key

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam Valladares 2026-02-12 03:19:07 -06:00
parent 6a5c3771fb
commit a92fb2b6ed
18 changed files with 332 additions and 118 deletions

View file

@ -546,8 +546,8 @@ impl MemoryCompressor {
let common_tags: Vec<_> = tag_counts
.iter()
.filter(|(_, &count)| count > memories.len() / 2)
.map(|(&tag, _)| tag)
.filter(|(_, count)| **count > memories.len() / 2)
.map(|(tag, _)| *tag)
.take(3)
.collect();

View file

@ -1591,9 +1591,7 @@ impl MemoryDreamer {
let word_novelty = (novel_words as f64 / total_words as f64) * 0.5;
// Boost novelty if connecting multiple sources
let source_bonus = ((source_memories.len() as f64 - 2.0) * 0.1)
.max(0.0)
.min(0.3);
let source_bonus = ((source_memories.len() as f64 - 2.0) * 0.1).clamp(0.0, 0.3);
(word_novelty + source_bonus + 0.2).min(1.0)
}

View file

@ -56,8 +56,8 @@ impl NodeType {
}
}
/// Parse from string
pub fn from_str(s: &str) -> Self {
/// Parse from string name
pub fn parse_name(s: &str) -> Self {
match s.to_lowercase().as_str() {
"fact" => NodeType::Fact,
"concept" => NodeType::Concept,
@ -215,7 +215,7 @@ impl KnowledgeNode {
/// Get the parsed node type
pub fn get_node_type(&self) -> NodeType {
NodeType::from_str(&self.node_type)
NodeType::parse_name(&self.node_type)
}
}
@ -330,7 +330,7 @@ mod tests {
NodeType::Event,
NodeType::Code,
] {
assert_eq!(NodeType::from_str(node_type.as_str()), node_type);
assert_eq!(NodeType::parse_name(node_type.as_str()), node_type);
}
}

View file

@ -177,8 +177,8 @@ impl MemoryState {
}
}
/// Parse from string representation.
pub fn from_str(s: &str) -> Self {
/// Parse from string name.
pub fn parse_name(s: &str) -> Self {
match s.to_lowercase().as_str() {
"active" => MemoryState::Active,
"dormant" => MemoryState::Dormant,
@ -1373,7 +1373,7 @@ mod tests {
MemoryState::Silent,
MemoryState::Unavailable,
] {
assert_eq!(MemoryState::from_str(state.as_str()), state);
assert_eq!(MemoryState::parse_name(state.as_str()), state);
}
}

View file

@ -1232,7 +1232,7 @@ impl ProspectiveMemory {
.collect();
// Sort by effective priority (highest first)
result.sort_by(|a, b| b.effective_priority().cmp(&a.effective_priority()));
result.sort_by_key(|i| std::cmp::Reverse(i.effective_priority()));
Ok(result)
}
@ -1301,7 +1301,7 @@ impl ProspectiveMemory {
}
// Sort triggered by effective priority
triggered.sort_by(|a, b| b.effective_priority().cmp(&a.effective_priority()));
triggered.sort_by_key(|i| std::cmp::Reverse(i.effective_priority()));
Ok(triggered)
}

View file

@ -99,12 +99,27 @@ impl Storage {
let data_dir = proj_dirs.data_dir();
std::fs::create_dir_all(data_dir)?;
// Restrict directory permissions to owner-only on Unix
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let perms = std::fs::Permissions::from_mode(0o700);
let _ = std::fs::set_permissions(data_dir, perms);
}
data_dir.join("vestige.db")
}
};
let conn = Connection::open(&path)?;
// Restrict database file permissions to owner-only on Unix
#[cfg(unix)]
if path.exists() {
use std::os::unix::fs::PermissionsExt;
let perms = std::fs::Permissions::from_mode(0o600);
let _ = std::fs::set_permissions(&path, perms);
}
// Apply encryption key if SQLCipher is enabled and key is provided
#[cfg(feature = "encryption")]
{