Fork of github.com/did-method-plc/did-method-plc
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Strip unknown values from payload when parsing operation (#73)

fixes did-method-plc/did-method-plc#71

authored by

Matthieu Sieben and committed by
GitHub
31f665e5 f96c8989

+30 -14
+28 -11
packages/server/src/constraints.ts
··· 1 - import { DAY, HOUR, cborEncode, check } from '@atproto/common' 1 + import { DAY, HOUR, cborEncode } from '@atproto/common' 2 2 import * as plc from '@did-plc/lib' 3 3 import { ServerError } from './error' 4 4 import { parseDidKey } from '@atproto/crypto' ··· 12 12 const MAX_SERVICE_ENDPOINT_LENGTH = 512 13 13 const MAX_ID_LENGTH = 32 14 14 15 - export function assertValidIncomingOp( 16 - op: unknown, 17 - ): asserts op is plc.OpOrTombstone { 18 - const byteLength = cborEncode(op).byteLength 15 + export function validateIncomingOp(input: unknown): plc.OpOrTombstone { 16 + const byteLength = cborEncode(input).byteLength 19 17 if (byteLength > MAX_OP_BYTES) { 20 18 throw new ServerError( 21 19 400, 22 20 `Operation too large (${MAX_OP_BYTES} bytes maximum in cbor encoding)`, 23 21 ) 24 22 } 25 - if (!check.is(op, plc.def.opOrTombstone)) { 26 - throw new ServerError(400, `Not a valid operation: ${JSON.stringify(op)}`) 23 + 24 + // We *need* to parse, and use the result of the parsing, to ensure that any 25 + // unknown fields are removed from the input. "@atproto/common"'s check 26 + // function will not remove unknown fields. 27 + const result = plc.def.opOrTombstone.safeParse(input) 28 + 29 + if (!result.success) { 30 + const errors = result.error.errors.map( 31 + (e) => `${e.message} at /${e.path.join('/')}`, 32 + ) 33 + throw new ServerError( 34 + 400, 35 + errors.length 36 + ? errors.join('. ') + '.' 37 + : `Not a valid operation: ${JSON.stringify(input)}`, 38 + ) 27 39 } 40 + 41 + const op = result.data 42 + 28 43 if (op.type === 'plc_tombstone') { 29 - return 44 + return op 30 45 } 31 46 if (op.alsoKnownAs.length > MAX_AKA_ENTRIES) { 32 47 throw new ServerError( ··· 34 49 `To many alsoKnownAs entries (max ${MAX_AKA_ENTRIES})`, 35 50 ) 36 51 } 37 - const akaDupe: Record<string, boolean> = {} 52 + const akaDupe = new Set<string>() 38 53 for (const aka of op.alsoKnownAs) { 39 54 if (aka.length > MAX_AKA_LENGTH) { 40 55 throw new ServerError( ··· 42 57 `alsoKnownAs entry too long (max ${MAX_AKA_LENGTH}): ${aka}`, 43 58 ) 44 59 } 45 - if (akaDupe[aka]) { 60 + if (akaDupe.has(aka)) { 46 61 throw new ServerError(400, `duplicate alsoKnownAs entry: ${aka}`) 47 62 } else { 48 - akaDupe[aka] = true 63 + akaDupe.add(aka) 49 64 } 50 65 } 51 66 if (op.rotationKeys.length > MAX_ROTATION_ENTRIES) { ··· 102 117 throw new ServerError(400, `Invalid verificationMethod key: ${key}`) 103 118 } 104 119 } 120 + 121 + return op 105 122 } 106 123 107 124 const HOUR_LIMIT = 10
+2 -3
packages/server/src/routes.ts
··· 2 2 import * as plc from '@did-plc/lib' 3 3 import { ServerError } from './error' 4 4 import { AppContext } from './context' 5 - import { assertValidIncomingOp } from './constraints' 5 + import { validateIncomingOp } from './constraints' 6 6 7 7 export const createRouter = (ctx: AppContext): express.Router => { 8 8 const router = express.Router() ··· 113 113 // Update or create a DID doc 114 114 router.post('/:did', async function (req, res) { 115 115 const { did } = req.params 116 - const op = req.body 117 - assertValidIncomingOp(op) 116 + const op = validateIncomingOp(req.body) 118 117 await ctx.db.validateAndAddOp(did, op) 119 118 res.sendStatus(200) 120 119 })