update_kit/utils/
security.rs1use crate::errors::UpdateKitError;
2
3pub fn require_https(url: &str) -> Result<(), UpdateKitError> {
7 if !url.starts_with("https://") {
8 return Err(UpdateKitError::InsecureUrl(format!(
9 "Only HTTPS URLs are allowed. Got: {url}"
10 )));
11 }
12 Ok(())
13}
14
15pub fn timing_safe_equal(a: &str, b: &str) -> bool {
20 let a_lower = a.to_ascii_lowercase();
21 let b_lower = b.to_ascii_lowercase();
22
23 if a_lower.len() != b_lower.len() {
24 return false;
25 }
26
27 let result = a_lower
28 .as_bytes()
29 .iter()
30 .zip(b_lower.as_bytes().iter())
31 .fold(0u8, |acc, (x, y)| acc | (x ^ y));
32
33 result == 0
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn require_https_accepts_https() {
42 assert!(require_https("https://example.com").is_ok());
43 assert!(require_https("https://example.com/path?q=1").is_ok());
44 }
45
46 #[test]
47 fn require_https_rejects_http() {
48 let result = require_https("http://example.com");
49 assert!(result.is_err());
50 let err = result.unwrap_err();
51 assert!(
52 matches!(err, UpdateKitError::InsecureUrl(_)),
53 "Expected InsecureUrl, got: {err:?}"
54 );
55 }
56
57 #[test]
58 fn require_https_rejects_other_schemes() {
59 assert!(require_https("ftp://example.com").is_err());
60 assert!(require_https("file:///etc/passwd").is_err());
61 assert!(require_https("").is_err());
62 }
63
64 #[test]
65 fn timing_safe_equal_matches_same() {
66 assert!(timing_safe_equal("abc123", "abc123"));
67 }
68
69 #[test]
70 fn timing_safe_equal_matches_case_insensitively() {
71 assert!(timing_safe_equal("ABC123", "abc123"));
72 assert!(timing_safe_equal("AbCdEf", "abcdef"));
73 }
74
75 #[test]
76 fn timing_safe_equal_rejects_different_strings() {
77 assert!(!timing_safe_equal("abc123", "abc124"));
78 assert!(!timing_safe_equal("abc123", "xyz789"));
79 }
80
81 #[test]
82 fn timing_safe_equal_rejects_different_lengths() {
83 assert!(!timing_safe_equal("abc", "abcd"));
84 assert!(!timing_safe_equal("", "a"));
85 }
86
87 #[test]
88 fn timing_safe_equal_empty_strings() {
89 assert!(timing_safe_equal("", ""));
90 }
91}