Skip to content

Commit

Permalink
fix(Byte): update isHex validator (#543)
Browse files Browse the repository at this point in the history
* fix(Byte): replace validators with explicit type

The base64 and hex validators are not guaranteeed to work.
There are some false positive cases, so instead add a type header to
the string, so a user can specify what the encoding of the string is.

* fix(Byte): update hex validator

There were cases where the hex regex validator would return true
for a value that was acutally base64 encoded. Added tests for this
case and updated the validator.
  • Loading branch information
rufman authored Nov 23, 2020
1 parent bc0b06d commit e0600de
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 4 deletions.
24 changes: 20 additions & 4 deletions src/scalars/Byte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,26 @@ import {
print,
} from 'graphql';

const base64Validator = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
const hexValidator = /(0x|0X)?[a-fA-F0-9]+$/;

type BufferJson = { type: 'Buffer'; data: number[] };
const base64Validator = /^(?:[A-Za-z0-9+]{4})*(?:[A-Za-z0-9+]{2}==|[A-Za-z0-9+]{3}=)?$/;
function hexValidator(value: string) {
// For larger strings, we run into issues with MAX_SAFE_INTEGER, so split the string
// into smaller pieces to avoid this issue.
if (value.length > 8) {
let parsedString = '';
for (
let startIndex = 0, endIndex = 8;
startIndex < value.length;
startIndex += 8, endIndex += 8
) {
parsedString += parseInt(value.slice(startIndex, endIndex), 16).toString(
16,
);
}
return parsedString === value;
}
return parseInt(value, 16).toString(16) === value;
}

function validate(value: Buffer | string | BufferJson) {
if (typeof value !== 'string' && !(value instanceof global.Buffer)) {
Expand All @@ -20,7 +36,7 @@ function validate(value: Buffer | string | BufferJson) {
}
if (typeof value === 'string') {
const isBase64 = base64Validator.test(value);
const isHex = hexValidator.test(value);
const isHex = hexValidator(value);
if (!isBase64 && !isHex) {
throw new TypeError(
`Value is not a valid base64 or hex encoded string: ${JSON.stringify(
Expand Down
17 changes: 17 additions & 0 deletions tests/Byte.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const base64String = byte.toString('base64');
const hexString = byte.toString('hex');
const notBase64 = 'RG9kZ2VycyBSdWxlIQ=';
const notHex = '446f64676572732052756c65z';
const looksLikeBase64 = 'c40473746174';
const looksLikeBase64Buffer = Buffer.from(looksLikeBase64, 'hex');
const looksLikeHex = 'xARzdGF0';
const looksLikeHexBuffer = Buffer.from(looksLikeHex, 'base64');
const notByte = 1;

function createBufferObject(type: string, values: ValueNode[]) {
Expand Down Expand Up @@ -125,3 +129,16 @@ describe.each<[string, Buffer | string, string | number]>([
});
});
});

describe.each<[string, string, Buffer]>([
['Hex String desguised as base64', looksLikeBase64, looksLikeBase64Buffer],
['Base64 String desguised as hex', looksLikeHex, looksLikeHexBuffer],
])('Byte string edge cases', (testType, encodedValue, decodedValue) => {
test(`serialize (${testType})`, () => {
expect(GraphQLByte.serialize(encodedValue)).toEqual(decodedValue);
});

test(`parseValue (${testType})`, () => {
expect(GraphQLByte.parseValue(encodedValue)).toEqual(decodedValue);
});
});

0 comments on commit e0600de

Please sign in to comment.