• 02-Jan-2023
Lightrun Team
Author Lightrun Team
Share
This is a glossary of all the common issues in Typestack - Class Validator

Troubleshooting Common Issues in Typestack – Class Validator

Lightrun Team
Lightrun Team
02-Jan-2023

Project Description

 

“TypeScript – Class Validator” is a library for validating the properties of a TypeScript class, based on decorators that you can add to the class and its members. These decorators can specify validation rules, such as the type of a property, or a minimum or maximum value for a number. The library provides a set of built-in validators that you can use, or you can define your own custom validators.

To use “TypeScript – Class Validator”, you would first install it as a dependency in your project. Then, you would define a TypeScript class with decorators to specify the validation rules for its properties.

Finally, you would call a method provided by the library to validate an instance of the class and check whether it meets the specified rules.

 

Troubleshooting Typestack – Class Validator with the Lightrun Developer Observability Platform

 

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.
  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

The most common issues for Typestack – Class Validator are:

 

How to describe validators for two possible types: array or string?

 

Try a signature like:

@IsType(Array<(val: any) => boolean>)
@IsType([
 val => typeof val == 'string',
 val => typeof val == 'boolean',
])
private readonly foo: boolean | string;

 

How to customize validation messages globally?

 

In “TypeScript – Class Validator”, you can customize the error messages that are displayed when validation fails by using the validateOrReject function and providing your own custom messages as the second argument. This function returns a rejected promise if validation fails, along with an array of validation errors that contain your custom messages.

Here is an example of how you might customize the error messages globally for a User class:

import { Max, Min, IsInt, IsEmail, Matches, IsString } from 'class-validator';

export class User {
  @IsString()
  firstName: string;

  @IsString()
  lastName: string;

  @IsInt()
  @Min(0)
  @Max(150)
  age: number;

  @IsEmail()
  email: string;

  @Matches(/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/)
  password: string;
}

const user = new User();
user.firstName = 'John';
user.lastName = 'Doe';
user.age = 25;
user.email = 'john.doe@example.com';
user.password = 'P@ssw0rd';

import { validateOrReject } from 'class-validator';

const errorMessages = {
  firstName: 'First name is required',
  lastName: 'Last name is required',
  age: 'Age must be a positive integer between 0 and 150',
  email: 'Email must be a valid email address',
  password: 'Password must be at least 8 characters long and contain at least one letter and one number'
}

try {
  await validateOrReject(user, { validationError: { target: false, value: false }, messages: errorMessages });
  console.log('Validation succeeded');
} catch (errors) {
  console.log('Validation failed');
  console.log(errors);
}

In this example, the errorMessages object defines custom error messages for each property of the User class. The validateOrReject function is called with the errorMessages object as the second argument, and it will use these messages if validation fails. If validation succeeds, the validateOrReject function will resolve and the message “Validation succeeded” will be logged to the console. If validation fails, the function will reject with an array of validation errors containing the custom error messages, and these errors will be logged to the console.

You can also customize the error messages for a specific property by providing a message option in the decorator for that property. For example:

import { Max, Min, IsInt, IsEmail, Matches, IsString } from 'class-validator';

export class User {
  @IsString()
  firstName: string;

  @IsString()
  lastName: string;

  @IsInt()
  @Min(0)
  @Max(150, { message: 'Age must be between 0 and 150' })
  age: number;

  @IsEmail()
  email: string;

  @Matches(/^(?=.*[A-Za-z])(?=.*\d)[A-

 

feat: add decorator to check if two properties match

 

After considerable investigation and analysis,  a viable resolution for the control in question was discovered. This solution can be found on StackOverflowpost along with additional details.

user-create.dto.ts

export class UserCreateDto {
   @IsString()
   @IsNotEmpty()
   firstName: string;

   @IsEmail()
   emailAddress: string;

   @Match('emailAddress')
   @IsEmail()
   emailAddressConfirm: string;
}

match.decorator.ts

import {registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface} from 'class-validator';

export function Match(property: string, validationOptions?: ValidationOptions) {
    return (object: any, propertyName: string) => {
        registerDecorator({
            target: object.constructor,
            propertyName,
            options: validationOptions,
            constraints: [property],
            validator: MatchConstraint,
        });
    };
}

@ValidatorConstraint({name: 'Match'})
export class MatchConstraint implements ValidatorConstraintInterface {

    validate(value: any, args: ValidationArguments) {
        const [relatedPropertyName] = args.constraints;
        const relatedValue = (args.object as any)[relatedPropertyName];
        return value === relatedValue;
    }

}

 

Is it possible to validate if only one of a group of properties are defined?

 

It is possible to unite two decorators for successful results.

export function IncompatableWith(incompatibleSiblings: string[]) {
  const notSibling = IsNotSiblingOf(incompatibleSiblings);
  const validateIf = ValidateIf(
    incompatibleSiblingsNotPresent(incompatibleSiblings)
  );
  return function(target: any, key: string) {
    notSibling(target, key);
    validateIf(target, key);
  };
}
class UpdateUserDTO {

  @IncompatableWith(['deleted'])
  @IsString()
  readonly status: string;
  
  @IncompatableWith(['status'])
  @IsBoolean()
  readonly deleted: boolean;

  @IsString()
  readonly name: string;

 

Problem with nested object validation

 

While not ideal, employing IsNotEmptyObject or IsObject was a viable solution to the issue of arrays; albeit one that may lead to other errors. Nevertheless, it proved satisfactory for some purposes.

  @IsDefined()
  @IsNotEmptyObject() / @IsObject()
  @ValidateNested()
  @Type(() => A)
  public nested!: A;

 

Flag for @IsDateString() to validate date without time

 

You can use the full-date flag with the @IsDateString() decorator from the class-validator library to validate that a string is in the format of a full-date as defined by the ISO 8601 standard. This will allow you to validate that the string is a valid date, but does not contain a time component.

Here’s an example of how you can use the @IsDateString decorator with the full-date flag:

import { IsDateString } from 'class-validator';

class MyClass {
  @IsDateString({ message: 'Invalid date format', full-date: true })
  date: string;
}

This will validate that the date field is a string in the format of a full-date as defined by the ISO 8601 standard, which is YYYY-MM-DD. If the string does not match this format, the validation will fail and the specified error message will be returned.

 

documentation: default settings for class-validator allows arbitrary bypass vulnerability

 

It is generally considered a security vulnerability if it is possible to bypass validation rules by providing arbitrary input to an application. In “TypeScript – Class Validator”, the default behavior is to allow any input, including values that do not meet the validation rules specified by the decorators. This means that if you do not configure the library properly, it may be possible for an attacker to bypass the validation and provide arbitrary input to your application. To prevent this vulnerability, you should configure the library to disallow arbitrary input by setting the forbidUnknownValues option to true when calling the validate or validateOrReject function. This will cause the function to reject any input that does not meet the validation rules specified by the decorators. Here is an example of how you might configure “TypeScript – Class Validator” to disallow arbitrary input:

import { validateOrReject } from 'class-validator';

const user = new User();
user.firstName = 'John';
user.lastName = 'Doe';
user.age = 25;
user.email = 'john.doe@example.com';
user.password = 'P@ssw0rd';

try {
  await validateOrReject(user, { forbidUnknownValues: true });
  console.log('Validation succeeded');
} catch (errors) {
  console.log('Validation failed');
  console.log(errors);
}

In this example, the validateOrReject function is called with the forbidUnknownValues option set to true. This will cause the function to reject any input that does not meet the validation rules specified by the decorators, effectively preventing an attacker from bypassing the validation and providing arbitrary input to the application.

It is important to note that setting the forbidUnknownValues option to true may cause the validate or validateOrReject function to reject valid input if the input contains properties that are not covered by the validation rules. To avoid this issue, you should make sure that your validation rules cover all properties of the input object, or use the whitelist option to specify which properties should be allowed.

 

More issues from Typestack repos

 

Troubleshooting typestack-routing-controllers

 

Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications. It’s a registration form away.

Get Lightrun

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.