Skip to content

Commit 3473ca5

Browse files
Reject Infinity supplied as Int or Float value (#1365)
1 parent 85b4f58 commit 3473ca5

File tree

5 files changed

+73
-22
lines changed

5 files changed

+73
-22
lines changed

src/jsutils/isInteger.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright (c) 2018-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
declare function isInteger(value: mixed): boolean %checks(typeof value ===
11+
'number');
12+
13+
/* eslint-disable no-redeclare */
14+
// $FlowFixMe workaround for: https://github.com/facebook/flow/issues/4441
15+
const isInteger =
16+
Number.isInteger ||
17+
function(value) {
18+
return (
19+
typeof value === 'number' &&
20+
isFinite(value) &&
21+
Math.floor(value) === value
22+
);
23+
};
24+
export default isInteger;

src/subscription/__tests__/subscribe-test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,7 @@ describe('Subscription Initialization Phase', () => {
474474
{
475475
message:
476476
'Variable "$priority" got invalid value "meow"; Expected ' +
477-
'type Int; Int cannot represent non 32-bit signed ' +
478-
'integer value: meow',
477+
'type Int; Int cannot represent non-integer value: meow',
479478
locations: [{ line: 2, column: 21 }],
480479
},
481480
],

src/type/__tests__/serialization-test.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,17 @@ describe('Type System: Scalar coercion', () => {
5656
'Int cannot represent non 32-bit signed integer value: -1e+100',
5757
);
5858
expect(() => GraphQLInt.serialize('one')).to.throw(
59-
'Int cannot represent non 32-bit signed integer value: one',
59+
'Int cannot represent non-integer value: one',
6060
);
6161
// Doesn't represent number
6262
expect(() => GraphQLInt.serialize('')).to.throw(
63-
'Int cannot represent non 32-bit signed integer value: (empty string)',
63+
'Int cannot represent non-integer value: (empty string)',
6464
);
6565
expect(() => GraphQLInt.serialize(NaN)).to.throw(
66-
'Int cannot represent non 32-bit signed integer value: NaN',
66+
'Int cannot represent non-integer value: NaN',
67+
);
68+
expect(() => GraphQLInt.serialize(Infinity)).to.throw(
69+
'Int cannot represent non-integer value: Infinity',
6770
);
6871
expect(() => GraphQLInt.serialize([5])).to.throw(
6972
'Int cannot represent an array value: [5]',
@@ -85,6 +88,9 @@ describe('Type System: Scalar coercion', () => {
8588
expect(() => GraphQLFloat.serialize(NaN)).to.throw(
8689
'Float cannot represent non numeric value: NaN',
8790
);
91+
expect(() => GraphQLFloat.serialize(Infinity)).to.throw(
92+
'Float cannot represent non numeric value: Infinity',
93+
);
8894
expect(() => GraphQLFloat.serialize('one')).to.throw(
8995
'Float cannot represent non numeric value: one',
9096
);

src/type/scalars.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import inspect from '../jsutils/inspect';
11+
import isInteger from '../jsutils/isInteger';
1112
import { GraphQLScalarType, isNamedType } from './definition';
1213
import { Kind } from '../language/kinds';
1314

@@ -27,22 +28,22 @@ function coerceInt(value: mixed): number {
2728
}
2829
if (value === '') {
2930
throw new TypeError(
30-
'Int cannot represent non 32-bit signed integer value: (empty string)',
31+
'Int cannot represent non-integer value: (empty string)',
3132
);
3233
}
3334
const num = Number(value);
34-
if (num !== num || num > MAX_INT || num < MIN_INT) {
35+
if (!isInteger(num)) {
3536
throw new TypeError(
36-
'Int cannot represent non 32-bit signed integer value: ' + inspect(value),
37+
'Int cannot represent non-integer value: ' + inspect(value),
3738
);
3839
}
39-
const int = Math.floor(num);
40-
if (int !== num) {
40+
41+
if (num > MAX_INT || num < MIN_INT) {
4142
throw new TypeError(
42-
'Int cannot represent non-integer value: ' + inspect(value),
43+
'Int cannot represent non 32-bit signed integer value: ' + inspect(value),
4344
);
4445
}
45-
return int;
46+
return num;
4647
}
4748

4849
export const GraphQLInt = new GraphQLScalarType({
@@ -75,7 +76,7 @@ function coerceFloat(value: mixed): number {
7576
);
7677
}
7778
const num = Number(value);
78-
if (num === num) {
79+
if (isFinite(num)) {
7980
return num;
8081
}
8182
throw new TypeError(

src/utilities/__tests__/coerceValue-test.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,31 +62,45 @@ describe('coerceValue', () => {
6262
expectValue(result).to.equal(null);
6363
});
6464

65-
it('returns a single error for empty value', () => {
65+
it('returns a single error for empty string as value', () => {
6666
const result = coerceValue('', GraphQLInt);
6767
expectErrors(result).to.deep.equal([
68-
'Expected type Int; Int cannot represent non 32-bit signed integer value: (empty string)',
68+
'Expected type Int; Int cannot represent non-integer value: (empty string)',
6969
]);
7070
});
7171

72-
it('returns error for float input as int', () => {
72+
it('returns a single error for 2^32 input as int', () => {
73+
const result = coerceValue(Math.pow(2, 32), GraphQLInt);
74+
expectErrors(result).to.deep.equal([
75+
'Expected type Int; Int cannot represent non 32-bit signed integer value: 4294967296',
76+
]);
77+
});
78+
79+
it('returns a single error for float input as int', () => {
7380
const result = coerceValue('1.5', GraphQLInt);
7481
expectErrors(result).to.deep.equal([
7582
'Expected type Int; Int cannot represent non-integer value: 1.5',
7683
]);
7784
});
7885

86+
it('returns a single error for Infinity input as int', () => {
87+
const result = coerceValue(Infinity, GraphQLInt);
88+
expectErrors(result).to.deep.equal([
89+
'Expected type Int; Int cannot represent non-integer value: Infinity',
90+
]);
91+
});
92+
7993
it('returns a single error for char input', () => {
8094
const result = coerceValue('a', GraphQLInt);
8195
expectErrors(result).to.deep.equal([
82-
'Expected type Int; Int cannot represent non 32-bit signed integer value: a',
96+
'Expected type Int; Int cannot represent non-integer value: a',
8397
]);
8498
});
8599

86100
it('returns a single error for char input', () => {
87101
const result = coerceValue('meow', GraphQLInt);
88102
expectErrors(result).to.deep.equal([
89-
'Expected type Int; Int cannot represent non 32-bit signed integer value: meow',
103+
'Expected type Int; Int cannot represent non-integer value: meow',
90104
]);
91105
});
92106
});
@@ -112,13 +126,20 @@ describe('coerceValue', () => {
112126
expectValue(result).to.equal(null);
113127
});
114128

115-
it('returns a single error for empty value', () => {
129+
it('returns a single error for empty string input', () => {
116130
const result = coerceValue('', GraphQLFloat);
117131
expectErrors(result).to.deep.equal([
118132
'Expected type Float; Float cannot represent non numeric value: (empty string)',
119133
]);
120134
});
121135

136+
it('returns a single error for Infinity input', () => {
137+
const result = coerceValue(Infinity, GraphQLFloat);
138+
expectErrors(result).to.deep.equal([
139+
'Expected type Float; Float cannot represent non numeric value: Infinity',
140+
]);
141+
});
142+
122143
it('returns a single error for char input', () => {
123144
const result = coerceValue('a', GraphQLFloat);
124145
expectErrors(result).to.deep.equal([
@@ -191,15 +212,15 @@ describe('coerceValue', () => {
191212
it('returns no error for an invalid field', () => {
192213
const result = coerceValue({ foo: 'abc' }, TestInputObject);
193214
expectErrors(result).to.deep.equal([
194-
'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc',
215+
'Expected type Int at value.foo; Int cannot represent non-integer value: abc',
195216
]);
196217
});
197218

198219
it('returns multiple errors for multiple invalid fields', () => {
199220
const result = coerceValue({ foo: 'abc', bar: 'def' }, TestInputObject);
200221
expectErrors(result).to.deep.equal([
201-
'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc',
202-
'Expected type Int at value.bar; Int cannot represent non 32-bit signed integer value: def',
222+
'Expected type Int at value.foo; Int cannot represent non-integer value: abc',
223+
'Expected type Int at value.bar; Int cannot represent non-integer value: def',
203224
]);
204225
});
205226

0 commit comments

Comments
 (0)