this repo has no description
1/* 2 * CONTENT WARNING 3 * 4 * This file contains explicit slurs and hateful language. We're sorry you have to see them. 5 * 6 * These words exist here for one reason: to ensure our moderation system correctly blocks them. 7 * We can't verify the filter catches the n-word without testing against the actual word. 8 * Euphemisms wouldn't prove the protection works. 9 * 10 * If reading this file has caused you distress, please know: 11 * - you are valued and welcome in this community 12 * - these words do not reflect the views of this project or its contributors 13 * - we maintain this code precisely because we believe everyone deserves an experience on the web that is free from this kinda language 14 */ 15 16mod common; 17mod helpers; 18use common::*; 19use helpers::*; 20use reqwest::StatusCode; 21use serde_json::json; 22 23fn decode(b64: &str) -> String { 24 use base64::Engine; 25 let bytes = base64::engine::general_purpose::STANDARD 26 .decode(b64) 27 .expect("invalid base64 in test"); 28 String::from_utf8(bytes).expect("invalid utf8 in test") 29} 30 31#[tokio::test] 32async fn test_handle_with_slur_rejected() { 33 let client = client(); 34 let timestamp = chrono::Utc::now().timestamp_millis(); 35 let slur = decode("bmlnZ2Vy"); 36 let offensive_handle = format!("{}{}", slur, timestamp); 37 38 let create_payload = json!({ 39 "handle": offensive_handle, 40 "email": format!("test{}@example.com", timestamp), 41 "password": "TestPassword123!" 42 }); 43 44 let res = client 45 .post(format!( 46 "{}/xrpc/com.atproto.server.createAccount", 47 base_url().await 48 )) 49 .json(&create_payload) 50 .send() 51 .await 52 .expect("Request failed"); 53 54 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 55 let body: serde_json::Value = res.json().await.unwrap(); 56 assert_eq!(body["error"], "InvalidHandle"); 57 assert!( 58 body["message"] 59 .as_str() 60 .unwrap_or("") 61 .contains("Inappropriate language") 62 ); 63} 64 65#[tokio::test] 66async fn test_handle_with_normalized_slur_rejected() { 67 let client = client(); 68 let timestamp = chrono::Utc::now().timestamp_millis(); 69 let slur = decode("bi1pLWctZy1lLXI="); 70 let offensive_handle = format!("{}{}", slur, timestamp); 71 72 let create_payload = json!({ 73 "handle": offensive_handle, 74 "email": format!("test{}@example.com", timestamp), 75 "password": "TestPassword123!" 76 }); 77 78 let res = client 79 .post(format!( 80 "{}/xrpc/com.atproto.server.createAccount", 81 base_url().await 82 )) 83 .json(&create_payload) 84 .send() 85 .await 86 .expect("Request failed"); 87 88 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 89 let body: serde_json::Value = res.json().await.unwrap(); 90 assert_eq!(body["error"], "InvalidHandle"); 91} 92 93#[tokio::test] 94async fn test_handle_update_with_slur_rejected() { 95 let client = client(); 96 let (_, jwt) = setup_new_user("handleupdate").await; 97 98 let update_payload = json!({ 99 "handle": decode("ZmFnZ290cw==") 100 }); 101 102 let res = client 103 .post(format!( 104 "{}/xrpc/com.atproto.identity.updateHandle", 105 base_url().await 106 )) 107 .bearer_auth(&jwt) 108 .json(&update_payload) 109 .send() 110 .await 111 .expect("Request failed"); 112 113 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 114 let body: serde_json::Value = res.json().await.unwrap(); 115 assert_eq!(body["error"], "InvalidHandle"); 116} 117 118#[tokio::test] 119async fn test_profile_displayname_with_slur_rejected() { 120 let client = client(); 121 let (did, jwt) = setup_new_user("profileslur").await; 122 123 let profile = json!({ 124 "repo": did, 125 "collection": "app.bsky.actor.profile", 126 "rkey": "self", 127 "record": { 128 "$type": "app.bsky.actor.profile", 129 "displayName": decode("SSBhbSBhIGtpa2U=") 130 } 131 }); 132 133 let res = client 134 .post(format!( 135 "{}/xrpc/com.atproto.repo.putRecord", 136 base_url().await 137 )) 138 .bearer_auth(&jwt) 139 .json(&profile) 140 .send() 141 .await 142 .expect("Request failed"); 143 144 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 145 let body: serde_json::Value = res.json().await.unwrap(); 146 assert_eq!(body["error"], "InvalidRecord"); 147} 148 149#[tokio::test] 150async fn test_profile_description_with_slur_rejected() { 151 let client = client(); 152 let (did, jwt) = setup_new_user("profiledesc").await; 153 154 let profile = json!({ 155 "repo": did, 156 "collection": "app.bsky.actor.profile", 157 "rkey": "self", 158 "record": { 159 "$type": "app.bsky.actor.profile", 160 "displayName": "Normal Name", 161 "description": decode("SSBoYXRlIGFsbCBjaGlua3M=") 162 } 163 }); 164 165 let res = client 166 .post(format!( 167 "{}/xrpc/com.atproto.repo.putRecord", 168 base_url().await 169 )) 170 .bearer_auth(&jwt) 171 .json(&profile) 172 .send() 173 .await 174 .expect("Request failed"); 175 176 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 177 let body: serde_json::Value = res.json().await.unwrap(); 178 assert_eq!(body["error"], "InvalidRecord"); 179} 180 181#[tokio::test] 182async fn test_clean_content_allowed() { 183 let client = client(); 184 let (did, jwt) = setup_new_user("cleanpost").await; 185 186 let post = json!({ 187 "repo": did, 188 "collection": "app.bsky.feed.post", 189 "record": { 190 "$type": "app.bsky.feed.post", 191 "text": "This is a perfectly normal post about coding and technology!", 192 "createdAt": chrono::Utc::now().to_rfc3339() 193 } 194 }); 195 196 let res = client 197 .post(format!( 198 "{}/xrpc/com.atproto.repo.createRecord", 199 base_url().await 200 )) 201 .bearer_auth(&jwt) 202 .json(&post) 203 .send() 204 .await 205 .expect("Request failed"); 206 207 assert_eq!(res.status(), StatusCode::OK); 208}