Skip to content

Commit

Permalink
chore: check for spam before creating user / drive file
Browse files Browse the repository at this point in the history
  • Loading branch information
anatawa12 committed Nov 4, 2024
1 parent 06489cc commit d6cd53f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 4 deletions.
7 changes: 4 additions & 3 deletions packages/backend/src/core/activitypub/ApAudienceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class ApAudienceService {
}

@bindThis
public async parseAudience(actor: MiRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
public async parseAudience(actor: MiRemoteUser | null, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
const toGroups = this.groupingAudience(getApIds(to), actor);
const ccGroups = this.groupingAudience(getApIds(cc), actor);

Expand Down Expand Up @@ -74,7 +74,7 @@ export class ApAudienceService {
}

@bindThis
private groupingAudience(ids: string[], actor: MiRemoteUser): GroupedAudience {
private groupingAudience(ids: string[], actor: MiRemoteUser | null): GroupedAudience {
const groups: GroupedAudience = {
public: [],
followers: [],
Expand Down Expand Up @@ -106,7 +106,8 @@ export class ApAudienceService {
}

@bindThis
private isFollowers(id: string, actor: MiRemoteUser): boolean {
private isFollowers(id: string, actor: MiRemoteUser | null): boolean {
if (actor == null) return false;
return id === (actor.followersUri ?? `${actor.uri}/followers`);
}
}
41 changes: 40 additions & 1 deletion packages/backend/src/core/activitypub/models/ApNoteService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
import { checkHttps } from '@/misc/check-https.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { SpamFilterService } from '@/core/SpamFilterService.js';
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
import { ApLoggerService } from '../ApLoggerService.js';
import { ApMfmService } from '../ApMfmService.js';
Expand Down Expand Up @@ -72,6 +73,7 @@ export class ApNoteService {
private noteCreateService: NoteCreateService,
private apDbResolverService: ApDbResolverService,
private apLoggerService: ApLoggerService,
private spamFilterService: SpamFilterService,
) {
this.logger = this.apLoggerService.logger;
}
Expand Down Expand Up @@ -156,7 +158,7 @@ export class ApNoteService {
const uri = getOneApId(note.attributedTo);

// ローカルで投稿者を検索し、もし凍結されていたらスキップ
const cachedActor = await this.apPersonService.fetchPerson(uri) as MiRemoteUser;
const cachedActor = await this.apPersonService.fetchPerson(uri) as MiRemoteUser | null;
if (cachedActor && cachedActor.isSuspended) {
throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended');
}
Expand Down Expand Up @@ -189,6 +191,43 @@ export class ApNoteService {
}
//#endregion

// spam check
{
// fetch audience information.
// this logic may treat followers as direct audience, but it's not a problem for spam check.
const noteAudience = await this.apAudienceService.parseAudience(cachedActor, note.to, note.cc, resolver);
let visibility = noteAudience.visibility;
const visibleUsers = noteAudience.visibleUsers;

// Audience (to, cc) が指定されてなかった場合
if (visibility === 'specified' && visibleUsers.length === 0) {
if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している
// こちらから匿名GET出来たものならばpublic
visibility = 'public';
}
}

// reply
const reply: MiNote | null = note.inReplyTo ? await this.fetchNote(getApId(note.inReplyTo)) : null;

// 引用
const quoteUris = unique([note._misskey_quote, note.quoteUrl].filter(x => x != null));
const quoteFetchResults = await Promise.all(quoteUris.map(uri => this.fetchNote(getApId(uri))));
const quote = quoteFetchResults.filter((x) => x != null).at(0) ?? null;

if (await this.spamFilterService.isSpam({
mentionedUsers: apMentions,
visibility,
visibleUsers,
reply: reply,
quote: quote,
user: cachedActor,
})) {
this.logger.error('Request rejected because user has no local followers', { user: uri });
throw new IdentifiableError('e11b3a16-f543-4885-8eb1-66cad131dbfd', 'Notes including mentions, replies, or renotes from remote users are not allowed until user has at least one local follower.');
}
}

const actor = cachedActor ?? await this.apPersonService.resolvePerson(uri, resolver) as MiRemoteUser;

// 解決した投稿者が凍結されていたらスキップ
Expand Down

0 comments on commit d6cd53f

Please sign in to comment.