fix(security): validate avatar-decoration slugs from remote profiles
The MSC4133 io.lotus.avatar_decoration value was interpolated into the CDN URL verbatim, letting a room member steer the path/query of a request every viewer's browser makes. Accept only slugs present in the catalog; anything else is treated as no decoration. Unit-tested. Fixes #64 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -188,6 +188,19 @@ export const ALL_DECORATIONS: AvatarDecoration[] = DECORATION_CATEGORIES.flatMap
|
||||
(c) => c.decorations,
|
||||
);
|
||||
|
||||
const DECORATION_SLUGS = new Set(ALL_DECORATIONS.map((d) => d.slug));
|
||||
|
||||
/**
|
||||
* Whether `slug` is a known catalog decoration. `io.lotus.avatar_decoration`
|
||||
* is a free-form MSC4133 profile field set by a remote user (and their
|
||||
* homeserver), and its value is interpolated verbatim into `decorationUrl`
|
||||
* — so anything not in the catalog (path traversal, a query string, an
|
||||
* oversized value) must be rejected before it reaches a URL.
|
||||
*/
|
||||
export function isValidDecorationSlug(slug: string): boolean {
|
||||
return DECORATION_SLUGS.has(slug);
|
||||
}
|
||||
|
||||
export function decorationUrl(slug: string): string {
|
||||
return `${RESOLVED_DECORATION_CDN}/${slug}.png`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user