Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: serialization and primitives validation (no. 1) #3312

Merged
merged 29 commits into from
May 14, 2023

Conversation

michaldziuba03
Copy link
Contributor

@michaldziuba03 michaldziuba03 commented May 1, 2023

What change does this PR introduce?

This is first PR in validation fix pull requests series.
It fixes some problems I mentioned in DMs to @scopsy :)

Affected modules (some of them only partially)

  • auth
  • integration
  • notification-template
  • execution-detail
  • change
  • invite
  • organization
  • subscribers

Changes:

  • add validation for pagination limit parameter
  • add primitives validation (@IsString, @IsEnum...) for affected modules to avoid data corruption and some other dangerous things.
  • add field selection for joined user in Change entity because API returns whole user object with sensitive data (password hash, reset token hash). Now it returns only necessary for UI data.
  • add @IsMongoId in some places, because API often returns HTTP Status: 500 when Mongo Id is expected but some random value is provided. During review pay attention to whether the right fields have been annotated as Mongo Ids.
  • replace inline @Query('someQueryParam') param: string with @Query() query: QueryDto to perform validation and improve Swagger documentation :) I fixed HTTP Parameter Pollution in affected modules.

image

image

HTTP Parameter Pollution. Keep in mind query param is not always string - it's possible to inject object or array.

  • create transformers.ts file in shared modules. It actually will be used in future PRs in the series.
  • create validators.ts file for custom class validators
  • move some dtos from organization to invites. Why? Because some dtos defined in organization module was ONLY used in invites module.
  • change some tests in integration module. In most cases I try to fix my solutions first without changing existing tests - but in some tests "EMAIL" was used for channel instead ChannelTypeEnum.EMAIL. I checked frontend and some other tests (they rely on enum) - I decided to accept only ChannelTypeEnum as is without transformations to lowercase etc.

Why was this change needed?

To improve security and prevent some crazy things with corrupted data to happen in the future.

Other information (Screenshots)

@scopsy
Copy link
Contributor

scopsy commented May 2, 2023

Amazing work! A small suggestion for future PR's is a one I learned from @LetItRock, is to create inline comments on the PR code diff to explain why a particular change is made. In the way you have written in the PR body, just makes it more contextual to the actual code change. Not a must, but just something I find makes easier to review bigger PR's 🚀

Copy link
Contributor

@djabarovgeorge djabarovgeorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like amazing work! left a small question for your review :)


export class ChangesRequestDto extends PaginationRequestDto(10, 100) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! :)

@UserSession() user: IJwtPayload,
@Body() body: { color: string; logo: string; fontColor: string; contentBackground: string; fontFamily: string }
) {
async updateBrandingDetails(@UserSession() user: IJwtPayload, @Body() body: UpdateBrandingDetailsDto) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥳

@@ -12,6 +12,8 @@ export class ChangeEntity {

_environmentId: EnvironmentId;

user: Pick<UserEntity, '_id' | 'firstName' | 'lastName' | 'profilePicture'>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add this one here only for the ChangeRepository.getList populate?
Maybe instead we can create a response type with it for getList, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - I will create type for this particular method

Comment on lines 4 to 7
export const IsBooleanAny = () =>
IsIn([true, false, 'true', 'false'], {
message: '$property must be a boolean value',
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test in order to check this one?

Copy link
Contributor Author

@michaldziuba03 michaldziuba03 May 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I made this validator because frontend sends "true"/"false" for some credentials fields - maybe we will change this behaviour to send only real boolean and use built-in @IsBoolean?

I can imagine scenario when we have something like this and someone will trust dto types:

class ExampleDto {
  @IsBooleanAny()
  isSample: boolean;
}

@Post()
createExample(@Body() body: ExampleDto) {
  if (body.isSample) {
    console.log(body.isSample) // 'false' is truthy value because it's string :D
    // do something
 }
}

Our dtos are not reliable. For credentials it works because I guess mongoose can handle it with schema.

So I would change frontend code and just add @IsBoolean validator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but for example in some e2e tests for integration - regular boolean is sent.

@scopsy
Copy link
Contributor

scopsy commented May 3, 2023

Looking good! Let's finish with @djabarovgeorge suggestions and we can merge this :)

@michaldziuba03
Copy link
Contributor Author

@scopsy @djabarovgeorge new changes are ready to review

Copy link
Contributor

@djabarovgeorge djabarovgeorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loved your work here, and left a small suggestion.

P.S. I was inspired a bit by you and created a @isnotempty validator in one of my tasks, once we merge this one I will move it to your validators folder :)

@@ -6,6 +6,10 @@ import { ChangeEntity, ChangeDBModel } from './change.entity';
import { Change } from './change.schema';
import { UserEntity } from '../user';

type ChangeEntityPopulated = ChangeEntity & {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we export it one level outside
as we have in environment types?
libs/dal/src/repositories/environment/types.ts

@michaldziuba03
Copy link
Contributor Author

@djabarovgeorge @scopsy I think it's ready to merge - check out last small changes I made.

@scopsy scopsy added this pull request to the merge queue May 14, 2023
Merged via the queue into novuhq:next with commit 1b1860f May 14, 2023
29 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants