Prototype Pollution
Description
Prototype pollution in @tsed/core's deepExtend allows attackers to overwrite Object prototype, leading to potential DoS or RCE.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Prototype pollution in @tsed/core's deepExtend allows attackers to overwrite Object prototype, leading to potential DoS or RCE.
The vulnerability is a prototype pollution flaw in the deepExtend function of @tsed/core, a utility library for the Ts.ED framework. The function does not properly restrict the modification of object prototypes, allowing an attacker to inject properties into the global Object.prototype [1][4].
Exploitation occurs when user input is passed to deepExtend without sanitization. An attacker can craft a malicious object containing __proto__ or constructor.prototype properties, which when merged pollutes the prototype chain. This can be triggered via application endpoints that accept JSON or other serialized input that eventually reaches deepExtend [2][4].
Once the prototype is polluted, all objects inherit the injected properties, potentially causing denial of service via exceptions or altering application behavior. In severe cases, this can lead to remote code execution if the polluted properties are used in security-sensitive operations [4].
The fix was released in version 5.65.7, which uses an objectKeys function to prevent prototype properties from being overwritten [2]. Users should upgrade to the latest version. The vulnerability is listed in Snyk and other databases [1][4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
@tsed/corenpm | < 5.65.7 | 5.65.7 |
Affected products
2- @tsed/core/@tsed/coredescription
Patches
11395773ddac3fix: Use objectKeys to prevent prototype pollution
9 files changed · +28 −31
packages/common/src/converters/components/MapConverter.ts+2 −1 modified@@ -1,3 +1,4 @@ +import {objectKeys} from "@tsed/core"; import {Converter} from "../decorators/converter"; import {IConverter, IDeserializer, ISerializer} from "../interfaces/index"; @@ -19,7 +20,7 @@ export class MapConverter implements IConverter { deserialize<T>(data: any, target: any, baseType: T, deserializer: IDeserializer): Map<string, T> { const obj = new Map<string, T>(); - Object.keys(data).forEach((key) => { + objectKeys(data).forEach((key) => { obj.set(key, deserializer(data[key], baseType) as T); });
packages/common/src/converters/components/SetConverter.ts+2 −1 modified@@ -1,3 +1,4 @@ +import {objectKeys} from "@tsed/core"; import {Converter} from "../decorators/converter"; import {IConverter, IDeserializer, ISerializer} from "../interfaces/index"; @@ -19,7 +20,7 @@ export class SetConverter implements IConverter { deserialize<T>(data: any, target: any, baseType: T, deserializer: IDeserializer): Set<T> { const obj = new Set<T>(); - Object.keys(data).forEach((key) => { + objectKeys(data).forEach((key) => { obj.add(deserializer(data[key], baseType) as T); });
packages/common/src/converters/services/ConverterService.ts+3 −3 modified@@ -1,4 +1,4 @@ -import {getClass, isArrayOrArrayClass, isEmpty, isPrimitiveOrPrimitiveClass, Metadata, Type} from "@tsed/core"; +import {getClass, isArrayOrArrayClass, isEmpty, isPrimitiveOrPrimitiveClass, Metadata, objectKeys, Type} from "@tsed/core"; import {Configuration, Injectable, InjectorService} from "@tsed/di"; import {IConverterSettings} from "../../config/interfaces/IConverterSettings"; import {PropertyMetadata} from "../../mvc/models/PropertyMetadata"; @@ -101,7 +101,7 @@ export class ConverterService { const plainObject: any = {}; const properties = PropertyMetadata.getProperties(options.type || obj, {withIgnoredProps}); - const keys = properties.size ? Array.from(properties.keys()) : Object.keys(obj); + const keys = properties.size ? Array.from(properties.keys()) : objectKeys(obj); keys.forEach((propertyKey) => { if (typeof obj[propertyKey] !== "function") { @@ -179,7 +179,7 @@ export class ConverterService { const instance = new targetType(); const properties = PropertyMetadata.getProperties(targetType); - Object.keys(obj).forEach((propertyName: string) => { + objectKeys(obj).forEach((propertyName: string) => { const propertyMetadata = ConverterService.getPropertyMetadata(properties, propertyName); return this.convertProperty(obj, instance, propertyName, propertyMetadata, options);
packages/core/src/utils/cleanObject.ts+13 −11 modified@@ -1,17 +1,19 @@ +import {isProtectedKey} from "./isProtectedKey"; /** * Remove undefined value * @param obj */ - export function cleanObject(obj: any): any { - return Object.entries(obj).reduce( - (obj, [key, value]) => - value === undefined - ? obj - : { - ...obj, - [key]: value - }, - {} - ); + return Object.entries(obj).reduce((obj, [key, value]) => { + if (isProtectedKey(key)) { + return obj; + } + + return value === undefined + ? obj + : { + ...obj, + [key]: value + }; + }, {}); }
packages/json-mapper/src/components/MapMapper.ts+2 −1 modified@@ -1,3 +1,4 @@ +import {objectKeys} from "@tsed/core"; import {JsonMapper} from "../decorators/jsonMapper"; import {JsonMapperCtx, JsonMapperMethods} from "../interfaces/JsonMapperMethods"; @@ -12,7 +13,7 @@ export class MapMapper implements JsonMapperMethods { deserialize<T = any, C = Map<string, T>>(data: {[key: string]: any}, ctx: JsonMapperCtx<T, C>): Map<string, T> { const obj = new Map<string, T>(); - Object.keys(data).forEach((key) => { + objectKeys(data).forEach((key) => { obj.set(key, ctx.next(data[key]) as T); });
packages/json-mapper/src/components/SetMapper.ts+2 −1 modified@@ -1,3 +1,4 @@ +import {objectKeys} from "@tsed/core"; import {JsonMapper} from "../decorators/jsonMapper"; import {JsonMapperCtx, JsonMapperMethods} from "../interfaces/JsonMapperMethods"; @@ -12,7 +13,7 @@ export class SetMapper implements JsonMapperMethods { deserialize<T>(data: any, ctx: JsonMapperCtx): Set<T> { const obj = new Set<T>(); - Object.keys(data).forEach((key) => { + objectKeys(data).forEach((key) => { obj.add(ctx.next(data[key])); });
packages/json-mapper/src/utils/deserialize.ts+2 −2 modified@@ -1,4 +1,4 @@ -import {isArray, isEmpty, isNil, MetadataTypes, nameOf, Type} from "@tsed/core"; +import {isArray, isEmpty, isNil, MetadataTypes, nameOf, objectKeys, Type} from "@tsed/core"; import {getPropertiesStores, JsonEntityStore, JsonHookContext, JsonSchema} from "@tsed/schema"; import "../components"; import {JsonMapperContext} from "../domain/JsonMapperContext"; @@ -77,7 +77,7 @@ export function plainObjectToClass<T = any>(src: any, options: JsonDeserializerO const {type, store = JsonEntityStore.from(type), ...next} = options; const propertiesMap = getPropertiesStores(store); - let keys = Object.keys(src); + let keys = objectKeys(src); const additionalProperties = propertiesMap.size ? !!store.schema.get("additionalProperties") || options.additionalProperties : true; const out: any = new type(src);
packages/mongoose/src/utils/cleanProps.ts+0 −8 removed@@ -1,8 +0,0 @@ -export const cleanProps = (src: any) => - Object.keys(src).reduce((obj: any, k: any) => { - if (src[k] !== undefined) { - obj[k] = src[k]; - } - - return obj; - }, {});
packages/mongoose/src/utils/createSchema.ts+2 −3 modified@@ -1,10 +1,9 @@ import {ConverterService, IConverterOptions, JsonSchema, PropertyMetadata} from "@tsed/common"; -import {getClass, Store, Type} from "@tsed/core"; +import {cleanObject, getClass, Store, Type} from "@tsed/core"; import * as mongoose from "mongoose"; import {SchemaDefinition, SchemaTypeOpts} from "mongoose"; import {MONGOOSE_SCHEMA} from "../constants"; import {MongooseSchemaOptions} from "../interfaces"; -import {cleanProps} from "./cleanProps"; import {schemaOptions} from "./schemaOptions"; const MONGOOSE_RESERVED_KEYS = ["_id"]; @@ -132,7 +131,7 @@ export function createSchemaTypeOptions(propertyMetadata: PropertyMetadata): Sch schemaTypeOptions = {...schemaTypeOptions, type: getSchema(propertyMetadata.type)}; } - schemaTypeOptions = cleanProps({...schemaTypeOptions, ...rawMongooseSchema}); + schemaTypeOptions = cleanObject({...schemaTypeOptions, ...rawMongooseSchema}); if (propertyMetadata.isCollection) { if (propertyMetadata.isArray) {
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-77xq-cpvg-7xm2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-7748ghsaADVISORY
- github.com/TypedProject/tsed/blob/production/packages/core/src/utils/deepExtends.ts%23L36ghsax_refsource_MISCWEB
- github.com/TypedProject/tsed/commit/1395773ddac35926cf058fc6da9fb8e82266761bghsax_refsource_MISCWEB
- snyk.io/vuln/SNYK-JS-TSEDCORE-1019382ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.