Major additions: - Community Next.js app (port 18187) for browsing claims with API docs - stemedb-chaos crate: Fault injection, chaos testing, CRDT properties - Latent ingestion system: Reddit/FDA ingesters with ADK-Go agents - Disputed claims handling: Manual review workflows and validation - Aphoria security scanner: New extractors (SQL injection, command injection, weak crypto, TLS version), policy-based ignores, UAT reports - Docker infrastructure: Dockerfile, docker-compose.yml for full stack - VulnBank demo: Intentionally vulnerable multi-language test corpus SDK & API enhancements: - Source registry handlers for tracking data provenance - Metrics endpoint - Skeptic filtering improvements Code quality: - Split 14 large files (>500 lines) into focused modules - All files now under 500-line limit per project guidelines Documentation: - Chaos testing guide, circuit breakers, observability docs - Phase 7 UAT documentation updates - Martin Kleppmann technical writer agent Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
76 lines
2.6 KiB
Rust
76 lines
2.6 KiB
Rust
//! JWT Authentication - Contains intentional vulnerabilities
|
|
//!
|
|
//! Vulnerabilities:
|
|
//! - Audience validation disabled
|
|
//! - Algorithm::None allowed
|
|
//! - Insecure decode without verification
|
|
|
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct Claims {
|
|
pub sub: String,
|
|
pub exp: usize,
|
|
pub aud: String,
|
|
}
|
|
|
|
/// VULNERABILITY: Audience validation disabled
|
|
/// RFC 7519 requires audience validation to prevent token misuse
|
|
pub fn verify_token_no_aud(token: &str, secret: &[u8]) -> Result<Claims, String> {
|
|
let mut validation = Validation::new(Algorithm::HS256);
|
|
|
|
// BLOCK: JWT audience validation disabled - tokens can be replayed across services
|
|
validation.validate_aud = false;
|
|
|
|
decode::<Claims>(token, &DecodingKey::from_secret(secret), &validation)
|
|
.map(|data| data.claims)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
/// VULNERABILITY: Algorithm::None allows unsigned tokens
|
|
/// Attackers can forge tokens without knowing the secret
|
|
pub fn verify_token_alg_none(token: &str) -> Result<Claims, String> {
|
|
// BLOCK: Algorithm::None allows forged tokens - critical authentication bypass
|
|
let validation = Validation::new(Algorithm::None);
|
|
|
|
decode::<Claims>(token, &DecodingKey::from_secret(&[]), &validation)
|
|
.map(|data| data.claims)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
/// VULNERABILITY: Dangerous insecure decode
|
|
/// Parses JWT without any signature verification
|
|
pub fn decode_without_verify(token: &str) -> Result<Claims, String> {
|
|
// BLOCK: dangerous_insecure_decode bypasses all security checks
|
|
let parts: Vec<&str> = token.split('.').collect();
|
|
if parts.len() != 3 {
|
|
return Err("Invalid token format".to_string());
|
|
}
|
|
|
|
// Decode payload without verifying signature - INSECURE
|
|
let payload = base64_decode(parts[1])?;
|
|
serde_json::from_slice(&payload).map_err(|e| e.to_string())
|
|
}
|
|
|
|
fn base64_decode(input: &str) -> Result<Vec<u8>, String> {
|
|
// Simplified base64 decode for demo
|
|
Ok(input.as_bytes().to_vec())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_insecure_patterns_exist() {
|
|
// These tests verify the vulnerable patterns are present for demo purposes
|
|
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNjE2MjM5MDIyLCJhdWQiOiJ0ZXN0In0.signature";
|
|
|
|
// All these should be detected by Aphoria
|
|
let _ = verify_token_no_aud(token, b"secret");
|
|
let _ = verify_token_alg_none(token);
|
|
let _ = decode_without_verify(token);
|
|
}
|
|
}
|