diego 0.1.2

Pure Rust Active Directory security diagnostic agent. AS-REP Roasting, Kerberoasting, LDAP enumeration, OPSEC-friendly with Claude API analysis and MCP server mode.
Documentation
//! Integration tests: LDAP query structure validation

#[test]
fn test_windows_filetime_constants() {
    // Verify Windows FILETIME epoch constant
    let windows_epoch_delta = 116_444_736_000_000_000i64;

    // Should be a large positive number (difference between Windows and Unix epochs)
    assert!(windows_epoch_delta > 100_000_000_000_000_000i64);

    // Verify approximate timestamp: 2024-01-01 should be after Windows epoch
    let ts_2024 = (1704067200i64 * 10_000_000) + windows_epoch_delta;
    assert!(ts_2024 > windows_epoch_delta);
}

#[test]
fn test_description_leak_keywords() {
    // Verify all keyword variations are tested
    let keywords = vec!["pass", "pwd", "secret", "cred", "token", "key", "p@ss"];

    assert_eq!(keywords.len(), 7, "Should check 7 keywords");
    for kw in keywords {
        assert!(!kw.is_empty(), "Keyword should not be empty");
        assert!(kw.len() < 20, "Keyword should be reasonable length");
    }
}

#[test]
fn test_ldap_dn_format_validation() {
    // Verify DN format assumptions
    let dn = "CN=Domain Admins,CN=Users,DC=corp,DC=local";

    assert!(dn.contains("CN="), "DN should have CN components");
    assert!(dn.contains("DC="), "DN should have DC components");
    assert!(dn.contains(","), "DN should have commas separating components");

    // Count components
    let components: Vec<&str> = dn.split(',').collect();
    assert_eq!(components.len(), 4, "This DN should have 4 components");
}

#[test]
fn test_privileged_group_names() {
    // Verify well-known privileged groups
    let groups = vec![
        "Domain Admins",
        "Enterprise Admins",
        "Schema Admins",
        "Backup Operators",
        "Account Operators",
        "Print Operators",
        "Server Operators",
        "Group Policy Creator Owners",
    ];

    assert_eq!(groups.len(), 8, "Should have exactly 8 well-known groups");

    // Verify critical groups are present
    assert!(groups.contains(&"Domain Admins"), "Must include Domain Admins");
    assert!(groups.contains(&"Backup Operators"), "Must include Backup Operators");
    assert!(groups.contains(&"Schema Admins"), "Must include Schema Admins");
}

#[test]
fn test_ldap_oid_constants() {
    // Verify LDAP OID constants
    let bitwise_and_oid = "1.2.840.113556.1.4.803";     // Bitwise AND matching rule
    let in_chain_oid = "1.2.840.113556.1.4.1941";       // IN_CHAIN matching rule

    assert_ne!(bitwise_and_oid, in_chain_oid, "OIDs should be different");
    assert!(bitwise_and_oid.contains("."), "OID should have dot notation");
    assert!(in_chain_oid.contains("."), "OID should have dot notation");

    // Verify these are AD-specific OIDs (Microsoft allocated)
    assert!(bitwise_and_oid.starts_with("1.2.840.113556"), "Should be Microsoft OID");
    assert!(in_chain_oid.starts_with("1.2.840.113556"), "Should be Microsoft OID");
}

#[test]
fn test_uac_bit_flags() {
    // Verify userAccountControl bit flags
    let uac_dont_req_preauth = 0x400000;        // Bit 22
    let uac_disabled = 0x2;                     // Bit 1
    let uac_trusted_for_deleg = 0x80000;        // Bit 19
    let uac_trusted_to_auth = 0x100000;         // Bit 20

    // Verify no overlap
    assert_ne!(uac_dont_req_preauth, uac_disabled);
    assert_ne!(uac_trusted_for_deleg, uac_trusted_to_auth);

    // Verify decimal equivalents (as used in filters)
    assert_eq!(uac_dont_req_preauth, 4194304);
    assert_eq!(uac_trusted_for_deleg, 524288);
    assert_eq!(uac_trusted_to_auth, 1048576);
}

#[test]
fn test_ldap_attribute_names() {
    // Verify critical LDAP attribute names
    let attrs = vec![
        "sAMAccountName",
        "userAccountControl",
        "servicePrincipalName",
        "pwdLastSet",
        "distinguishedName",
        "msDS-AllowedToDelegateTo",
        "msDS-AllowedToActOnBehalfOfOtherIdentity",
        "msDS-SupportedEncryptionTypes",
        "description",
        "memberOf",
        "objectClass",
        "cn",
        "dnsHostName",
    ];

    // Verify no duplicates
    assert_eq!(attrs.len(), 13);
    let mut seen = std::collections::HashSet::new();
    for attr in &attrs {
        assert!(seen.insert(*attr), "Duplicate attribute: {}", attr);
    }
}

#[test]
fn test_ldap_filter_operators() {
    // Verify LDAP filter operator syntax
    let and_filter = "(&(a=1)(b=2))";
    let or_filter = "(|(a=1)(b=2))";
    let not_filter = "(!(a=1))";

    assert!(and_filter.starts_with("(&"), "AND filter should start with (&");
    assert!(or_filter.starts_with("(|"), "OR filter should start with (|");
    assert!(not_filter.starts_with("(!"), "NOT filter should start with (!");

    // Verify balanced parentheses
    fn balanced_parens(s: &str) -> bool {
        let mut count = 0;
        for c in s.chars() {
            match c {
                '(' => count += 1,
                ')' => count -= 1,
                _ => {}
            }
            if count < 0 {
                return false;
            }
        }
        count == 0
    }

    assert!(balanced_parens(and_filter));
    assert!(balanced_parens(or_filter));
    assert!(balanced_parens(not_filter));
}

#[test]
fn test_ldap_scope_values() {
    // Verify LDAP scope concept (not testing actual ldap3 enum)
    // Scope::Base → just the object itself
    // Scope::OneLevel → direct children
    // Scope::Subtree → recursive

    let scope_base = "base";
    let scope_one = "one";
    let scope_subtree = "subtree";

    assert_eq!(scope_base.len(), 4);
    assert_eq!(scope_one.len(), 3);
    assert_eq!(scope_subtree.len(), 7);
}