reputation-core 0.1.0

Core calculation engine for the KnowThat Reputation System with advanced scoring algorithms
Documentation
//! Security-focused tests for the reputation engine
//! 
//! This module contains tests specifically designed to verify the security
//! properties of the reputation system, including input validation, overflow
//! protection, and error handling.

use reputation_core::Calculator;
use reputation_types::AgentDataBuilder;
use chrono::{Utc, Duration};

#[test]
fn test_did_validation_security() {
    // Test various malicious DID formats
    let malicious_dids = [
        // Path traversal attempts
        "did:test:../../../etc/passwd",
        "did:test://etc/passwd",
        "did:test:agent/../../../secret",
        
        // SQL injection attempts
        "did:test:'; DROP TABLE agents; --",
        "did:test:' OR '1'='1",
        
        // Script injection
        "did:test:<script>alert('xss')</script>",
        "did:test:javascript:alert(1)",
        
        // Invalid characters
        "did:test:\0null",
        "did:test:\nnewline",
        "did:test:\ttab",
        
        // Empty components
        "did::",
        "did:test:",
        ":test:agent",
        
        // Very long DIDs (over 1000 char limit)
        &format!("did:test:{}", "a".repeat(2000)),
    ];
    
    for malicious_did in &malicious_dids {
        let result = AgentDataBuilder::new(*malicious_did).build();
        assert!(
            result.is_err(),
            "Malicious DID should be rejected: {}",
            malicious_did
        );
    }
}

#[test]
fn test_arithmetic_overflow_protection() {
    let calc = Calculator::default();
    
    // Test with maximum values
    let agent = AgentDataBuilder::new("did:test:overflow")
        .total_interactions(u32::MAX)
        .total_reviews(u32::MAX / 2)
        .with_reviews(u32::MAX / 2, 5.0)
        .build()
        .unwrap();
    
    // Should not panic or overflow
    let result = calc.calculate(&agent);
    assert!(result.is_ok());
    
    let score = result.unwrap();
    assert!(score.score >= 0.0 && score.score <= 100.0);
    assert!(score.confidence >= 0.0 && score.confidence <= 1.0);
}

#[test]
fn test_rating_bounds_security() {
    // Test ratings outside valid bounds
    let invalid_ratings = [
        0.0,      // Below minimum
        0.5,      // Below minimum
        0.99999,  // Just below minimum
        5.00001,  // Just above maximum
        6.0,      // Above maximum
        100.0,    // Way above maximum
        -1.0,     // Negative
        -5.0,     // Negative
        f64::NAN,         // Not a number
        f64::INFINITY,    // Positive infinity
        f64::NEG_INFINITY, // Negative infinity
    ];
    
    for rating in &invalid_ratings {
        let result = AgentDataBuilder::new("did:test:rating")
            .total_interactions(10)
            .with_reviews(5, *rating)
            .build();
        
        assert!(
            result.is_err(),
            "Invalid rating {} should be rejected",
            rating
        );
    }
    
    // Test valid edge cases
    let valid_ratings = [1.0, 1.000001, 3.0, 4.999999, 5.0];
    
    for rating in &valid_ratings {
        let result = AgentDataBuilder::new("did:test:rating-valid")
            .total_interactions(10)
            .with_reviews(5, *rating)
            .build();
        
        assert!(
            result.is_ok(),
            "Valid rating {} should be accepted",
            rating
        );
    }
}

#[test]
fn test_review_consistency_security() {
    // Test cases where reviews > interactions
    let result = AgentDataBuilder::new("did:test:consistency")
        .total_interactions(10)
        .total_reviews(20) // More reviews than interactions
        .build();
    
    assert!(result.is_err(), "Reviews cannot exceed interactions");
    
    // Test with very large numbers that could overflow when added
    let large_num = u32::MAX / 2 + 1000;
    let result = AgentDataBuilder::new("did:test:large")
        .total_interactions(large_num)
        .total_reviews(large_num)
        .build();
    
    // Should handle large numbers gracefully
    assert!(result.is_ok(), "Should handle large numbers safely");
}

#[test]
fn test_mcp_level_validation_security() {
    // Test invalid MCP levels
    let invalid_levels = [0, 4, 5, 100, 255];
    
    for level in &invalid_levels {
        let result = AgentDataBuilder::new("did:test:mcp")
            .mcp_level(*level)
            .build();
        
        match level {
            0 => assert!(result.is_ok(), "MCP level 0 (None) should be valid"),
            _ => assert!(result.is_err(), "Invalid MCP level {} should be rejected", level),
        }
    }
}

