Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,45 @@ When using `om-data-mapper`:
4. Follow the principle of least privilege when processing untrusted data
5. Validate and sanitize all input data before mapping

### ⚠️ Critical: Dynamic Code Generation Security

This library uses dynamic code generation (`new Function()`) for performance optimization. **Mapping configurations MUST come from trusted sources only.**

#### βœ… Safe Usage

```typescript
// βœ… SAFE: Developer-defined configuration
const userMapper = Mapper.create({
name: 'user.fullName',
email: 'user.email'
});

// βœ… SAFE: Using Decorator API (recommended)
@Mapper()
class UserDTO {
@Map('user.fullName')
name: string;
}
```

#### ❌ Unsafe Usage - NEVER DO THIS

```typescript
// ❌ DANGEROUS: User input as mapping config
const userConfig = JSON.parse(request.body.mappingConfig);
const mapper = Mapper.create(userConfig); // CODE INJECTION RISK!

// ❌ DANGEROUS: External untrusted source
const externalConfig = await fetch('https://untrusted-api.com/config');
const mapper = Mapper.create(externalConfig); // CODE INJECTION RISK!
```

**Why this matters**: If an attacker can control the mapping configuration, they could inject arbitrary JavaScript code that executes with your application's privileges.

**Recommended approach**: Use the Decorator API (`@Mapper`, `@Map`, `@Transform`) which is compile-time safe and provides better performance (112-474% faster).

See the class documentation and `docs/DECORATOR_API.md` for more details.

## Additional Resources

