activitypub: simplify some URI/id related checks

followup on previous commit
This commit is contained in:
Johann150 2022-12-15 00:31:23 +01:00
parent 3582fd8260
commit ef53ec276a
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1
3 changed files with 32 additions and 25 deletions

View file

@ -1,5 +1,5 @@
import { CacheableRemoteUser } from '@/models/entities/user.js'; import { CacheableRemoteUser } from '@/models/entities/user.js';
import { getApType, IUpdate, isActor } from '@/remote/activitypub/type.js'; import { getApId, getApType, IUpdate, isActor } from '@/remote/activitypub/type.js';
import { apLogger } from '@/remote/activitypub/logger.js'; import { apLogger } from '@/remote/activitypub/logger.js';
import { updateQuestion } from '@/remote/activitypub/models/question.js'; import { updateQuestion } from '@/remote/activitypub/models/question.js';
import { Resolver } from '@/remote/activitypub/resolver.js'; import { Resolver } from '@/remote/activitypub/resolver.js';
@ -21,7 +21,11 @@ export default async (actor: CacheableRemoteUser, activity: IUpdate, resolver: R
}); });
if (isActor(object)) { if (isActor(object)) {
await updatePerson(actor.uri!, resolver, object); if (actor.uri !== getApId(object)) {
return 'skip: actor id !== updated actor id';
}
await updatePerson(object, resolver);
return 'ok: Person updated'; return 'ok: Person updated';
} else if (getApType(object) === 'Question') { } else if (getApType(object) === 'Question') {
await updateQuestion(object, resolver).catch(e => console.log(e)); await updateQuestion(object, resolver).catch(e => console.log(e));

View file

@ -28,9 +28,7 @@ import { extractApHashtags } from './tag.js';
import { extractPollFromQuestion } from './question.js'; import { extractPollFromQuestion } from './question.js';
import { extractApMentions } from './mention.js'; import { extractApMentions } from './mention.js';
export function validateNote(object: any, uri: string): Error | null { export function validateNote(object: IObject): Error | null {
const expectHost = extractDbHost(uri);
if (object == null) { if (object == null) {
return new Error('invalid Note: object is null'); return new Error('invalid Note: object is null');
} }
@ -39,12 +37,20 @@ export function validateNote(object: any, uri: string): Error | null {
return new Error(`invalid Note: invalid object type ${getApType(object)}`); return new Error(`invalid Note: invalid object type ${getApType(object)}`);
} }
if (object.id && extractDbHost(object.id) !== expectHost) { const id = getApId(object);
return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`); if (id == null) {
// Only transient objects or anonymous objects may not have an id or an id that is explicitly null.
// We consider all Notes as not transient and not anonymous so require ids for them.
return new Error(`invalid Note: id required but not present`);
} }
if (object.attributedTo && extractDbHost(getOneApId(object.attributedTo)) !== expectHost) { // Check that the server is authorized to act on behalf of this author.
return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`); const expectHost = extractDbHost(id);
const attributedToHost = object.attributedTo
? extractDbHost(getOneApId(object.attributedTo))
: null;
if (attributedToHost !== expectHost) {
return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${attributedToHost}`);
} }
return null; return null;
@ -64,10 +70,9 @@ export async function fetchNote(object: string | IObject): Promise<Note | null>
* Noteを作成します * Noteを作成します
*/ */
export async function createNote(value: string | IObject, resolver: Resolver, silent = false): Promise<Note | null> { export async function createNote(value: string | IObject, resolver: Resolver, silent = false): Promise<Note | null> {
const object: any = await resolver.resolve(value); const object: IObject = await resolver.resolve(value);
const entryUri = getApId(value); const err = validateNote(object);
const err = validateNote(object, entryUri);
if (err) { if (err) {
apLogger.error(`${err.message}`, { apLogger.error(`${err.message}`, {
resolver: { resolver: {

View file

@ -39,8 +39,7 @@ const summaryLength = 2048;
* @param x Fetched object * @param x Fetched object
* @param uri Fetch target URI * @param uri Fetch target URI
*/ */
function validateActor(x: IObject, uri: string): IActor { function validateActor(x: IObject): IActor {
const expectHost = toPuny(new URL(uri).hostname);
if (x == null) { if (x == null) {
throw new Error('invalid Actor: object is null'); throw new Error('invalid Actor: object is null');
@ -50,7 +49,10 @@ function validateActor(x: IObject, uri: string): IActor {
throw new Error(`invalid Actor type '${x.type}'`); throw new Error(`invalid Actor type '${x.type}'`);
} }
if (!(typeof x.id === 'string' && x.id.length > 0)) { const uri = getApId(x);
if (uri == null) {
// Only transient objects or anonymous objects may not have an id or an id that is explicitly null.
// We consider all actors as not transient and not anonymous so require ids for them.
throw new Error('invalid Actor: wrong id'); throw new Error('invalid Actor: wrong id');
} }
@ -78,16 +80,12 @@ function validateActor(x: IObject, uri: string): IActor {
x.summary = truncate(x.summary, summaryLength); x.summary = truncate(x.summary, summaryLength);
} }
const idHost = toPuny(new URL(x.id!).hostname);
if (idHost !== expectHost) {
throw new Error('invalid Actor: id has different host');
}
if (x.publicKey) { if (x.publicKey) {
if (typeof x.publicKey.id !== 'string') { if (typeof x.publicKey.id !== 'string') {
throw new Error('invalid Actor: publicKey.id is not a string'); throw new Error('invalid Actor: publicKey.id is not a string');
} }
const expectHost = extractDbHost(uri);
const publicKeyIdHost = toPuny(new URL(x.publicKey.id).hostname); const publicKeyIdHost = toPuny(new URL(x.publicKey.id).hostname);
if (publicKeyIdHost !== expectHost) { if (publicKeyIdHost !== expectHost) {
throw new Error('invalid Actor: publicKey.id has different host'); throw new Error('invalid Actor: publicKey.id has different host');
@ -140,7 +138,7 @@ export async function createPerson(uri: string, resolver: Resolver): Promise<Use
const object = await resolver.resolve(uri) as any; const object = await resolver.resolve(uri) as any;
const person = validateActor(object, uri); const person = validateActor(object);
apLogger.info(`Creating the Person: ${person.id}`); apLogger.info(`Creating the Person: ${person.id}`);
@ -278,8 +276,8 @@ export async function createPerson(uri: string, resolver: Resolver): Promise<Use
* @param resolver Resolver * @param resolver Resolver
* @param hint Hint of Person object (If this value is a valid Person, it is used for updating without Remote resolve.) * @param hint Hint of Person object (If this value is a valid Person, it is used for updating without Remote resolve.)
*/ */
export async function updatePerson(uri: string, resolver: Resolver, hint?: IObject): Promise<void> { export async function updatePerson(value: IObject | string, resolver: Resolver): Promise<void> {
if (typeof uri !== 'string') throw new Error('uri is not string'); const uri = getApId(value);
// URIがこのサーバーを指しているならスキップ // URIがこのサーバーを指しているならスキップ
if (uri.startsWith(config.url + '/')) { if (uri.startsWith(config.url + '/')) {
@ -294,9 +292,9 @@ export async function updatePerson(uri: string, resolver: Resolver, hint?: IObje
} }
//#endregion //#endregion
const object = hint || await resolver.resolve(uri); const object = await resolver.resolve(value);
const person = validateActor(object, uri); const person = validateActor(object);
apLogger.info(`Updating the Person: ${person.id}`); apLogger.info(`Updating the Person: ${person.id}`);