refactor(federation): 🦺 Update Lysand object validatio to be stricter

This commit is contained in:
Jesse Wierzbinski 2024-05-13 11:33:39 -10:00
parent eb976250a4
commit 5e87f85851
No known key found for this signature in database

View file

@ -1,5 +1,30 @@
import { emojiValidator } from "@api";
import {
charIn,
createRegExp,
digit,
exactly,
letter,
oneOrMore,
} from "magic-regexp";
import { types } from "mime-types";
import { z } from "zod"; import { z } from "zod";
const ContentFormat = z.record(
z.enum(Object.values(types) as [string, ...string[]]),
z.object({
content: z.string(),
description: z.string().optional(),
size: z.number().int().nonnegative().optional(),
hash: z.record(z.string(), z.string()).optional(),
blurhash: z.string().optional(),
fps: z.number().int().nonnegative().optional(),
width: z.number().int().nonnegative().optional(),
height: z.number().int().nonnegative().optional(),
duration: z.number().nonnegative().optional(),
}),
);
const Entity = z.object({ const Entity = z.object({
id: z.string().uuid(), id: z.string().uuid(),
created_at: z.string(), created_at: z.string(),
@ -9,33 +34,18 @@ const Entity = z.object({
"org.lysand:custom_emojis": z.object({ "org.lysand:custom_emojis": z.object({
emojis: z.array( emojis: z.array(
z.object({ z.object({
shortcode: z.string(), name: z.string().regex(emojiValidator),
url: z.string(), url: ContentFormat,
}), }),
), ),
}), }),
}), }),
}); });
const ContentFormat = z.record(
z.string(),
z.object({
content: z.string(),
description: z.string().optional(),
size: z.number().optional(),
hash: z.record(z.string().optional()).optional(),
blurhash: z.string().optional(),
fps: z.number().optional(),
width: z.number().optional(),
height: z.number().optional(),
duration: z.number().optional(),
}),
);
const Visibility = z.enum(["public", "unlisted", "private", "direct"]); const Visibility = z.enum(["public", "unlisted", "private", "direct"]);
const Publication = Entity.extend({ const Publication = Entity.extend({
type: z.union([z.literal("Note"), z.literal("Patch")]), type: z.enum(["Note", "Patch"]),
author: z.string().url(), author: z.string().url(),
content: ContentFormat.optional(), content: ContentFormat.optional(),
attachments: z.array(ContentFormat).optional(), attachments: z.array(ContentFormat).optional(),
@ -55,7 +65,7 @@ const Publication = Entity.extend({
.object({ .object({
poll: z.object({ poll: z.object({
options: z.array(ContentFormat), options: z.array(ContentFormat),
votes: z.array(z.number()), votes: z.array(z.number().int().nonnegative()),
multiple_choice: z.boolean().optional(), multiple_choice: z.boolean().optional(),
expires_at: z.string(), expires_at: z.string(),
}), }),
@ -183,7 +193,20 @@ const Announce = Action.extend({
const Extension = Entity.extend({ const Extension = Entity.extend({
type: z.literal("Extension"), type: z.literal("Extension"),
extension_type: z.string(), extension_type: z.string().regex(
createRegExp(
// org namespace, then colon, then alphanumeric/_/-, then extension name
exactly(
oneOrMore(
exactly(letter.lowercase.or(digit).or(charIn("_-."))),
),
exactly(":"),
oneOrMore(exactly(letter.lowercase.or(digit).or(charIn("_-")))),
exactly("/"),
oneOrMore(exactly(letter.or(digit).or(charIn("_-")))),
),
),
),
}); });
const Reaction = Extension.extend({ const Reaction = Extension.extend({
@ -195,7 +218,7 @@ const Reaction = Extension.extend({
const Poll = Extension.extend({ const Poll = Extension.extend({
extension_type: z.literal("org.lysand:polls/Poll"), extension_type: z.literal("org.lysand:polls/Poll"),
options: z.array(ContentFormat), options: z.array(ContentFormat),
votes: z.array(z.number()), votes: z.array(z.number().int().nonnegative()),
multiple_choice: z.boolean().optional(), multiple_choice: z.boolean().optional(),
expires_at: z.string(), expires_at: z.string(),
}); });
@ -209,7 +232,7 @@ const Vote = Extension.extend({
const VoteResult = Extension.extend({ const VoteResult = Extension.extend({
extension_type: z.literal("org.lysand:polls/VoteResult"), extension_type: z.literal("org.lysand:polls/VoteResult"),
poll: z.string().url(), poll: z.string().url(),
votes: z.array(z.number()), votes: z.array(z.number().int().nonnegative()),
}); });
const Report = Extension.extend({ const Report = Extension.extend({