- [GitHub Security Advisories](https://github.com/Isqanderm/data-mapper/security/advisories)
Expand Down
1 change: 0 additions & 1 deletion examples/01-basic/nested-mapping/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { MappingConfiguration } from '../../src/interface';
import { Mapper } from '../../src';

class Employee {
Expand Down
2 changes: 1 addition & 1 deletion examples/02-advanced/error-handling/complex.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Mapper, MappingConfiguration } from '../../src';
import { Mapper } from '../../src';

class Country {
name?: string;
Expand Down
72 changes: 72 additions & 0 deletions src/core/Mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { getValueByPath, PathObject } from './utils';
* - Backward compatibility with existing code
* - Dynamic mapping scenarios where decorators can't be used
*
* ⚠️ **SECURITY NOTICE**: This mapper uses dynamic code generation (`new Function()`)
* for performance optimization. Mapping configurations MUST come from trusted sources only.
* Never pass user-controlled data as mapping configuration to prevent code injection attacks.
*
* See docs/DECORATOR_API.md for the recommended approach.
* See docs/MIGRATION_GUIDE.md for migration instructions.
*
Expand All @@ -37,6 +41,43 @@ export class Mapper<Source, Target> {
this.createCompiler = this.createCompiler.bind(this);
}

/**
* Creates a new Mapper instance with the specified configuration.
*
* ⚠️ **SECURITY WARNING**: The `mappingConfig` parameter MUST come from a trusted source.
* This mapper uses dynamic code generation for performance. Passing user-controlled data
* as mapping configuration can lead to arbitrary code execution vulnerabilities.
*
* **Safe usage examples**:
* ```typescript
* // βœ… Safe: Configuration defined by developer
* const mapper = Mapper.create({
* name: 'user.fullName',
* email: 'user.email'
* });
*
* // βœ… Safe: Configuration from trusted internal source
* const mapper = Mapper.create(TRUSTED_MAPPING_CONFIGS.userMapper);
* ```
*
* **Unsafe usage examples**:
* ```typescript
* // ❌ UNSAFE: User input as mapping config
* const userConfig = JSON.parse(request.body);
* const mapper = Mapper.create(userConfig); // DANGEROUS!
*
* // ❌ UNSAFE: External untrusted source
* const externalConfig = await fetch('untrusted-api.com/config');
* const mapper = Mapper.create(externalConfig); // DANGEROUS!
* ```
*
* @param mappingConfig - Mapping configuration (MUST be from trusted source)
* @param defaultValues - Optional default values for target properties
* @param config - Optional mapper configuration
* @returns A new Mapper instance
*
* @public
*/
public static create<Source, Target>(
mappingConfig: MappingConfiguration<Source, Target>,
defaultValues?: DefaultValues<Target>,
Expand Down Expand Up @@ -268,6 +309,36 @@ export class Mapper<Source, Target> {
.join('\n');
}

/**
* Compiles mapping configuration into an optimized function using dynamic code generation.
*
* ⚠️ **SECURITY WARNING**: This method uses `new Function()` for performance optimization.
* The mapping configuration MUST come from trusted sources only (developer-defined code).
* DO NOT pass user-controlled data as mapping configuration, as this could lead to
* arbitrary code execution.
*
* **Safe usage**: Mapping configuration is defined by developers at compile-time
* ```typescript
* const mapper = Mapper.create({
* name: 'user.fullName', // βœ… Safe: developer-defined
* age: 'user.age'
* });
* ```
*
* **Unsafe usage**: DO NOT DO THIS
* ```typescript
* const userConfig = JSON.parse(request.body); // ❌ UNSAFE: user input
* const mapper = Mapper.create(userConfig);
* ```
*
* @param mappingConfig - Mapping configuration (MUST be from trusted source)
* @param parentTarget - Optional parent target path for nested mappings
* @returns Compiled mapping function optimized for performance
*
* @internal
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
* @see https://owasp.org/www-community/attacks/Code_Injection
*/
private getCompiledFn(
mappingConfig: MappingConfiguration<Source, Target>,
parentTarget?: string,
Expand All @@ -278,6 +349,7 @@ export class Mapper<Source, Target> {
cache: { [key: string]: any },
) => MappingResult<Target> {
const body = this.getCompiledFnBody(mappingConfig, parentTarget, undefined, this.config);
// Using new Function for performance optimization - mapping config MUST be from trusted source
const func = new Function(`source, target, __errors, cache`, `${body}`);

return func as (
Expand Down
2 changes: 1 addition & 1 deletion tests/benchmarks/memory-leak.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Ensures that validation and transformation operations don't cause memory leaks
*/

import { describe, it, expect, beforeEach } from 'vitest';
import { describe, it, expect } from 'vitest';
import { validateSync } from '../../src/compat/class-validator';
import { plainToInstance } from '../../src/compat/class-transformer';
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,7 @@ import {
ValidateIf,
ValidateNested,
IsArray,
ArrayMinSize,
ArrayMaxSize,
MinLength,
MaxLength,
IsDateString,
IsUUID,
IsURL,
IsPort,
IsLatitude,
IsLongitude,
IsISO31661Alpha2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { describe, it, expect } from 'vitest';
import { validate, validateSync } from '../../../../src/compat/class-validator';
import { validate } from '../../../../src/compat/class-validator';
import {
IsOptional,
IsString,
Expand All @@ -18,7 +18,6 @@ import {
MinLength,
MaxLength,
Min,
Max,
} from '../../../../src/compat/class-validator/decorators';
import { Type } from '../../../../src/compat/class-transformer';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ import {
IsMobilePhone,
IsPostalCode,
IsMongoId,
IsJWT,
IsStrongPassword,
IsPort,
IsMACAddress,
IsBase64,
validate,
validateSync,
} from '../../../../src/compat/class-validator';

describe('class-validator-compat - High Priority Validators', () => {
Expand Down
2 changes: 0 additions & 2 deletions tests/unit/compat/class-validator/phase2-validators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import {
// Number
IsDivisibleBy,
IsDecimal,
IsPositive,
IsNegative,
// Date
MinDate,
MaxDate,
Expand Down
2 changes: 0 additions & 2 deletions tests/unit/integration/validation-and-mapping.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@ import {
IsBoolean,
IsArray,
MinLength,
MaxLength,
Min,
Max,
IsOptional,
IsDefined,
IsNotEmpty,
ValidateNested,
} from '../../../src/compat/class-validator';

Expand Down