Moderate severityNVD Advisory· Published Mar 18, 2026· Updated Mar 18, 2026
Elysia Cookie Value Prototype Pollution
CVE-2026-31865
Description
Elysia is a Typescript framework for request validation, type inference, OpenAPI documentation, and client-server communication. Prior to version 1.4.27, an Elysia cookie can be overridden by prototype pollution , eg. __proto__. This issue is patched in 1.4.27. As a workaround, use t.Cookie validation to enforce validation value and/or prevent iterable over cookie if possible.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
elysianpm | < 1.4.27 | 1.4.27 |
Affected products
1Patches
17 files changed · +62 −14
CHANGELOG.md+2 −0 modified@@ -1,10 +1,12 @@ # 1.4.27 - 1 Mar 2025 Bug fix: - getSchemaValidator: handle TypeBox as sub type +- handle cookie prototype pollution when parsing cookie Improvement: - conditional async on getSchemaValidator when schema is Standard Schema - use Response.json on Bun +- export `AnySchema`, `UnwrapSchema`, `ModelsToTypes` from root # 1.4.26 - 25 Feb 2025 Bug fix:
example/a.ts+5 −3 modified@@ -2,6 +2,8 @@ import { Elysia, t, getSchemaValidator, fileType } from '../src' import { z } from 'zod' import { post } from '../test/utils' -console.log(Response.json({ - a: 'a' -})) +console.log( + Response.json({ + a: 'a' + }) +)
package.json+1 −1 modified@@ -1,7 +1,7 @@ { "name": "elysia", "description": "Ergonomic Framework for Human", - "version": "1.4.26", + "version": "1.4.27", "author": { "name": "saltyAom", "url": "https://github.com/SaltyAom",
src/cookies.ts+14 −9 modified@@ -150,7 +150,7 @@ export class Cookie<T> implements ElysiaCookie { constructor( private name: string, private jar: Record<string, ElysiaCookie>, - private initial: Partial<ElysiaCookie> = {} + private initial: Partial<ElysiaCookie> = Object.create(null) ) {} get cookie() { @@ -350,7 +350,7 @@ export const createCookieJar = ( store: Record<string, ElysiaCookie>, initial?: Partial<ElysiaCookie> ): Record<string, Cookie<unknown>> => { - if (!set.cookie) set.cookie = {} + if (!set.cookie) set.cookie = Object.create(null) return new Proxy(store, { get(_, key: string) { @@ -379,18 +379,24 @@ export const parseCookie = async ( ...initial }: CookieOptions & { sign?: true | string | string[] - } = {} + } = Object.create(null) ) => { - if (!cookieString) return createCookieJar(set, {}, initial) + if (!cookieString) return createCookieJar(set, Object.create(null), initial) const isStringKey = typeof secrets === 'string' if (sign && sign !== true && !Array.isArray(sign)) sign = [sign] - const jar: Record<string, ElysiaCookie> = {} + const jar: Record<string, ElysiaCookie> = Object.create(null) const cookies = parse(cookieString) for (const [name, v] of Object.entries(cookies)) { - if (v === undefined) continue + if ( + v === undefined || + name === '__proto__' || + name === 'constructor' || + name === 'prototype' + ) + continue let value = decode(v) @@ -440,9 +446,8 @@ export const parseCookie = async ( } catch {} } - jar[name] = { - value - } + jar[name] = Object.create(null) + jar[name].value = value } return createCookieJar(set, jar, initial)
src/index.ts+2 −1 modified@@ -164,7 +164,8 @@ import type { UnknownRouteSchema, MaybeFunction, InlineHandlerNonMacro, - Router + Router, + ModelsToTypes } from './types' import { coercePrimitiveRoot,
src/types.ts+4 −0 modified@@ -2711,3 +2711,7 @@ export interface Router { } history: InternalRoute[] } + +export type ModelsToTypes<T extends Record<keyof any, AnySchema>> = { + [K in keyof T]: UnwrapSchema<T[K]> +}
test/validator/cookie.test.ts+34 −0 modified@@ -638,4 +638,38 @@ describe('Cookie Validation', () => { expect(second.status).toBe(200) }) + + it('handle prototype pollution', () => { + const app = new Elysia().get('/profile', ({ cookie }) => { + const proto = Object.getPrototypeOf(cookie) + const protoIsClean = proto === Object.prototype || proto === null + return { + hasPhantomValue: 'value' in cookie, + prototypeIsClean: protoIsClean, + prototype: proto, + enumeratedKeys: (() => { + const keys: string[] = [] + for (const k in cookie) keys.push(k) + return keys + })() + } + }) + + expect( + app + .handle( + new Request('http://localhost/profile', { + headers: { + cookie: 'a=hi;__proto__=%7B%22injected%22%3A%22polluted%22%7D' + } + }) + ) + .then((x) => x.json()) + ).resolves.toEqual({ + hasPhantomValue: false, + prototypeIsClean: true, + prototype: null, + enumeratedKeys: ['a'] + }) + }) })
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-8hq9-phh3-p2wpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-31865ghsaADVISORY
- github.com/elysiajs/elysia/commit/e9d6b1743fa7368ef942dce181f6a089757f6aabghsax_refsource_MISCWEB
- github.com/elysiajs/elysia/security/advisories/GHSA-8hq9-phh3-p2wpghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.