linkrs-query 0.1.0

Query engine, parser, and executor for Linkrs
Documentation
//! Security DCL Tests
//!
//! Test security-related behaviors:
//! - SQL injection prevention
//! - Password handling in logs/output
//! - Invalid input handling

use super::common;
use common::test_scenario::TestScenario;

fn new_scenario() -> TestScenario {
    TestScenario::new().expect("Failed to create test scenario")
}

// ==================== SQL Injection Prevention Tests ====================

#[test]
fn test_sql_injection_in_username() {
    let scenario = new_scenario();

    // Attempt SQL injection in username
    let _ = scenario
        .exec_dcl("CREATE USER \"'; DROP USER --\" WITH PASSWORD 'pass'")
        .assert_success()
        // Username should be treated as literal, not executed
        .exec_dcl("DESCRIBE USER \"'; DROP USER --\"")
        .assert_success()
        // Original system should remain intact
        .exec_dcl("SHOW USERS")
        .assert_success();
}

#[test]
fn test_sql_injection_in_password() {
    let scenario = new_scenario();

    // Attempt SQL injection in password
    scenario
        .exec_dcl("CREATE USER injection_test WITH PASSWORD \"' OR '1'='1\"")
        .assert_success()
        // Password should be stored as-is, not executed
        .exec_dcl("DESCRIBE USER injection_test")
        .assert_success();
}

#[test]
fn test_sql_injection_in_space_name() {
    let scenario = new_scenario()
        .exec_dcl("CREATE USER inj_user WITH PASSWORD 'pass'")
        .assert_success();

    scenario
        // Injection in space name
        .exec_dcl("CREATE SPACE \"inject'; DROP SPACE --\"")
        .assert_success()
        // Should be treated as literal name
        .exec_dcl("GRANT ADMIN ON \"inject'; DROP SPACE --\" TO inj_user")
        .assert_success();
}

#[test]
fn test_special_chars_username_safety() {
    let scenarios = vec![
        (
            "user@domain",
            "CREATE USER user@domain WITH PASSWORD 'pass'",
        ),
        ("user:name", "CREATE USER user:name WITH PASSWORD 'pass'"),
        ("user/name", "CREATE USER user/name WITH PASSWORD 'pass'"),
        ("user\\name", "CREATE USER user\\name WITH PASSWORD 'pass'"),
        ("user%name", "CREATE USER user%name WITH PASSWORD 'pass'"),
    ];

    for (_username, query) in scenarios {
        let scenario = new_scenario();
        let result = scenario.exec_dcl(query);
        // Either succeeds or fails gracefully, but should not crash system
        drop(result);
    }
}

// ==================== Password Security Tests ====================

#[test]
fn test_password_not_in_describe_output() {
    let scenario = new_scenario()
        .exec_dcl("CREATE USER secure_user WITH PASSWORD 'SuperSecretPassword123!'")
        .assert_success();

    scenario
        // Describe user should not reveal password
        .exec_dcl("DESCRIBE USER secure_user")
        .assert_success();
    // Note: would need to check actual output to verify password is not included
    // This test documents the expected behavior
}

#[test]
fn test_password_not_in_show_users_output() {
    let scenario = new_scenario()
        .exec_dcl("CREATE USER visible_user WITH PASSWORD 'HiddenPassword456'")
        .assert_success();

    scenario
        // SHOW USERS should not reveal passwords
        .exec_dcl("SHOW USERS")
        .assert_success();
    // Note: would need to check actual output to verify passwords not included
}

#[test]
fn test_empty_password_not_allowed() {
    new_scenario()
        .exec_dcl("CREATE USER empty_pwd WITH PASSWORD ''")
        .assert_error();
}

#[test]
fn test_password_case_sensitive() {
    let scenario = new_scenario()
        .exec_dcl("CREATE USER case_user WITH PASSWORD 'MyPassword123'")
        .assert_success();

    scenario
        // Wrong case should fail
        .exec_dcl("CHANGE PASSWORD case_user 'mypassword123' TO 'NewPassword'")
        .assert_error();
}

// ==================== Input Validation Tests ====================

#[test]
fn test_empty_username_rejected() {
    new_scenario()
        .exec_dcl("CREATE USER '' WITH PASSWORD 'pass'")
        .assert_error();
}

#[test]
fn test_null_like_username() {
    new_scenario()
        .exec_dcl("CREATE USER NULL WITH PASSWORD 'pass'")
        .assert_success(); // NULL as literal string should work
}

#[test]
fn test_very_long_username() {
    let long_username = "a".repeat(10000);
    let query = format!("CREATE USER {} WITH PASSWORD 'pass'", long_username);

    new_scenario().exec_dcl(&query).assert_success(); // Should handle or reject gracefully
}

#[test]
fn test_very_long_password() {
    let long_password = "a".repeat(10000);
    let query = format!("CREATE USER longpwd WITH PASSWORD '{}'", long_password);

    new_scenario().exec_dcl(&query).assert_success(); // Should handle or reject gracefully
}

