From bb04caa602d0488c7681018400b35b7cfad6086f Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 23 Jan 2025 17:57:08 +0100 Subject: [PATCH 1/4] correct --- src/audits/server.ts | 51 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/audits/server.ts b/src/audits/server.ts index c3708540..82595af8 100644 --- a/src/audits/server.ts +++ b/src/audits/server.ts @@ -76,7 +76,7 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { ), audit( '47DE', - 'SHOULD accept */* and use application/json for the content-type', + 'SHOULD accept */* and use application/graphql-response+json or application/json for the content-type', async () => { const res = await fetchFn(await getUrl(opts.url), { method: 'POST', @@ -87,12 +87,18 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { body: JSON.stringify({ query: '{ __typename }' }), }); ressert(res).status.toBe(200); - ressert(res).header('content-type').toContain('application/json'); + try { + ressert(res) + .header('content-type') + .toContain('application/graphql-response+json'); + } catch { + ressert(res).header('content-type').toContain('application/json'); + } }, ), audit( '80D8', - 'SHOULD assume application/json content-type when accept is missing', + 'SHOULD assume application/graphql-response+json or application/json content-type when accept is missing', async () => { const res = await fetchFn(await getUrl(opts.url), { method: 'POST', @@ -103,7 +109,13 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { }); ressert(res).status.toBe(200); - ressert(res).header('content-type').toContain('application/json'); + try { + ressert(res) + .header('content-type') + .toContain('application/graphql-response+json'); + } catch { + ressert(res).header('content-type').toContain('application/json'); + } }, ), audit('82A3', 'MUST use utf-8 encoding when responding', async () => { @@ -498,23 +510,46 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { ), ...['string', 0, false, ['array']].map((invalid, index) => audit( - `58B${index}`, // TODO: convert to MUST after watershed - `MAY use 400 status code on ${extendedTypeof( + `028${index}`, + `SHOULD use 4xx or 5xx status codes on ${extendedTypeof( invalid, - )} {extensions} parameter`, + )} {extensions} parameter when accepting application/graphql-response+json`, async () => { const res = await fetchFn(await getUrl(opts.url), { method: 'POST', headers: { 'content-type': 'application/json', + accept: 'application/graphql-response+json', }, body: JSON.stringify({ query: '{ __typename }', extensions: invalid, }), }); - ressert(res).status.toBe(400); + ressert(res).status.toBeBetween(400, 599); + }, + ), + ), + ...['string', 0, false, ['array']].map((invalid, index) => + audit( + `58B${index}`, + `MAY use 4xx or 5xx status codes on ${extendedTypeof( + invalid, + )} {extensions} parameter when accepting application/json`, + async () => { + const res = await fetchFn(await getUrl(opts.url), { + method: 'POST', + headers: { + 'content-type': 'application/json', + accept: 'application/json', + }, + body: JSON.stringify({ + query: '{ __typename }', + extensions: invalid, + }), + }); + ressert(res).status.toBeBetween(400, 599); }, ), ), From d46136c01305f0821549d5a927e63359a17013a4 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 23 Jan 2025 18:01:46 +0100 Subject: [PATCH 2/4] containeither --- src/audits/server.ts | 26 ++++++++++++-------------- src/audits/utils.ts | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/audits/server.ts b/src/audits/server.ts index 82595af8..2f6abfae 100644 --- a/src/audits/server.ts +++ b/src/audits/server.ts @@ -87,13 +87,12 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { body: JSON.stringify({ query: '{ __typename }' }), }); ressert(res).status.toBe(200); - try { - ressert(res) - .header('content-type') - .toContain('application/graphql-response+json'); - } catch { - ressert(res).header('content-type').toContain('application/json'); - } + ressert(res) + .header('content-type') + .toContainEither( + 'application/graphql-response+json', + 'application/json', + ); }, ), audit( @@ -109,13 +108,12 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { }); ressert(res).status.toBe(200); - try { - ressert(res) - .header('content-type') - .toContain('application/graphql-response+json'); - } catch { - ressert(res).header('content-type').toContain('application/json'); - } + ressert(res) + .header('content-type') + .toContainEither( + 'application/graphql-response+json', + 'application/json', + ); }, ), audit('82A3', 'MUST use utf-8 encoding when responding', async () => { diff --git a/src/audits/utils.ts b/src/audits/utils.ts index ed6f70be..652312f6 100644 --- a/src/audits/utils.ts +++ b/src/audits/utils.ts @@ -110,6 +110,22 @@ export function ressert(res: Response) { ); } }, + toContainEither( + thisPart: string, + thatPart: string, + ...otherParts: string[] + ) { + const parts = [thisPart, thatPart, ...otherParts]; + for (const part of parts) { + if (res.headers.get(key)?.includes(part)) { + return; + } + } + throw new AuditError( + res, + `Response header ${key} does not contain ${parts.join(' or ')}`, + ); + }, notToContain(part: string) { if (res.headers.get(key)?.includes(part)) { throw new AuditError( From 3f6118c80c8a1a65b6cbac9c331379601c1b0043 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 23 Jan 2025 18:04:44 +0100 Subject: [PATCH 3/4] may stuff --- src/audits/server.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/audits/server.ts b/src/audits/server.ts index 2f6abfae..6723b727 100644 --- a/src/audits/server.ts +++ b/src/audits/server.ts @@ -529,10 +529,32 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { }, ), ), + ...['string', 0, false, ['array']].map((invalid, index) => + audit( + `028${index}`, + `MAY use 4xx status codes on ${extendedTypeof( + invalid, + )} {extensions} parameter when accepting application/graphql-response+json`, + async () => { + const res = await fetchFn(await getUrl(opts.url), { + method: 'POST', + headers: { + 'content-type': 'application/json', + accept: 'application/graphql-response+json', + }, + body: JSON.stringify({ + query: '{ __typename }', + extensions: invalid, + }), + }); + ressert(res).status.toBeBetween(400, 499); + }, + ), + ), ...['string', 0, false, ['array']].map((invalid, index) => audit( `58B${index}`, - `MAY use 4xx or 5xx status codes on ${extendedTypeof( + `MAY use 4xx status codes on ${extendedTypeof( invalid, )} {extensions} parameter when accepting application/json`, async () => { @@ -547,7 +569,7 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { extensions: invalid, }), }); - ressert(res).status.toBeBetween(400, 599); + ressert(res).status.toBeBetween(400, 499); }, ), ), From 5bd0c020cda516295a2431453021bc57ff0fe150 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 23 Jan 2025 18:34:15 +0100 Subject: [PATCH 4/4] WIP --- src/audits/server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audits/server.ts b/src/audits/server.ts index 6723b727..bcd92ae5 100644 --- a/src/audits/server.ts +++ b/src/audits/server.ts @@ -796,7 +796,7 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { ), audit( '74FF', - 'SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json', + 'MAY use 4xx status codes on document validation failure when accepting application/graphql-response+json', async () => { const res = await fetchFn(await getUrl(opts.url), { method: 'POST', @@ -808,7 +808,7 @@ export function serverAudits(opts: ServerAuditOptions): Audit[] { query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist }), }); - ressert(res).status.toBe(400); + ressert(res).status.toBeBetween(400, 499); }, ), audit(