Skip to content

Commit 0aa3bd4

Browse files
committed
Return 451 for legally blocked wiki repos
1 parent bfa78ab commit 0aa3bd4

5 files changed

Lines changed: 126 additions & 8 deletions

File tree

cfworkers/ghwsee-indexable-redirect/src/handler.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,31 @@ interface OriginalInfo {
44
moved_to?: string
55
}
66

7+
const LEGAL_BLOCK_REPOS = new Set(['mms75/sfz'])
8+
9+
function repoSlugFromPathComponents(
10+
pathComponents: string[],
11+
): string | undefined {
12+
if (pathComponents.length < 3) {
13+
return undefined
14+
}
15+
16+
return `${pathComponents[1]}/${pathComponents[2]}`.toLowerCase()
17+
}
18+
19+
function legalBlockResponse(repoSlug: string): Response {
20+
return new Response(
21+
`451 Unavailable For Legal Reasons - ${repoSlug} is unavailable on this service.`,
22+
{
23+
status: 451,
24+
statusText: 'Unavailable For Legal Reasons',
25+
headers: {
26+
'content-type': 'text/plain; charset=utf-8',
27+
},
28+
},
29+
)
30+
}
31+
732
class ModifiedDateAppender implements HTMLRewriterElementContentHandlers {
833
date: Date
934

@@ -23,15 +48,19 @@ export async function handleRequest(request: Request): Promise<Response> {
2348
request.url.replace('github-wiki-see.page/m', 'github.com'),
2449
)
2550

51+
const pathComponents = githubUrl.pathname.split('/')
52+
const repoSlug = repoSlugFromPathComponents(pathComponents)
53+
if (repoSlug && LEGAL_BLOCK_REPOS.has(repoSlug)) {
54+
return legalBlockResponse(repoSlug)
55+
}
56+
2657
const ghwseeResponse = fetch(request, {
2758
cf: {
2859
cacheEverything: true,
2960
cacheTtl: 7200,
3061
},
3162
})
3263

33-
const pathComponents = githubUrl.pathname.split('/')
34-
3564
// Don't redirect wiki_index path. Index that, even for indexable wikis.
3665
if (pathComponents.length > 3 && pathComponents[2] === 'wiki_index') {
3766
return await ghwseeResponse

cfworkers/ghwsee-indexable-redirect/test/handler.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ describe('handle', () => {
2020
expect(result.headers.has('Last-Modified')).toBeFalsy()
2121
})
2222

23+
test('returns 451 for a legally blocked repo', async () => {
24+
const request_url = `https://github-wiki-see.page/m/mms75/sfz/wiki`
25+
const result = await handleRequest(
26+
new Request(request_url, { method: 'GET' }),
27+
)
28+
29+
expect(result.status).toEqual(451)
30+
expect(await result.text()).toContain('mms75/sfz')
31+
})
32+
2333
test('can determine if a URL is not indexable', async () => {
2434
const url = new URL('https://github.com/commaai/openpilot/wiki')
2535
expect(await (await originalInfo(url)).indexable).toBeFalsy()
@@ -110,6 +120,5 @@ describe('handle', () => {
110120
expect(result.headers.get('location')).toEqual(
111121
'https://github-wiki-see.page/m/node-config/node-config/wiki',
112122
)
113-
114123
})
115124
})

src/legal_block.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use phf::phf_set;
2+
3+
pub static LEGAL_BLOCK_LIST: phf::Set<&'static str> = phf_set! {
4+
// Cloudflare Trust & Safety report 23d89ee107ed0942.
5+
"mms75/sfz",
6+
};
7+
8+
#[cfg(test)]
9+
mod tests {
10+
#[test]
11+
fn test_contain_phf() {
12+
let repo = "mms75/sfz".to_string();
13+
assert!(super::LEGAL_BLOCK_LIST.contains(repo.as_str()));
14+
}
15+
}

src/main.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::scraper::process_markdown;
1616

1717
mod decommission;
1818
mod gh_extensions;
19+
mod legal_block;
1920
mod retrieval;
2021
mod scraper;
2122

@@ -152,7 +153,16 @@ async fn wiki_debug_sitemaps(
152153
) -> Result<content::RawXml<String>, status::Custom<String>> {
153154
let content = retrieve_wiki_sitemap_index(account, repository, client)
154155
.await
155-
.map_err(|error| status::Custom(Status::InternalServerError, format!("Error: {error}")))?;
156+
.map_err(|error| {
157+
let status = match error {
158+
retrieval::ContentError::UnavailableForLegalReasons => {
159+
Status::UnavailableForLegalReasons
160+
}
161+
_ => Status::InternalServerError,
162+
};
163+
164+
status::Custom(status, format!("Error: {error}"))
165+
})?;
156166

157167
Ok(content::RawXml(content))
158168
}
@@ -171,6 +181,7 @@ struct MirrorTemplate {
171181
enum MirrorError {
172182
// DocumentNotFound(NotFound<MirrorTemplate>),
173183
InternalError(status::Custom<content::RawHtml<String>>),
184+
UnavailableForLegalReasons(status::Custom<content::RawHtml<String>>),
174185
GiveUpSendToGitHub(Redirect),
175186
}
176187

@@ -186,6 +197,15 @@ fn mirror_internal_error(template: MirrorTemplate) -> MirrorError {
186197
MirrorError::InternalError(status::Custom(Status::InternalServerError, rendered))
187198
}
188199

200+
fn mirror_unavailable_for_legal_reasons(account: &str, repository: &str) -> MirrorError {
201+
MirrorError::UnavailableForLegalReasons(status::Custom(
202+
Status::UnavailableForLegalReasons,
203+
content::RawHtml(format!(
204+
"451 Unavailable For Legal Reasons - {account}/{repository} is unavailable on this service."
205+
)),
206+
))
207+
}
208+
189209
#[get("/<account>/<repository>/wiki")]
190210
async fn mirror_home(
191211
account: &str,
@@ -290,6 +310,9 @@ async fn mirror_page(
290310
ContentError::Decommissioned => {
291311
GiveUpSendToGitHub(Redirect::permanent(original_url_encoded.clone()))
292312
}
313+
ContentError::UnavailableForLegalReasons => {
314+
mirror_unavailable_for_legal_reasons(account, repository)
315+
}
293316
ContentError::OtherError(e) => mirror_internal_error(MirrorTemplate {
294317
original_title: page_title.clone(),
295318
original_url: original_url.clone(),
@@ -361,6 +384,9 @@ async fn mirror_page_index(
361384
ContentError::Decommissioned => {
362385
GiveUpSendToGitHub(Redirect::permanent(original_url.clone()))
363386
}
387+
ContentError::UnavailableForLegalReasons => {
388+
mirror_unavailable_for_legal_reasons(account, repository)
389+
}
364390
ContentError::OtherError(e) => mirror_internal_error(MirrorTemplate {
365391
original_title: page_title.clone(),
366392
original_url: original_url.clone(),

src/retrieval.rs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::sync::LazyLock;
66
use thiserror::Error;
77

88
use crate::decommission::DECOMMISSION_LIST;
9+
use crate::legal_block::LEGAL_BLOCK_LIST;
910
use crate::scraper::process_html_index;
1011

1112
#[allow(dead_code)]
@@ -31,6 +32,8 @@ pub enum ContentError {
3132
TooMayRequests,
3233
#[error("wiki has been decommissioned")]
3334
Decommissioned,
35+
#[error("unavailable for legal reasons")]
36+
UnavailableForLegalReasons,
3437
#[error("{0}")]
3538
OtherError(String),
3639
}
@@ -45,6 +48,19 @@ fn repo_slug(account: &str, repository: &str) -> String {
4548
format!("{account}/{repository}")
4649
}
4750

51+
fn reject_blocked_repo(account: &str, repository: &str) -> Result<(), ContentError> {
52+
let repo = repo_slug(account, repository);
53+
if LEGAL_BLOCK_LIST.contains(repo.as_str()) {
54+
return Err(ContentError::UnavailableForLegalReasons);
55+
}
56+
57+
if DECOMMISSION_LIST.contains(repo.as_str()) {
58+
return Err(ContentError::Decommissioned);
59+
}
60+
61+
Ok(())
62+
}
63+
4864
fn raw_wiki_source_url(account: &str, repository: &str, page: &str, extension: &str) -> String {
4965
let page_encoded =
5066
percent_encoding::utf8_percent_encode(page, percent_encoding::NON_ALPHANUMERIC);
@@ -93,10 +109,7 @@ pub async fn retrieve_source_file(
93109
page: &str,
94110
client: &Client,
95111
) -> Result<Content, ContentError> {
96-
// Skip decommissioned wikis
97-
if DECOMMISSION_LIST.contains(repo_slug(account, repository).as_str()) {
98-
return Err(ContentError::Decommissioned);
99-
}
112+
reject_blocked_repo(account, repository)?;
100113

101114
match retrieve_source_file_extension(account, repository, page, client, Content::Markdown, "md")
102115
.await
@@ -183,6 +196,8 @@ pub async fn retrieve_wiki_index(
183196
repository: &str,
184197
client: &Client,
185198
) -> Result<Content, ContentError> {
199+
reject_blocked_repo(account, repository)?;
200+
186201
let html = with_rate_limit_fallback(|domain| async move {
187202
retrieve_github_com_html(account, repository, "", client, domain).await
188203
})
@@ -208,6 +223,8 @@ pub async fn retrieve_wiki_sitemap_index(
208223
repository: &str,
209224
client: &Client,
210225
) -> Result<String, ContentError> {
226+
reject_blocked_repo(account, repository)?;
227+
211228
let html = with_rate_limit_fallback(|domain| async move {
212229
retrieve_github_com_html(account, repository, "", client, domain).await
213230
})
@@ -368,6 +385,28 @@ mod tests {
368385
assert!(content.is_ok());
369386
}
370387

388+
#[tokio::test]
389+
async fn legal_block_source_file() {
390+
let client = Client::new();
391+
let content = retrieve_source_file("mms75", "sfz", "Home", &client).await;
392+
393+
assert!(matches!(
394+
content,
395+
Err(ContentError::UnavailableForLegalReasons)
396+
));
397+
}
398+
399+
#[tokio::test]
400+
async fn legal_block_wiki_index() {
401+
let client = Client::new();
402+
let content = retrieve_wiki_index("mms75", "sfz", &client).await;
403+
404+
assert!(matches!(
405+
content,
406+
Err(ContentError::UnavailableForLegalReasons)
407+
));
408+
}
409+
371410
#[tokio::test]
372411
async fn wiki_sitemap_index() {
373412
let client = Client::new();

0 commit comments

Comments
 (0)