File size: 4,511 Bytes
cfbbc1a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { __decorate, __metadata, __param } from "tslib";
import { types } from 'util';
import { Injectable } from '../decorators/core/injectable.decorator.js';
import { Optional } from '../decorators/core/optional.decorator.js';
import { HttpStatus } from '../enums/http-status.enum.js';
import { HttpErrorByCode, } from '../utils/http-error-by-code.util.js';
/**
 * Built-in JavaScript types that should be excluded from prototype stripping
 * to avoid conflicts with test frameworks like Jest's useFakeTimers
 */
const BUILT_IN_TYPES = [Date, RegExp, Error, Map, Set, WeakMap, WeakSet];
/**
 * Defines the built-in StandardSchemaValidation Pipe.
 *
 * Uses a standard schema object (conforming to the Standard Schema spec)
 * attached to the parameter metadata to validate incoming values.
 *
 * @see [Standard Schema](https://github.com/standard-schema/standard-schema)
 *
 * @publicApi
 */
let StandardSchemaValidationPipe = class StandardSchemaValidationPipe {
    options;
    isTransformEnabled;
    validateCustomDecorators;
    validateOptions;
    exceptionFactory;
    constructor(options) {
        this.options = options;
        const { transform = true, validateCustomDecorators = false, validateOptions, exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST, } = options || {};
        this.isTransformEnabled = transform;
        this.validateCustomDecorators = validateCustomDecorators;
        this.validateOptions = validateOptions;
        this.exceptionFactory =
            exceptionFactory ||
                (issues => {
                    const messages = issues.map(issue => issue.message);
                    return new HttpErrorByCode[errorHttpStatusCode](messages);
                });
    }
    /**
     * Method that validates the incoming value against the standard schema
     * provided in the parameter metadata.
     *
     * @param value currently processed route argument
     * @param metadata contains metadata about the currently processed route argument
     */
    async transform(value, metadata) {
        const schema = metadata.schema;
        if (!schema || !this.toValidate(metadata)) {
            return value;
        }
        this.stripProtoKeys(value);
        const result = await this.validate(value, schema, this.validateOptions);
        if (result.issues) {
            throw this.exceptionFactory(result.issues);
        }
        return this.isTransformEnabled ? result.value : value;
    }
    /**
     * Determines whether validation should be performed for the given metadata.
     * Skips validation for custom decorators unless `validateCustomDecorators` is enabled.
     *
     * @param metadata contains metadata about the currently processed route argument
     * @returns `true` if validation should be performed
     */
    toValidate(metadata) {
        const { type } = metadata;
        if (type === 'custom' && !this.validateCustomDecorators) {
            return false;
        }
        return true;
    }
    /**
     * Validates a value against a standard schema.
     * Can be overridden to customize validation behavior.
     *
     * @param value The value to validate
     * @param schema The standard schema to validate against
     * @param options Optional options forwarded to the schema's validate method
     * @returns The validation result
     */
    validate(value, schema, options) {
        return schema['~standard'].validate(value, options);
    }
    /**
     * Strips dangerous prototype pollution keys from an object.
     */
    stripProtoKeys(value) {
        if (value == null ||
            typeof value !== 'object' ||
            types.isTypedArray(value)) {
            return;
        }
        if (BUILT_IN_TYPES.some(type => value instanceof type)) {
            return;
        }
        if (Array.isArray(value)) {
            for (const v of value) {
                this.stripProtoKeys(v);
            }
            return;
        }
        delete value.__proto__;
        delete value.prototype;
        const constructorType = value?.constructor;
        if (constructorType && !BUILT_IN_TYPES.includes(constructorType)) {
            delete value.constructor;
        }
        for (const key in value) {
            this.stripProtoKeys(value[key]);
        }
    }
};
StandardSchemaValidationPipe = __decorate([
    Injectable(),
    __param(0, Optional()),
    __metadata("design:paramtypes", [Object])
], StandardSchemaValidationPipe);
export { StandardSchemaValidationPipe };