#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::needless_raw_string_hashes, clippy::duration_suboptimal_units, clippy::branches_sharing_code, clippy::used_underscore_binding, clippy::single_char_pattern, clippy::ignore_without_reason, clippy::cloned_ref_to_slice_refs, clippy::doc_overindented_list_items, clippy::match_wildcard_for_single_variants, clippy::ignored_unit_patterns, clippy::needless_collect, clippy::unnecessary_map_or, clippy::manual_flatten, clippy::manual_strip, clippy::future_not_send, clippy::unnested_or_patterns, clippy::no_effect_underscore_binding, clippy::literal_string_with_formatting_args)]
//! Security tests for input validation
use ggen_core::registry::SearchParams;
#[test]
fn test_xss_prevention_in_search() {
let malicious_query = "<script>alert('XSS')</script>";
let params = SearchParams {
query: malicious_query,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Query should be stored as-is (escaping happens at display time)
assert_eq!(params.query, malicious_query);
}
#[test]
fn test_sql_injection_prevention_in_search() {
let malicious_query = "'; DROP TABLE packages; --";
let params = SearchParams {
query: malicious_query,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Query should be stored safely
assert_eq!(params.query, malicious_query);
// Note: Actual SQL injection prevention happens at database layer
}
#[test]
fn test_path_traversal_prevention() {
let malicious_id = "../../../etc/passwd";
// Package ID should be validated/sanitized before file operations
// This test verifies we can handle such input without panicking
assert!(malicious_id.contains(".."));
}
#[test]
fn test_null_byte_injection() {
let malicious_query = "test\0hidden";
let params = SearchParams {
query: malicious_query,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should handle null bytes safely
assert!(params.query.contains('\0'));
}
#[test]
fn test_unicode_normalization() {
// Different Unicode representations of "é"
let query1 = "café"; // NFC form (single codepoint)
let query2 = "cafe\u{0301}"; // NFD form (base + combining accent)
let params1 = SearchParams {
query: query1,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
let params2 = SearchParams {
query: query2,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Both should be valid queries
assert!(!params1.query.is_empty());
assert!(!params2.query.is_empty());
// Note: Unicode normalization should happen during search
}
#[test]
fn test_extremely_long_input() {
let long_query = "a".repeat(10000);
let params = SearchParams {
query: &long_query,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should handle long input without panicking
assert_eq!(params.query.len(), 10000);
}
#[test]
fn test_special_regex_characters() {
let regex_chars = ".*+?^${}()|[]\\";
let params = SearchParams {
query: regex_chars,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should treat as literal string, not regex
assert_eq!(params.query, regex_chars);
}
#[test]
fn test_control_characters() {
let control_chars = "\t\n\r\x00\x1F";
let params = SearchParams {
query: control_chars,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should handle control characters safely
assert!(params.query.contains('\t'));
assert!(params.query.contains('\n'));
}
#[test]
fn test_mixed_scripts_unicode() {
// Mix of Latin, Cyrillic, Chinese, and Arabic
let mixed = "Hello Привет 你好 مرحبا";
let params = SearchParams {
query: mixed,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should handle mixed scripts safely
assert_eq!(params.query, mixed);
}
#[test]
fn test_homograph_attack_characters() {
// Cyrillic "а" (U+0430) looks like Latin "a" (U+0061)
let latin = "package";
let cyrillic = "pаckаge"; // Contains Cyrillic 'а'
// Should be treated as different strings
assert_ne!(latin, cyrillic);
}
#[test]
fn test_zero_width_characters() {
let with_zwsp = "pack\u{200B}age"; // Zero-width space
let normal = "package";
// Should be treated as different
assert_ne!(with_zwsp, normal);
}
#[test]
fn test_bidi_override_characters() {
// Right-to-left override can hide malicious content
let bidi_text = "package\u{202E}egakcap";
let params = SearchParams {
query: bidi_text,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should handle BiDi characters safely
assert!(params.query.contains('\u{202E}'));
}
#[test]
fn test_limit_boundaries() {
// Test extreme limit values
let params_zero = SearchParams {
query: "test",
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 0,
};
let params_max = SearchParams {
query: "test",
category: None,
keyword: None,
author: None,
stable_only: false,
limit: usize::MAX,
};
// Should not panic
assert_eq!(params_zero.limit, 0);
assert_eq!(params_max.limit, usize::MAX);
}
#[test]
fn test_command_injection_attempt() {
let malicious = "test; rm -rf /";
let params = SearchParams {
query: malicious,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should be treated as literal string
assert_eq!(params.query, malicious);
}
#[test]
fn test_format_string_injection() {
let malicious = "test %s %x %n";
let params = SearchParams {
query: malicious,
category: None,
keyword: None,
author: None,
stable_only: false,
limit: 10,
};
// Should be treated as literal string
assert_eq!(params.query, malicious);
}