summaryrefslogtreecommitdiff
path: root/packages/backend
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2024-03-01 11:57:26 +0900
committertaichanne30 <dev@taichan.site>2024-03-01 20:16:32 +0900
commiteb60460d28be24513b567d378cec6ecba5c158c7 (patch)
tree2479a77353e0657064d84de77e1c229c9cf71348 /packages/backend
parentadd missing license headers (diff)
downloadmisskey-eb60460d28be24513b567d378cec6ecba5c158c7.tar.gz
misskey-eb60460d28be24513b567d378cec6ecba5c158c7.tar.bz2
misskey-eb60460d28be24513b567d378cec6ecba5c158c7.zip
enhance: 禁止ワードチェック強化 (#27)
* enhance: 禁止ワードチェック強化 * リモートの禁止ワードチェックを添付ファイルとユーザーを登録する前に行うなど Resolve https://github.com/misskey-dev/misskey/issues/13374 * 禁止ワートの対象の見直し * performActivityで特定のエラーが出た際にDelayedに追加しないように * use IdentifiableError * NoteCreateService.checkProhibitedWords * https://github.com/misskey-dev/misskey-private/pull/27/files#r1507416135 * remove comment
Diffstat (limited to '')
-rw-r--r--packages/backend/src/core/NoteCreateService.ts25
-rw-r--r--packages/backend/src/core/UtilityService.ts14
-rw-r--r--packages/backend/src/core/activitypub/ApInboxService.ts1
-rw-r--r--packages/backend/src/core/activitypub/models/ApNoteService.ts62
-rw-r--r--packages/backend/src/queue/processors/InboxProcessorService.ts5
5 files changed, 83 insertions, 24 deletions
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 727787f868..81ae2908d3 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -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');
}
@@ -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();
diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts
index 638a0c019e..652e8f7449 100644
--- a/packages/backend/src/core/UtilityService.ts
+++ b/packages/backend/src/core/UtilityService.ts
@@ -43,6 +43,20 @@ export class UtilityService {
}
@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;
if (text === '') return false;
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index b0f56a5d82..1621c41bcc 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -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';
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index e201b88173..b2fd435f93 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -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';
@@ -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 {
@@ -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);
@@ -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は必ずしも配列ではない
@@ -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 });
@@ -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,
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 0a713149e5..3addead058 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -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;
}