#[test]
fn test_date_validation_security() {
    // Test future dates
    let future_date = Utc::now() + Duration::days(1);
    let result = AgentDataBuilder::new("did:test:future")
        .created_at(future_date)
        .build();
    
    assert!(result.is_err(), "Future creation dates should be rejected");
    
    // Test very old dates (should be accepted)
    let old_date = Utc::now() - Duration::days(365 * 10);
    let result = AgentDataBuilder::new("did:test:old")
        .created_at(old_date)
        .build();
    
    assert!(result.is_ok(), "Old dates should be accepted");
}

#[test]
fn test_error_message_security() {
    // Ensure error messages don't leak sensitive information
    let result = AgentDataBuilder::new("did:test:'; DROP TABLE --")
        .build();
    
    if let Err(e) = result {
        let error_str = e.to_string();
        
        // Error message should not contain the malicious input
        assert!(
            !error_str.contains("DROP TABLE"),
            "Error message should not echo malicious input"
        );
        
        // Should provide helpful but safe error message
        assert!(
            error_str.contains("invalid format") || error_str.contains("DID"),
            "Error message should indicate the problem without details"
        );
    } else {
        panic!("Expected error for malicious DID");
    }
}

#[test]
fn test_confidence_calculation_bounds() {
    let calc = Calculator::default();
    
    // Test edge cases for confidence calculation
    let test_cases = [
        (0, 0.0),          // Zero interactions
        (1, 0.0625),       // One interaction
        (15, 0.5),         // k interactions (50% confidence)
        (u32::MAX, 1.0),   // Maximum interactions (approaches 1.0)
    ];
    
    for (interactions, expected_min) in test_cases {
        let agent = AgentDataBuilder::new("did:test:conf")
            .total_interactions(interactions)
            .build()
            .unwrap();
        
        let score = calc.calculate(&agent).unwrap();
        
        assert!(
            score.confidence >= expected_min - 0.01,
            "Confidence {} below expected {} for {} interactions",
            score.confidence,
            expected_min,
            interactions
        );
        
        assert!(
            score.confidence <= 1.0,
            "Confidence {} exceeds maximum",
            score.confidence
        );
    }
}

#[test]
fn test_score_bounds_with_extreme_inputs() {
    let calc = Calculator::default();
    
    // Test all extreme combinations
    let extreme_agents = vec![
        // Maximum everything
        AgentDataBuilder::new("did:test:max-all")
            .total_interactions(u32::MAX)
            .with_reviews(u32::MAX / 2, 5.0)
            .mcp_level(3)
            .identity_verified(true)
            .security_audit_passed(true)
            .open_source(true)
            .created_at(Utc::now() - Duration::days(730))
            .build()
            .unwrap(),
        
        // Minimum everything
        AgentDataBuilder::new("did:test:min-all")
            .build()
            .unwrap(),
        
        // High interaction, low rating
        AgentDataBuilder::new("did:test:high-low")
            .total_interactions(100000)
            .with_reviews(50000, 1.0)
            .build()
            .unwrap(),
    ];
    
    for agent in extreme_agents {
        let result = calc.calculate(&agent);
        assert!(result.is_ok(), "Should handle extreme inputs");
        
        let score = result.unwrap();
        assert!(
            score.score >= 0.0 && score.score <= 100.0,
            "Score {} out of bounds for agent {}",
            score.score,
            agent.did
        );
    }
}

#[test]
fn test_no_panic_paths() {
    // This test verifies that no combination of valid inputs can cause a panic
    let calc = Calculator::default();
    
    // Generate various edge case combinations
    let interactions = [0, 1, 10, 100, 1000, 10000, u32::MAX];
    let ratings = [1.0, 2.5, 3.0, 4.5, 5.0];
    let ages = [0, 30, 90, 365, 730];
    
    for &inter in &interactions {
        for &rating in &ratings {
            for &age in &ages {
                let reviews = inter / 2;
                
                let mut builder = AgentDataBuilder::new(&format!("did:test:combo-{}-{}", inter, rating))
                    .total_interactions(inter)
                    .created_at(Utc::now() - Duration::days(age));
                
                if reviews > 0 {
                    builder = builder.with_reviews(reviews, rating);
                }
                
                if let Ok(agent) = builder.build() {
                    // Should never panic
                    let _ = calc.calculate(&agent);
                }
            }
        }
    }
}