From 887a9a9ceb34ad37d4dda6f3f5744bf2cc2bcd6d Mon Sep 17 00:00:00 2001 From: Danil Trapeznikov Date: Wed, 28 Feb 2024 16:02:30 +0700 Subject: [PATCH] feat(aliases.spec.ts): add tests for handling aliases in GraphQL queries to ensure proper query merging and filtering based on allowed queries fix(index.ts): add logging to output queryMap for debugging purposes --- src/__tests__/aliases.spec.ts | 146 ++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 2 files changed, 147 insertions(+) create mode 100644 src/__tests__/aliases.spec.ts diff --git a/src/__tests__/aliases.spec.ts b/src/__tests__/aliases.spec.ts new file mode 100644 index 0000000..8e15618 --- /dev/null +++ b/src/__tests__/aliases.spec.ts @@ -0,0 +1,146 @@ +import { getAllowedQueryForRequest } from '../get-allowed-query'; +import { mergeQueries } from '../merge'; + +const allowedQueries = { + 'FindMyTalentJobApplications.findJobApplications': `query FindMyTalentJobApplications { + data: findJobApplications { + id + createdAt + deletedAt + jobAd { + id + location + title + publisherCompany { + name + } + workMode + } + } + }`, + 'FindMyCompanyTalentJobApplications.findJobApplications': `query FindMyCompanyTalentJobApplications($where: TalentJobApplicationWhereInput, $orderBy: [TalentJobApplicationOrderByWithRelationInput!]) { + data: findJobApplications(where: $where, orderBy: $orderBy) { + createdAt + id + jobAd { + title + } + talentProfile { + profileName + } + } + }`, +}; + +describe('aliases', () => { + test('FindMyTalentJobApplications should handle aliases (request talentProfile when it is not allowed)', () => { + const requestQuery = `query FindMyTalentJobApplications { + data: findJobApplications { + id + createdAt + deletedAt + jobAd { + id + location + title + publisherCompany { + name + } + workMode + } + talentProfile { + profileName + } + } + }`; + + const expected = `query FindMyTalentJobApplications { + data: findJobApplications { + id + createdAt + deletedAt + jobAd { + id + location + title + publisherCompany { + name + } + workMode + } + } +}`; + const allowedQuery = getAllowedQueryForRequest( + requestQuery, + allowedQueries + ); + expect(mergeQueries(requestQuery, allowedQuery)).toBe(expected); + }); + + test('FindMyCompanyTalentJobApplications should handle aliases2 (request workMode when it is not allowed)', () => { + const requestQuery = `query FindMyCompanyTalentJobApplications($where: TalentJobApplicationWhereInput, $orderBy: [TalentJobApplicationOrderByWithRelationInput!]) { + data: findJobApplications(where: $where, orderBy: $orderBy) { + createdAt + id + jobAd { + title + __typename + } + talentProfile { + profileName + __typename + } + workMode + __typename + } + }`; + const expected = `query FindMyCompanyTalentJobApplications($where: TalentJobApplicationWhereInput, $orderBy: [TalentJobApplicationOrderByWithRelationInput!]) { + data: findJobApplications(where: $where, orderBy: $orderBy) { + createdAt + id + jobAd { + title + } + talentProfile { + profileName + } + } +}`; + const allowedQuery = getAllowedQueryForRequest( + requestQuery, + allowedQueries + ); + console.log('allowedQuery', allowedQuery); + expect(mergeQueries(requestQuery, allowedQuery)).toBe(expected); + }); + + test('Exploit with Aliased Fields to bypass restrictions', () => { + const requestQuery = `query FindMyTalentJobApplications { + data: findJobApplications { + id + jobAd { + id + location + secretTitle: title + workMode + } + } + }`; + const expected = `query FindMyTalentJobApplications { + data: findJobApplications { + id + jobAd { + id + location + secretTitle: title + workMode + } + } +}`; // 'secretTitle' alias for 'title' is allowed since 'title' is allowed + const allowedQuery = getAllowedQueryForRequest( + requestQuery, + allowedQueries + ); + expect(mergeQueries(requestQuery, allowedQuery)).toBe(expected); + }); +}); diff --git a/src/index.ts b/src/index.ts index 7d08df4..b6909f7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -107,6 +107,7 @@ export class GraphQLQueryPurifier { const key = `${operationName}.${firstFieldName}`.trim(); this.queryMap[key] = content; } + console.log('this.queryMap', this.queryMap); }); }