From 16440d6be2b5e0461c2f96add006155539af6243 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:24:59 +0900 Subject: [PATCH 1/9] Update CHANGELOG.md Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 800c646c67..4bbc18850d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ### General - Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように - * デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。) + * デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。) * 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。 - Enhance: 通知がミュート、凍結を考慮するようになりました - Enhance: サーバーごとにモデレーションノートを残せるように From 5befd66e2120ba6e44b4c324264ca862374a1869 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:25:54 +0900 Subject: [PATCH 2/9] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bbc18850d..359b8acf56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - Fix: 設定のバックアップ作成時に名前を入力しなかった場合、ローカライゼーションがおかしくなる問題を修正 - Fix: ページ`/admin/emojis`の絵文字編集ダイアログで「リアクションとして使えるロール」を追加する際に何も選択せずOKを押下すると画面が固まる問題を修正 - Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正 +- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正 ### Server - Enhance: エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました @@ -119,7 +120,6 @@ - Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正 - Fix: MkCodeEditorで行がずれていってしまう問題の修正 - Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196 -- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正 ### Server - Enhance: 連合先のレートリミットを超過した際にリトライするようになりました From ca6399437cfb4836e19e707c5a985050bfcc9834 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:26:13 +0900 Subject: [PATCH 3/9] format --- packages/backend/src/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index d894ef730e..929070d0d2 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -18,7 +18,6 @@ * achievementEarned - 実績を獲得 * app - アプリ通知 * test - テスト通知(サーバー側) - * */ export const notificationTypes = [ 'note', From 5904d98208769b96cbbb1b7a8471408f0a5dfe1c Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:26:27 +0900 Subject: [PATCH 4/9] Update packages/backend/test/e2e/mute.ts Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com> --- packages/backend/test/e2e/mute.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts index 1e4225184a..1d28e07b7d 100644 --- a/packages/backend/test/e2e/mute.ts +++ b/packages/backend/test/e2e/mute.ts @@ -117,6 +117,7 @@ describe('Mute', () => { assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); }); + test('通知にミュートしているユーザーからのリプライが含まれない', async () => { const aliceNote = await post(alice, { text: 'hi' }); await post(bob, { text: '@alice hi', replyId: aliceNote.id }); From 6158ef138ec8088c0e3c615b2605c0fa1a6f402c Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:27:03 +0900 Subject: [PATCH 5/9] format --- packages/frontend/test/autocomplete.test.ts | 40 ++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/frontend/test/autocomplete.test.ts b/packages/frontend/test/autocomplete.test.ts index f6a7ce9450..394ac3a821 100644 --- a/packages/frontend/test/autocomplete.test.ts +++ b/packages/frontend/test/autocomplete.test.ts @@ -7,28 +7,28 @@ import { assert, describe, test } from 'vitest'; import { searchEmoji } from '@/scripts/search-emoji.js'; describe('emoji autocomplete', () => { - test('名前の完全一致は名前の前方一致より優先される', async () => { - const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); - assert.equal(result[0].emoji, ':foooo:'); - }); + test('名前の完全一致は名前の前方一致より優先される', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); - test('名前の前方一致は名前の部分一致より優先される', async () => { - const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); - assert.equal(result[0].emoji, ':baaar:'); - }); + test('名前の前方一致は名前の部分一致より優先される', async () => { + const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':baaar:'); + }); - test('名前の完全一致はタグの完全一致より優先される', async () => { - const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); - assert.equal(result[0].emoji, ':foooo:'); - }); + test('名前の完全一致はタグの完全一致より優先される', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); - test('名前の前方一致はタグの前方一致より優先される', async () => { - const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); - assert.equal(result[0].emoji, ':foooo:'); - }); + test('名前の前方一致はタグの前方一致より優先される', async () => { + const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); - test('名前の部分一致はタグの部分一致より優先される', async () => { - const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); - assert.equal(result[0].emoji, ':foooo:'); - }); + test('名前の部分一致はタグの部分一致より優先される', async () => { + const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); }); From d1bf432e14faa7bb420b6338038195963f6b73d2 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:28:46 +0900 Subject: [PATCH 6/9] add missing license headers --- packages/backend/src/misc/FileWriterStream.ts | 5 +++++ packages/backend/src/misc/JsonArrayStream.ts | 5 +++++ packages/frontend/src/scripts/search-emoji.ts | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/packages/backend/src/misc/FileWriterStream.ts b/packages/backend/src/misc/FileWriterStream.ts index 828851df0e..367a8eb560 100644 --- a/packages/backend/src/misc/FileWriterStream.ts +++ b/packages/backend/src/misc/FileWriterStream.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + import * as fs from 'node:fs/promises'; import type { PathLike } from 'node:fs'; diff --git a/packages/backend/src/misc/JsonArrayStream.ts b/packages/backend/src/misc/JsonArrayStream.ts index ad35bb3a79..754938989d 100644 --- a/packages/backend/src/misc/JsonArrayStream.ts +++ b/packages/backend/src/misc/JsonArrayStream.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + import { TransformStream } from 'node:stream/web'; /** diff --git a/packages/frontend/src/scripts/search-emoji.ts b/packages/frontend/src/scripts/search-emoji.ts index 07f55e5531..371f69b9a7 100644 --- a/packages/frontend/src/scripts/search-emoji.ts +++ b/packages/frontend/src/scripts/search-emoji.ts @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + export type EmojiDef = { emoji: string; name: string; From eb60460d28be24513b567d378cec6ecba5c158c7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Fri, 1 Mar 2024 11:57:26 +0900 Subject: [PATCH 7/9] =?UTF-8?q?enhance:=20=E7=A6=81=E6=AD=A2=E3=83=AF?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E5=BC=B7?= =?UTF-8?q?=E5=8C=96=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- .../backend/src/core/NoteCreateService.ts | 25 +++++++- packages/backend/src/core/UtilityService.ts | 14 +++++ .../src/core/activitypub/ApInboxService.ts | 1 - .../core/activitypub/models/ApNoteService.ts | 62 ++++++++++++------- .../queue/processors/InboxProcessorService.ts | 5 +- 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[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 @@ -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; 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; } From ba9d47fb69f6254a4a07c5a1ad18585a99a3fc8e Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 20:22:06 +0900 Subject: [PATCH 8/9] 2024.3.0 --- CHANGELOG.md | 3 ++- package.json | 2 +- packages/misskey-js/package.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 359b8acf56..15028e7008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ - --> -## 202x.x.x (unreleased) + +## 2024.3.0 ### General - Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように diff --git a/package.json b/package.json index 68814f74b8..dee4645ee3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.2.0", + "version": "2024.3.0", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 1069e85b23..a7c629119c 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2024.2.0", + "version": "2024.3.0", "description": "Misskey SDK for JavaScript", "types": "./built/dts/index.d.ts", "exports": { From fe5efd926efca8a4b14f54a5af930eb51eb4d5ec Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 1 Mar 2024 21:00:43 +0900 Subject: [PATCH 9/9] New translations ja-jp.yml (Chinese Traditional) (#13480) --- locales/zh-TW.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index bc872823f1..5cdecc10ac 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1655,6 +1655,7 @@ _role: gtlAvailable: "瀏覽全域時間軸" ltlAvailable: "瀏覽本地時間軸" canPublicNote: "允許公開貼文" + mentionMax: "貼文內的最大提及數" canInvite: "發行伺服器邀請碼" inviteLimit: "可建立邀請碼的數量" inviteLimitCycle: "邀請碼的發放間隔" @@ -2299,6 +2300,7 @@ _notification: reactedBySomeUsers: "{n}人做出了反應" renotedBySomeUsers: "{n}人做了轉發" followedBySomeUsers: "被{n}人追隨了" + flushNotification: "重置通知歷史紀錄" _types: all: "全部 " note: "使用者的最新貼文"