Merge pull request #319 from cloudflare/sven/ignore-invalid-mentions

ignore invalid user mentions
pull/318/head
Sven Sauleau 2023-02-20 12:25:21 +00:00 zatwierdzone przez GitHub
commit e464d6f988
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 61 dodań i 26 usunięć

Wyświetl plik

@ -1,4 +1,6 @@
import { parseHandle } from 'wildebeest/backend/src/utils/parse'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
function tag(name: string, content: string, attrs: Record<string, string> = {}): string {
let htmlAttrs = ''
@ -13,18 +15,24 @@ const linkRegex = /(^|\s|\b)(https?:\/\/[-\w@:%._+~#=]{2,256}\.[a-z]{2,6}\b(?:[-
const mentionedEmailRegex = /(^|\s|\b|\W)@(\w+(?:[.-]?\w+)+@\w+(?:[.-]?\w+)+(?:\.\w{2,63})+)(\b|\s|$)/g
/// Transform a text status into a HTML status; enriching it with links / mentions.
export function enrichStatus(status: string): string {
export function enrichStatus(status: string, mentions: Array<Actor>): string {
const enrichedStatus = status
.replace(
linkRegex,
(_, matchPrefix: string, link: string, matchSuffix: string) =>
`${matchPrefix}${getLinkAnchor(link)}${matchSuffix}`
)
.replace(
mentionedEmailRegex,
(_, matchPrefix: string, email: string, matchSuffix: string) =>
`${matchPrefix}${getMentionSpan(email)}${matchSuffix}`
)
.replace(mentionedEmailRegex, (_, matchPrefix: string, email: string, matchSuffix: string) => {
// ensure that the match is part of the mentions array
for (let i = 0, len = mentions.length; i < len; i++) {
if (email === urlToHandle(mentions[i].id)) {
return `${matchPrefix}${getMentionSpan(email)}${matchSuffix}`
}
}
// otherwise the match isn't valid and we don't add HTML
return `${matchPrefix}${email}${matchSuffix}`
})
return tag('p', enrichedStatus)
}

Wyświetl plik

@ -21,20 +21,21 @@ export async function queryAcct(domain: string, acct: string): Promise<Actor | n
export async function queryAcctLink(domain: string, acct: string): Promise<URL | null> {
const params = new URLSearchParams({ resource: `acct:${acct}` })
let res
let data: WebFingerResponse
try {
const url = new URL('/.well-known/webfinger?' + params, 'https://' + domain)
console.log('query', url.href)
res = await fetch(url, { headers })
const res = await fetch(url, { headers })
if (!res.ok) {
throw new Error(`WebFinger API returned: ${res.status}`)
}
data = await res.json<WebFingerResponse>()
} catch (err) {
console.warn('failed to query WebFinger:', err)
return null
}
const data = await res.json<WebFingerResponse>()
for (let i = 0, len = data.links.length; i < len; i++) {
const link = data.links[i]
if (link.rel === 'self' && link.type === 'application/activity+json') {

Wyświetl plik

@ -167,7 +167,7 @@ describe('Mastodon APIs', () => {
})
describe('Microformats', () => {
test('convert mentions to HTML', () => {
test('convert mentions to HTML', async () => {
const mentionsToTest = [
{
mention: '@sven2@example.com',
@ -195,18 +195,34 @@ describe('Mastodon APIs', () => {
'<span class="h-card"><a href="https://123456.test.testey.abcdef/@testey" class="u-url mention">@<span>testey</span></a></span>',
},
]
mentionsToTest.forEach(({ mention, expectedMentionSpan }) => {
assert.equal(enrichStatus(`hey ${mention} hi`), `<p>hey ${expectedMentionSpan} hi</p>`)
assert.equal(enrichStatus(`${mention} hi`), `<p>${expectedMentionSpan} hi</p>`)
assert.equal(enrichStatus(`${mention}\n\thein`), `<p>${expectedMentionSpan}\n\thein</p>`)
assert.equal(enrichStatus(`hey ${mention}`), `<p>hey ${expectedMentionSpan}</p>`)
assert.equal(enrichStatus(`${mention}`), `<p>${expectedMentionSpan}</p>`)
assert.equal(enrichStatus(`@!@£${mention}!!!`), `<p>@!@£${expectedMentionSpan}!!!</p>`)
})
for (let i = 0, len = mentionsToTest.length; i < len; i++) {
const { mention, expectedMentionSpan } = mentionsToTest[i]
// List of mentioned actors, only the `id` is required so we can hack together an Actor
const mentions: any = [
{ id: new URL('https://example.com/sven2') },
{ id: new URL('https://example.eng.com/test') },
{ id: new URL('https://example.eng.co.uk/test.a.b.c-d') },
{ id: new URL('https://123456.abcdef/testey') },
{ id: new URL('https://123456.test.testey.abcdef/testey') },
]
assert.equal(enrichStatus(`hey ${mention} hi`, mentions), `<p>hey ${expectedMentionSpan} hi</p>`)
assert.equal(enrichStatus(`${mention} hi`, mentions), `<p>${expectedMentionSpan} hi</p>`)
assert.equal(enrichStatus(`${mention}\n\thein`, mentions), `<p>${expectedMentionSpan}\n\thein</p>`)
assert.equal(enrichStatus(`hey ${mention}`, mentions), `<p>hey ${expectedMentionSpan}</p>`)
assert.equal(enrichStatus(`${mention}`, mentions), `<p>${expectedMentionSpan}</p>`)
assert.equal(enrichStatus(`@!@£${mention}!!!`, mentions), `<p>@!@£${expectedMentionSpan}!!!</p>`)
}
})
test('handle invalid mention', () => {
assert.equal(enrichStatus('hey @#-...@example.com'), '<p>hey @#-...@example.com</p>')
assert.equal(enrichStatus('hey @#-...@example.com', []), '<p>hey @#-...@example.com</p>')
})
test('mention to invalid user', () => {
assert.equal(enrichStatus('hey test@example.com', []), '<p>hey test@example.com</p>')
})
test('convert links to HTML', () => {
@ -222,11 +238,11 @@ describe('Mastodon APIs', () => {
linksToTest.forEach((link) => {
const url = new URL(link)
const urlDisplayText = `${url.hostname}${url.pathname}`
assert.equal(enrichStatus(`hey ${link} hi`), `<p>hey <a href="${link}">${urlDisplayText}</a> hi</p>`)
assert.equal(enrichStatus(`${link} hi`), `<p><a href="${link}">${urlDisplayText}</a> hi</p>`)
assert.equal(enrichStatus(`hey ${link}`), `<p>hey <a href="${link}">${urlDisplayText}</a></p>`)
assert.equal(enrichStatus(`${link}`), `<p><a href="${link}">${urlDisplayText}</a></p>`)
assert.equal(enrichStatus(`@!@£${link}!!!`), `<p>@!@£<a href="${link}">${urlDisplayText}</a>!!!</p>`)
assert.equal(enrichStatus(`hey ${link} hi`, []), `<p>hey <a href="${link}">${urlDisplayText}</a> hi</p>`)
assert.equal(enrichStatus(`${link} hi`, []), `<p><a href="${link}">${urlDisplayText}</a> hi</p>`)
assert.equal(enrichStatus(`hey ${link}`, []), `<p>hey <a href="${link}">${urlDisplayText}</a></p>`)
assert.equal(enrichStatus(`${link}`, []), `<p><a href="${link}">${urlDisplayText}</a></p>`)
assert.equal(enrichStatus(`@!@£${link}!!!`, []), `<p>@!@£<a href="${link}">${urlDisplayText}</a>!!!</p>`)
})
})
})

Wyświetl plik

@ -457,6 +457,11 @@ describe('Mastodon APIs', () => {
})
)
}
if (
input.toString() === 'https://cloudflare.com/.well-known/webfinger?resource=acct%3Ano-json%40cloudflare.com'
) {
return new Response('not json', { status: 200 })
}
if (input.toString() === 'https://instance.horse/users/sven') {
return new Response(
@ -496,7 +501,7 @@ describe('Mastodon APIs', () => {
}
{
const mentions = await getMentions('unknown@actor.com', domain)
const mentions = await getMentions('no-json@actor.com', domain)
assert.equal(mentions.length, 0)
}
@ -524,6 +529,11 @@ describe('Mastodon APIs', () => {
assert.equal(mentions.length, 1)
assert.equal(mentions[0].id.toString(), 'https://' + domain + '/users/sven')
}
{
const mentions = await getMentions('<p>@unknown</p>', domain)
assert.equal(mentions.length, 0)
}
})
test('get status count likes', async () => {

Wyświetl plik

@ -107,12 +107,12 @@ export async function handleRequest(
const hashtags = getHashtags(body.status)
const content = enrichStatus(body.status)
const mentions = await getMentions(body.status, domain)
if (mentions.length > 0) {
extraProperties.tag = mentions.map(newMention)
}
const content = enrichStatus(body.status, mentions)
const note = await createStatus(domain, db, connectedActor, content, mediaAttachments, extraProperties)
if (hashtags.length > 0) {