enhance(backend): Implementation of HTTP header and body validation to fix SIF-2023-002 (#12334)
Using Buffer instead of string Co-authored-by: perillamint <perillamint@silicon.moe>
This commit is contained in:
parent
aa6d0d4359
commit
04075ee0be
4 changed files with 29 additions and 5 deletions
|
@ -151,6 +151,7 @@
|
||||||
"rss-parser": "3.13.0",
|
"rss-parser": "3.13.0",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
"sanitize-html": "2.11.0",
|
"sanitize-html": "2.11.0",
|
||||||
|
"secure-json-parse": "^2.4.0",
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
"sharp-read-bmp": "github:misskey-dev/sharp-read-bmp",
|
"sharp-read-bmp": "github:misskey-dev/sharp-read-bmp",
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
|
|
|
@ -11,6 +11,7 @@ import httpSignature from '@peertube/http-signature';
|
||||||
import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
|
import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
|
||||||
import accepts from 'accepts';
|
import accepts from 'accepts';
|
||||||
import vary from 'vary';
|
import vary from 'vary';
|
||||||
|
import secureJson from 'secure-json-parse';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
|
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
|
||||||
import * as url from '@/misc/prelude/url.js';
|
import * as url from '@/misc/prelude/url.js';
|
||||||
|
@ -28,7 +29,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { IActivity } from '@/core/activitypub/type.js';
|
import { IActivity } from '@/core/activitypub/type.js';
|
||||||
import { isPureRenote } from '@/misc/is-pure-renote.js';
|
import { isPureRenote } from '@/misc/is-pure-renote.js';
|
||||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
|
||||||
import type { FindOptionsWhere } from 'typeorm';
|
import type { FindOptionsWhere } from 'typeorm';
|
||||||
|
|
||||||
const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
|
const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
|
||||||
|
@ -512,9 +513,28 @@ export class ActivityPubServerService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const almostDefaultJsonParser: FastifyBodyParser<Buffer> = function (request, rawBody, done) {
|
||||||
|
if (rawBody.length === 0) {
|
||||||
|
const err = new Error('Body cannot be empty!') as any;
|
||||||
|
err.statusCode = 400;
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = secureJson.parse(rawBody.toString('utf8'), null, {
|
||||||
|
protoAction: 'ignore',
|
||||||
|
constructorAction: 'ignore',
|
||||||
|
});
|
||||||
|
done(null, json);
|
||||||
|
} catch (err: any) {
|
||||||
|
err.statusCode = 400;
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fastify.register(fastifyAccepts);
|
fastify.register(fastifyAccepts);
|
||||||
fastify.addContentTypeParser('application/activity+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'));
|
fastify.addContentTypeParser('application/activity+json', { parseAs: 'buffer' }, almostDefaultJsonParser);
|
||||||
fastify.addContentTypeParser('application/ld+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'));
|
fastify.addContentTypeParser('application/ld+json', { parseAs: 'buffer' }, almostDefaultJsonParser);
|
||||||
|
|
||||||
fastify.addHook('onRequest', (request, reply, done) => {
|
fastify.addHook('onRequest', (request, reply, done) => {
|
||||||
reply.header('Access-Control-Allow-Headers', 'Accept');
|
reply.header('Access-Control-Allow-Headers', 'Accept');
|
||||||
|
|
|
@ -88,9 +88,9 @@ export class ServerService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register raw-body parser for ActivityPub HTTP signature validation.
|
// Register raw-body parser for ActivityPub HTTP signature validation.
|
||||||
fastify.register(fastifyRawBody, {
|
await fastify.register(fastifyRawBody, {
|
||||||
global: false,
|
global: false,
|
||||||
encoding: 'utf-8',
|
encoding: null,
|
||||||
runFirst: true,
|
runFirst: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -338,6 +338,9 @@ importers:
|
||||||
sanitize-html:
|
sanitize-html:
|
||||||
specifier: 2.11.0
|
specifier: 2.11.0
|
||||||
version: 2.11.0
|
version: 2.11.0
|
||||||
|
secure-json-parse:
|
||||||
|
specifier: ^2.4.0
|
||||||
|
version: 2.7.0
|
||||||
sharp:
|
sharp:
|
||||||
specifier: 0.32.6
|
specifier: 0.32.6
|
||||||
version: 0.32.6
|
version: 0.32.6
|
||||||
|
|
Loading…
Reference in a new issue