From 12bc076189effea8eeeee76eee99449a6ea9be20 Mon Sep 17 00:00:00 2001 From: AlejandroAkbal <37181533+AlejandroAkbal@users.noreply.github.com> Date: Fri, 13 Mar 2026 04:19:56 -0700 Subject: [PATCH 1/5] fix: decode encoded booru tags --- src/booru/dto/booru-queries.dto.spec.ts | 22 ++++++++++++++++++++++ src/booru/dto/booru-queries.dto.ts | 7 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/booru/dto/booru-queries.dto.spec.ts diff --git a/src/booru/dto/booru-queries.dto.spec.ts b/src/booru/dto/booru-queries.dto.spec.ts new file mode 100644 index 0000000..6006467 --- /dev/null +++ b/src/booru/dto/booru-queries.dto.spec.ts @@ -0,0 +1,22 @@ +import { plainToInstance } from 'class-transformer' +import { booruQueryValuesPostsDTO } from './booru-queries.dto' + +describe('booruQueryValuesPostsDTO', () => { + describe('tags transform', () => { + it('should decode URL-encoded ampersands in tags', () => { + const dto = plainToInstance(booruQueryValuesPostsDTO, { + tags: 'panty_%26_stocking_with_garterbelt' + }) + + expect(dto.tags).toEqual(['panty_&_stocking_with_garterbelt']) + }) + + it('should split pipe-separated tags and decode each one', () => { + const dto = plainToInstance(booruQueryValuesPostsDTO, { + tags: 'panty_%26_stocking_with_garterbelt|rating%3Asafe' + }) + + expect(dto.tags).toEqual(['panty_&_stocking_with_garterbelt', 'rating:safe']) + }) + }) +}) diff --git a/src/booru/dto/booru-queries.dto.ts b/src/booru/dto/booru-queries.dto.ts index 4a4bd12..3865101 100644 --- a/src/booru/dto/booru-queries.dto.ts +++ b/src/booru/dto/booru-queries.dto.ts @@ -180,7 +180,12 @@ export class booruQueryValuesPostsDTO extends booruQueriesDTO { @IsArray() @ArrayNotEmpty() @ArrayNotContains(['']) - @Transform(({ value }) => value.trim().split('|')) + @Transform(({ value }) => + value + .trim() + .split('|') + .map((tag) => decodeURIComponent(tag)) + ) @IsOptional() readonly tags: IBooruQueryValues['posts']['tags'] From 706652a24e8db73663ec343432ba4f53a72d3845 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:18:21 +0000 Subject: [PATCH 2/5] fix: apply CodeRabbit auto-fixes Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- src/booru/dto/booru-queries.dto.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/booru/dto/booru-queries.dto.ts b/src/booru/dto/booru-queries.dto.ts index 3865101..61d02a2 100644 --- a/src/booru/dto/booru-queries.dto.ts +++ b/src/booru/dto/booru-queries.dto.ts @@ -18,6 +18,7 @@ import { Min } from 'class-validator' import { Transform } from 'class-transformer' +import { BadRequestException } from '@nestjs/common' abstract class booruEndpointsDTO { @IsFQDN() @@ -184,7 +185,13 @@ export class booruQueryValuesPostsDTO extends booruQueriesDTO { value .trim() .split('|') - .map((tag) => decodeURIComponent(tag)) + .map((tag) => { + try { + return decodeURIComponent(tag) + } catch (error) { + throw new BadRequestException('Invalid tag encoding') + } + }) ) @IsOptional() readonly tags: IBooruQueryValues['posts']['tags'] @@ -244,4 +251,4 @@ export class booruQueryValuesTagsDTO extends booruQueriesDTO { @IsNotEmpty() @IsOptional() readonly order: IBooruQueryValues['tags']['order'] -} +} \ No newline at end of file From a93b66c3b3dbfbca1221e15f7e066959648b2fe5 Mon Sep 17 00:00:00 2001 From: AlejandroAkbal <37181533+AlejandroAkbal@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:21:15 -0700 Subject: [PATCH 3/5] fix: guard booru tag decoding --- src/booru/dto/booru-queries.dto.spec.ts | 23 +++++++++++++++++++++++ src/booru/dto/booru-queries.dto.ts | 8 ++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/booru/dto/booru-queries.dto.spec.ts b/src/booru/dto/booru-queries.dto.spec.ts index 6006467..c32a6bf 100644 --- a/src/booru/dto/booru-queries.dto.spec.ts +++ b/src/booru/dto/booru-queries.dto.spec.ts @@ -1,3 +1,4 @@ +import { BadRequestException } from '@nestjs/common' import { plainToInstance } from 'class-transformer' import { booruQueryValuesPostsDTO } from './booru-queries.dto' @@ -18,5 +19,27 @@ describe('booruQueryValuesPostsDTO', () => { expect(dto.tags).toEqual(['panty_&_stocking_with_garterbelt', 'rating:safe']) }) + + it('should keep non-encoded percent tags unchanged', () => { + const dto = plainToInstance(booruQueryValuesPostsDTO, { + tags: '100%_real' + }) + + expect(dto.tags).toEqual(['100%_real']) + }) + + it('should throw BadRequestException when encoded tag decoding fails', () => { + expect(() => + plainToInstance(booruQueryValuesPostsDTO, { + tags: 'bad%25%' + }) + ).toThrow(BadRequestException) + + expect(() => + plainToInstance(booruQueryValuesPostsDTO, { + tags: 'bad%25%' + }) + ).toThrow('Invalid tag encoding') + }) }) }) diff --git a/src/booru/dto/booru-queries.dto.ts b/src/booru/dto/booru-queries.dto.ts index 61d02a2..e1a38f3 100644 --- a/src/booru/dto/booru-queries.dto.ts +++ b/src/booru/dto/booru-queries.dto.ts @@ -186,9 +186,13 @@ export class booruQueryValuesPostsDTO extends booruQueriesDTO { .trim() .split('|') .map((tag) => { + if (!/%[0-9A-Fa-f]{2}/.test(tag)) { + return tag + } + try { return decodeURIComponent(tag) - } catch (error) { + } catch { throw new BadRequestException('Invalid tag encoding') } }) @@ -251,4 +255,4 @@ export class booruQueryValuesTagsDTO extends booruQueriesDTO { @IsNotEmpty() @IsOptional() readonly order: IBooruQueryValues['tags']['order'] -} \ No newline at end of file +} From da827fe402c72a3f9e7c84f614711261320f0d94 Mon Sep 17 00:00:00 2001 From: AlejandroAkbal <37181533+AlejandroAkbal@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:36:42 -0700 Subject: [PATCH 4/5] fix: normalize booru tag inputs --- src/booru/dto/booru-queries.dto.spec.ts | 20 ++++++++++++++++++++ src/booru/dto/booru-queries.dto.ts | 14 +++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/booru/dto/booru-queries.dto.spec.ts b/src/booru/dto/booru-queries.dto.spec.ts index c32a6bf..9590fae 100644 --- a/src/booru/dto/booru-queries.dto.spec.ts +++ b/src/booru/dto/booru-queries.dto.spec.ts @@ -20,6 +20,26 @@ describe('booruQueryValuesPostsDTO', () => { expect(dto.tags).toEqual(['panty_&_stocking_with_garterbelt', 'rating:safe']) }) + it('should normalize array tag inputs and keep tag array shape', () => { + const dto = plainToInstance(booruQueryValuesPostsDTO, { + tags: ['panty_%26_stocking_with_garterbelt|rating%3Asafe', 'score%3A%3E100'] + }) + + expect(dto.tags).toEqual([ + 'panty_&_stocking_with_garterbelt', + 'rating:safe', + 'score:>100' + ]) + }) + + it('should normalize non-string tag input without throwing', () => { + const dto = plainToInstance(booruQueryValuesPostsDTO, { + tags: 123 + }) + + expect(dto.tags).toEqual(['123']) + }) + it('should keep non-encoded percent tags unchanged', () => { const dto = plainToInstance(booruQueryValuesPostsDTO, { tags: '100%_real' diff --git a/src/booru/dto/booru-queries.dto.ts b/src/booru/dto/booru-queries.dto.ts index e1a38f3..3acc88e 100644 --- a/src/booru/dto/booru-queries.dto.ts +++ b/src/booru/dto/booru-queries.dto.ts @@ -181,10 +181,14 @@ export class booruQueryValuesPostsDTO extends booruQueriesDTO { @IsArray() @ArrayNotEmpty() @ArrayNotContains(['']) - @Transform(({ value }) => - value - .trim() - .split('|') + @Transform(({ value }) => { + if (value === undefined || value === null) { + return value + } + + return (Array.isArray(value) ? value : [value]) + .map((tag) => (typeof tag === 'string' ? tag : String(tag))) + .flatMap((tag) => tag.trim().split('|')) .map((tag) => { if (!/%[0-9A-Fa-f]{2}/.test(tag)) { return tag @@ -196,7 +200,7 @@ export class booruQueryValuesPostsDTO extends booruQueriesDTO { throw new BadRequestException('Invalid tag encoding') } }) - ) + }) @IsOptional() readonly tags: IBooruQueryValues['posts']['tags'] From 0707a3b477e7ccdf9f55f8ef3497f1d2f0508226 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 18:53:35 +0000 Subject: [PATCH 5/5] fix: apply CodeRabbit auto-fixes Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- src/booru/dto/booru-queries.dto.spec.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/booru/dto/booru-queries.dto.spec.ts b/src/booru/dto/booru-queries.dto.spec.ts index 9590fae..9722383 100644 --- a/src/booru/dto/booru-queries.dto.spec.ts +++ b/src/booru/dto/booru-queries.dto.spec.ts @@ -61,5 +61,19 @@ describe('booruQueryValuesPostsDTO', () => { }) ).toThrow('Invalid tag encoding') }) + + it('should return undefined when tags is undefined', () => { + const dto = plainToInstance(booruQueryValuesPostsDTO, {}) + + expect(dto.tags).toBeUndefined() + }) + + it('should return null when tags is null', () => { + const dto = plainToInstance(booruQueryValuesPostsDTO, { + tags: null + }) + + expect(dto.tags).toBeNull() + }) }) -}) +}) \ No newline at end of file