Skip to content

Commit

Permalink
enhance: 禁止ワードチェック強化 (#27)
Browse files Browse the repository at this point in the history
* enhance: 禁止ワードチェック強化
* リモートの禁止ワードチェックを添付ファイルとユーザーを登録する前に行うなど
  Resolve misskey-dev#13374
* 禁止ワートの対象の見直し

* performActivityで特定のエラーが出た際にDelayedに追加しないように

* use IdentifiableError

* NoteCreateService.checkProhibitedWords

* https://github.com/misskey-dev/misskey-private/pull/27/files#r1507416135

* remove comment
  • Loading branch information
tamaina authored and tai-cha committed Mar 1, 2024
1 parent d1bf432 commit eb60460
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 24 deletions.
25 changes: 24 additions & 1 deletion packages/backend/src/core/NoteCreateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,13 @@ export class NoteCreateService implements OnApplicationShutdown {
}
}

if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
const hasProhibitedWords = await this.checkProhibitedWordsContain({
cw: data.cw,
text: data.text,
pollChoices: data.poll?.choices,
}, meta.prohibitedWords);

if (hasProhibitedWords) {
throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
}

Expand Down Expand Up @@ -995,6 +1001,23 @@ export class NoteCreateService implements OnApplicationShutdown {
}
}

public async checkProhibitedWordsContain(content: Parameters<UtilityService['concatNoteContentsForKeyWordCheck']>[0], prohibitedWords?: string[]) {
if (prohibitedWords == null) {
prohibitedWords = (await this.metaService.fetch()).prohibitedWords;
}

if (
this.utilityService.isKeyWordIncluded(
this.utilityService.concatNoteContentsForKeyWordCheck(content),
prohibitedWords,
)
) {
return true;
}

return false;
}

@bindThis
public dispose(): void {
this.#shutdownController.abort();
Expand Down
14 changes: 14 additions & 0 deletions packages/backend/src/core/UtilityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ export class UtilityService {
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
}

@bindThis
public concatNoteContentsForKeyWordCheck(content: {
cw?: string | null;
text?: string | null;
pollChoices?: string[] | null;
others?: string[] | null;
}): string {
/**
* ノートの内容を結合してキーワードチェック用の文字列を生成する
* cwとtextは内容が繋がっているかもしれないので間に何も入れずにチェックする
*/
return `${content.cw ?? ''}${content.text ?? ''}\n${(content.pollChoices ?? []).join('\n')}\n${(content.others ?? []).join('\n')}`;
}

@bindThis
public isKeyWordIncluded(text: string, keyWords: string[]): boolean {
if (keyWords.length === 0) return false;
Expand Down
1 change: 0 additions & 1 deletion packages/backend/src/core/activitypub/ApInboxService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import { ApResolverService } from './ApResolverService.js';
import { ApAudienceService } from './ApAudienceService.js';
import { ApPersonService } from './models/ApPersonService.js';
import { ApQuestionService } from './models/ApQuestionService.js';
import { CacheService } from '@/core/CacheService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { Resolver } from './ApResolverService.js';
import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js';
Expand Down
62 changes: 41 additions & 21 deletions packages/backend/src/core/activitypub/models/ApNoteService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { StatusError } from '@/misc/status-error.js';
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 { isNotNull } from '@/misc/is-not-null.js';
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
import { ApLoggerService } from '../ApLoggerService.js';
import { ApMfmService } from '../ApMfmService.js';
Expand All @@ -37,7 +39,6 @@ import { ApQuestionService } from './ApQuestionService.js';
import { ApImageService } from './ApImageService.js';
import type { Resolver } from '../ApResolverService.js';
import type { IObject, IPost } from '../type.js';
import { isNotNull } from '@/misc/is-not-null.js';

@Injectable()
export class ApNoteService {
Expand Down Expand Up @@ -152,11 +153,47 @@ export class ApNoteService {
throw new Error('invalid note.attributedTo: ' + note.attributedTo);
}

const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser;
const uri = getOneApId(note.attributedTo);

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

const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
const apHashtags = extractApHashtags(note.tag);

const cw = note.summary === '' ? null : note.summary;

// テキストのパース
let text: string | null = null;
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
text = note.source.content;
} else if (typeof note._misskey_content !== 'undefined') {
text = note._misskey_content;
} else if (typeof note.content === 'string') {
text = this.apMfmService.htmlToMfm(note.content, note.tag);
}

const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);

// 投稿者が凍結されていたらスキップ
//#region Contents Check
// 添付ファイルとユーザーをこのサーバーで登録する前に内容をチェックする
/**
* 禁止ワードチェック
*/
const hasProhibitedWords = await this.noteCreateService.checkProhibitedWordsContain({ cw, text, pollChoices: poll?.choices });
if (hasProhibitedWords) {
throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
}
//#endregion

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

// 解決した投稿者が凍結されていたらスキップ
if (actor.isSuspended) {
throw new Error('actor has been suspended');
throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended');
}

const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver);
Expand All @@ -171,9 +208,6 @@ export class ApNoteService {
}
}

const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
const apHashtags = extractApHashtags(note.tag);

// 添付ファイル
// TODO: attachmentは必ずしもImageではない
// TODO: attachmentは必ずしも配列ではない
Expand Down Expand Up @@ -233,18 +267,6 @@ export class ApNoteService {
}
}

const cw = note.summary === '' ? null : note.summary;

// テキストのパース
let text: string | null = null;
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
text = note.source.content;
} else if (typeof note._misskey_content !== 'undefined') {
text = note._misskey_content;
} else if (typeof note.content === 'string') {
text = this.apMfmService.htmlToMfm(note.content, note.tag);
}

// vote
if (reply && reply.hasPoll) {
const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
Expand Down Expand Up @@ -274,8 +296,6 @@ export class ApNoteService {

const apEmojis = emojis.map(emoji => emoji.name);

const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);

try {
return await this.noteCreateService.create(actor, {
createdAt: note.published ? new Date(note.published) : null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ export class InboxProcessorService {
await this.apInboxService.performActivity(authUser.user, activity);
} catch (e) {
if (e instanceof IdentifiableError) {
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') return 'blocked notes with prohibited words';
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
return 'blocked notes with prohibited words';
}
if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') return 'actor has been suspended';
}
throw e;
}
Expand Down

0 comments on commit eb60460

Please sign in to comment.