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 suffix = &uuid::Uuid::new_v4().simple().to_string()[..8];
35 let slur = decode("bmlnZ2Vy");
36 let offensive_handle = format!("{}{}", slur, suffix);
37
38 let create_payload = json!({
39 "handle": offensive_handle,
40 "email": format!("test{}@example.com", suffix),
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 suffix = &uuid::Uuid::new_v4().simple().to_string()[..6];
69 let slur = decode("bi1pLWctZy1lLXI=");
70 let offensive_handle = format!("{}{}", slur, suffix);
71
72 let create_payload = json!({
73 "handle": offensive_handle,
74 "email": format!("test{}@example.com", suffix),
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}