// ==================== Role Validation Tests ====================

#[test]
fn test_create_user_with_role_clause_rejected() {
    // Role assignment is space-scoped and only reachable through GRANT; the
    // CREATE USER role clause no longer exists in the grammar.
    new_scenario()
        .exec_dcl("CREATE USER user WITH PASSWORD 'pass' WITH ROLE SUPERUSER")
        .assert_error();
}

#[test]
fn test_invalid_role_in_grant() {
    let scenario = new_scenario()
        .exec_dcl("CREATE USER grantuser WITH PASSWORD 'pass'")
        .assert_success()
        .exec_dcl("CREATE SPACE grantspace")
        .assert_success();

    scenario
        .exec_dcl("GRANT INVALID_ROLE ON grantspace TO grantuser")
        .assert_error();
}

// ==================== Username/Space Existence Checks ====================

#[test]
fn test_grant_nonexistent_user_error() {
    new_scenario()
        .exec_dcl("CREATE SPACE safe_space")
        .assert_success()
        .exec_dcl("GRANT ADMIN ON safe_space TO nonexistent_user")
        .assert_error();
}

#[test]
fn test_grant_nonexistent_space_error() {
    new_scenario()
        .exec_dcl("CREATE USER safe_user WITH PASSWORD 'pass'")
        .assert_success()
        .exec_dcl("GRANT ADMIN ON nonexistent_space TO safe_user")
        .assert_error();
}

#[test]
fn test_alter_nonexistent_user_error() {
    new_scenario()
        .exec_dcl("ALTER USER nonexistent_alter WITH PASSWORD 'newpass'")
        .assert_error();
}

#[test]
fn test_drop_nonexistent_user_error() {
    new_scenario()
        .exec_dcl("DROP USER nonexistent_drop")
        .assert_error();
}

// ==================== State Validation After Invalid Operations ====================

#[test]
fn test_system_state_after_injection_attempt() {
    let scenario = new_scenario();

    // Attempt injection
    scenario
        .exec_dcl("CREATE USER normal_user WITH PASSWORD 'pass'")
        .assert_success()
        .exec_dcl("DROP USER \"normal_user'; DROP USER --\"")
        .assert_error()
        // Original user should still exist
        .exec_dcl("DESCRIBE USER normal_user")
        .assert_success();
}

#[test]
fn test_system_state_after_invalid_role_attempt() {
    let scenario = new_scenario()
        .exec_dcl("CREATE USER role_user WITH PASSWORD 'pass'")
        .assert_success()
        .exec_dcl("CREATE SPACE role_space")
        .assert_success()
        .exec_dcl("GRANT ADMIN ON role_space TO role_user")
        .assert_success();

    scenario
        // Attempt invalid role grant
        .exec_dcl("GRANT INVALID_ROLE ON role_space TO role_user")
        .assert_error()
        // Previous state should be intact
        .exec_dcl("DESCRIBE USER role_user")
        .assert_success()
        .exec_dcl("SHOW ROLES IN role_space")
        .assert_success();
}

// ==================== Unicode and Encoding Tests ====================

#[test]
fn test_unicode_username_security() {
    // Unicode identifiers are legitimate test data (not code comments).
    let scenario = new_scenario();

    scenario
        .exec_dcl("CREATE USER 中文用户 WITH PASSWORD '中文密码'")
        .assert_success()
        .exec_dcl("DESCRIBE USER 中文用户")
        .assert_success()
        .exec_dcl("CHANGE PASSWORD 中文用户 '中文密码' TO '新密码'")
        .assert_success();
}

#[test]
fn test_mixed_charset_username() {
    // Unicode identifiers are legitimate test data (not code comments).
    let scenario = new_scenario();

    scenario
        .exec_dcl("CREATE USER user_用户_123 WITH PASSWORD 'pass'")
        .assert_success()
        .exec_dcl("DESCRIBE USER user_用户_123")
        .assert_success();
}

// ==================== Boundary Condition Tests ====================

#[test]
fn test_single_char_username() {
    new_scenario()
        .exec_dcl("CREATE USER a WITH PASSWORD 'pass'")
        .assert_success()
        .exec_dcl("DROP USER a")
        .assert_success();
}

#[test]
fn test_single_char_password() {
    new_scenario()
        .exec_dcl("CREATE USER single_pwd WITH PASSWORD 'x'")
        .assert_success()
        .exec_dcl("DROP USER single_pwd")
        .assert_success();
}

#[test]
fn test_special_sql_keywords_as_username() {
    let keywords = vec!["SELECT", "DROP", "DELETE", "INSERT", "UPDATE"];

    for keyword in keywords {
        let query = format!("CREATE USER {} WITH PASSWORD 'pass'", keyword);
        let scenario = new_scenario();
        let result = scenario.exec_dcl(&query);
        // Should either succeed (treat as literal) or fail gracefully
        drop(result);
    }
}