1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-05 22:56:00 +02:00

Merge commit from fork

* Tighten security in `HashtagChannel`

* Add isNoteVisibleForMe in stream channel

Co-Authored-By: Julia Johannesen <julia@insertdomain.name>

* Tighten note visibility checks in WebSocket (No.1)

* refactor

* Fix main channel

Co-Authored-By: Julia Johannesen <julia@insertdomain.name>

* fix typo

* fix missing lockdown (requireSigninToViewContents) checks

* fix(backend): streamingでのロックダウン挙動修正

* fix: 引用リノートを無条件で隠していた問題を修正

* fix: 引用リノートを単純にリノート場合に内容が見えることがある問題を修正

* refac

* fix

* fix

* fix

* Update docs

---------

Co-authored-by: Julia Johannesen <julia@insertdomain.name>
Co-authored-by: KanariKanaru <93921745+kanarikanaru@users.noreply.github.com>
This commit is contained in:
かっこかり
2026-03-09 08:15:31 +09:00
committed by GitHub
parent a07dc589e7
commit b361a10c48
15 changed files with 346 additions and 103 deletions

View File

@@ -259,7 +259,7 @@ export class QueryService {
@bindThis
public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void {
// This code must always be synchronized with the checks in Notes.isVisibleForMe.
// This code must always be synchronized with the checks in NoteEntityService.isVisibleForMe and Stream abstract class Channel.isNoteVisibleForMe.
if (me == null) {
q.andWhere(new Brackets(qb => {
qb

View File

@@ -17,6 +17,7 @@ import { DebounceLoader } from '@/misc/loader.js';
import { IdService } from '@/core/IdService.js';
import { shouldHideNoteByTime } from '@/misc/should-hide-note-by-time.js';
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
import { CacheService } from '@/core/CacheService.js';
import type { OnModuleInit } from '@nestjs/common';
import type { CustomEmojiService } from '../CustomEmojiService.js';
import type { ReactionService } from '../ReactionService.js';
@@ -66,6 +67,7 @@ export class NoteEntityService implements OnModuleInit {
private reactionService: ReactionService;
private reactionsBufferingService: ReactionsBufferingService;
private idService: IdService;
private cacheService: CacheService;
private noteLoader = new DebounceLoader(this.findNoteOrFail);
constructor(
@@ -101,6 +103,7 @@ export class NoteEntityService implements OnModuleInit {
//private reactionService: ReactionService,
//private reactionsBufferingService: ReactionsBufferingService,
//private idService: IdService,
//private cacheService: CacheService,
) {
}
@@ -111,6 +114,7 @@ export class NoteEntityService implements OnModuleInit {
this.reactionService = this.moduleRef.get('ReactionService');
this.reactionsBufferingService = this.moduleRef.get('ReactionsBufferingService');
this.idService = this.moduleRef.get('IdService');
this.cacheService = this.moduleRef.get('CacheService');
}
@bindThis
@@ -125,75 +129,65 @@ export class NoteEntityService implements OnModuleInit {
}
@bindThis
private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<void> {
if (meId === packedNote.userId) return;
public async shouldHideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<boolean> {
if (meId === packedNote.userId) return false;
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
let hide = false;
if (packedNote.user.requireSigninToViewContents && meId == null) {
hide = true;
return true;
}
if (!hide) {
const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
if (shouldHideNoteByTime(hiddenBefore, packedNote.createdAt)) {
hide = true;
}
const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
if (shouldHideNoteByTime(hiddenBefore, packedNote.createdAt)) {
return true;
}
// visibility が specified かつ自分が指定されていなかったら非表示
if (!hide) {
if (packedNote.visibility === 'specified') {
if (meId == null) {
hide = true;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some(id => meId === id);
if (packedNote.visibility === 'specified') {
if (meId == null) {
return true;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some(id => meId === id);
if (!specified) {
hide = true;
}
if (!specified) {
return true;
}
}
}
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
if (!hide) {
if (packedNote.visibility === 'followers') {
if (meId == null) {
hide = true;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
// 自分の投稿に対するリプライ
hide = false;
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
// 自分へのメンション
hide = false;
} else {
// フォロワーかどうか
// TODO: 当関数呼び出しごとにクエリが走るのは重そうだからなんとかする
const isFollowing = await this.followingsRepository.exists({
where: {
followeeId: packedNote.userId,
followerId: meId,
},
});
hide = !isFollowing;
if (packedNote.visibility === 'followers') {
if (meId == null) {
return true;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
// 自分の投稿に対するリプライ
return false;
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
// 自分へのメンション
return false;
} else {
// フォロワーかどうか
const followings = await this.cacheService.userFollowingsCache.fetch(meId);
if (!Object.hasOwn(followings, packedNote.userId)) {
return true;
}
}
}
if (hide) {
packedNote.visibleUserIds = undefined;
packedNote.fileIds = [];
packedNote.files = [];
packedNote.text = null;
packedNote.poll = undefined;
packedNote.cw = null;
packedNote.isHidden = true;
// TODO: hiddenReason みたいなのを提供しても良さそう
}
return false;
}
@bindThis
public hideNote(packedNote: Packed<'Note'>): void {
packedNote.visibleUserIds = undefined;
packedNote.fileIds = [];
packedNote.files = [];
packedNote.text = null;
packedNote.poll = undefined;
packedNote.cw = null;
packedNote.isHidden = true;
// TODO: hiddenReason みたいなのを提供しても良さそう
}
@bindThis
@@ -278,7 +272,7 @@ export class NoteEntityService implements OnModuleInit {
@bindThis
public async isVisibleForMe(note: MiNote, meId: MiUser['id'] | null): Promise<boolean> {
// This code must always be synchronized with the checks in generateVisibilityQuery.
// This code must always be synchronized with the checks in QueryService.generateVisibilityQuery.
// visibility が specified かつ自分が指定されていなかったら非表示
if (note.visibility === 'specified') {
if (meId == null) {
@@ -468,8 +462,8 @@ export class NoteEntityService implements OnModuleInit {
this.treatVisibility(packed);
if (!opts.skipHide) {
await this.hideNote(packed, meId);
if (!opts.skipHide && await this.shouldHideNote(packed, meId)) {
this.hideNote(packed);
}
return packed;