Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add bigint input validation #938

Merged
merged 3 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions integration/map-bigint-optional/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,15 @@ function createBaseMapBigInt_MapEntry(): MapBigInt_MapEntry {
export const MapBigInt_MapEntry = {
encode(message: MapBigInt_MapEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.key !== BigInt("0")) {
if (BigInt.asUintN(64, message.key) !== message.key) {
throw new Error("value provided for field message.key of type fixed64 too large");
}
writer.uint32(9).fixed64(message.key.toString());
}
if (message.value !== BigInt("0")) {
if (BigInt.asIntN(64, message.value) !== message.value) {
throw new Error("value provided for field message.value of type int64 too large");
}
writer.uint32(16).int64(message.value.toString());
}
if (message._unknownFields !== undefined) {
Expand Down
3 changes: 3 additions & 0 deletions integration/simple-long-bigint/google/protobuf/timestamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ function createBaseTimestamp(): Timestamp {
export const Timestamp = {
encode(message: Timestamp, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.seconds !== BigInt("0")) {
if (BigInt.asIntN(64, message.seconds) !== message.seconds) {
throw new Error("value provided for field message.seconds of type int64 too large");
}
writer.uint32(8).int64(message.seconds.toString());
}
if (message.nanos !== 0) {
Expand Down
6 changes: 6 additions & 0 deletions integration/simple-long-bigint/google/protobuf/wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ function createBaseInt64Value(): Int64Value {
export const Int64Value = {
encode(message: Int64Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.value !== BigInt("0")) {
if (BigInt.asIntN(64, message.value) !== message.value) {
throw new Error("value provided for field message.value of type int64 too large");
}
writer.uint32(8).int64(message.value.toString());
}
return writer;
Expand Down Expand Up @@ -272,6 +275,9 @@ function createBaseUInt64Value(): UInt64Value {
export const UInt64Value = {
encode(message: UInt64Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.value !== BigInt("0")) {
if (BigInt.asUintN(64, message.value) !== message.value) {
throw new Error("value provided for field message.value of type uint64 too large");
}
writer.uint32(8).uint64(message.value.toString());
}
return writer;
Expand Down
53 changes: 53 additions & 0 deletions integration/simple-long-bigint/numbers-long-string-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import PbNumbers = pbjs.Numbers;
import UInt64Value = google.protobuf.UInt64Value;
import PbTimestamp = google.protobuf.Timestamp;


// 18_446_744_073_709_551_615n
const MAX_UINT64 = BigInt("18446744073709551615")
// 9_223_372_036_854_775_807n
const MAX_INT64 = BigInt("9223372036854775807")

describe("number", () => {
it("generates types correctly", () => {
const simple: Numbers = {
Expand Down Expand Up @@ -168,4 +174,51 @@ describe("number", () => {
}
`);
});

it('throws error on too large bigints', () => {
const testCases = [
{
failValue: { int64: MAX_INT64 + BigInt("1") },
failMsg: "value provided for field message.int64 of type int64 too large",
passValue: { int64: MAX_INT64 },
},
{
failValue: { uint64: MAX_UINT64 + BigInt("1") },
failMsg: "value provided for field message.uint64 of type uint64 too large",
passValue: { uint64: MAX_UINT64 },
},
{
failValue: { sint64: MAX_INT64 + BigInt("1") },
failMsg: "value provided for field message.sint64 of type sint64 too large",
passValue: { sint64: MAX_INT64 },
},
{
failValue: { fixed64: MAX_UINT64 + BigInt("1") },
failMsg: "value provided for field message.fixed64 of type fixed64 too large",
passValue: { fixed64: MAX_UINT64 },
},
{
failValue: { sfixed64: MAX_INT64 + BigInt("1") },
failMsg: "value provided for field message.sfixed64 of type sfixed64 too large",
passValue: { sfixed64: MAX_INT64 },
},
{
failValue: { guint64: MAX_UINT64 + BigInt("1") },
failMsg: "value provided for field message.value of type uint64 too large",
passValue: { guint64: MAX_UINT64 },
},
{
failValue: { uint64s: [MAX_UINT64 + BigInt("1")] },
failMsg: "a value provided in array field uint64s of type uint64 is too large",
passValue: { uint64s: [MAX_UINT64] },
}
];

for (const testCase of testCases) {
const failValue = Numbers.fromPartial(testCase.failValue);
expect(() => { Numbers.encode(failValue) }).toThrow(testCase.failMsg);
const passValue = Numbers.fromPartial(testCase.passValue);
expect(() => Numbers.encode(passValue).finish()).not.toThrowError();
}
});
});
18 changes: 18 additions & 0 deletions integration/simple-long-bigint/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,30 +56,45 @@ export const Numbers = {
writer.uint32(24).int32(message.int32);
}
if (message.int64 !== BigInt("0")) {
if (BigInt.asIntN(64, message.int64) !== message.int64) {
throw new Error("value provided for field message.int64 of type int64 too large");
}
writer.uint32(32).int64(message.int64.toString());
}
if (message.uint32 !== 0) {
writer.uint32(40).uint32(message.uint32);
}
if (message.uint64 !== BigInt("0")) {
if (BigInt.asUintN(64, message.uint64) !== message.uint64) {
throw new Error("value provided for field message.uint64 of type uint64 too large");
}
writer.uint32(48).uint64(message.uint64.toString());
}
if (message.sint32 !== 0) {
writer.uint32(56).sint32(message.sint32);
}
if (message.sint64 !== BigInt("0")) {
if (BigInt.asIntN(64, message.sint64) !== message.sint64) {
throw new Error("value provided for field message.sint64 of type sint64 too large");
}
writer.uint32(64).sint64(message.sint64.toString());
}
if (message.fixed32 !== 0) {
writer.uint32(77).fixed32(message.fixed32);
}
if (message.fixed64 !== BigInt("0")) {
if (BigInt.asUintN(64, message.fixed64) !== message.fixed64) {
throw new Error("value provided for field message.fixed64 of type fixed64 too large");
}
writer.uint32(81).fixed64(message.fixed64.toString());
}
if (message.sfixed32 !== 0) {
writer.uint32(93).sfixed32(message.sfixed32);
}
if (message.sfixed64 !== BigInt("0")) {
if (BigInt.asIntN(64, message.sfixed64) !== message.sfixed64) {
throw new Error("value provided for field message.sfixed64 of type sfixed64 too large");
}
writer.uint32(97).sfixed64(message.sfixed64.toString());
}
if (message.guint64 !== undefined) {
Expand All @@ -90,6 +105,9 @@ export const Numbers = {
}
writer.uint32(122).fork();
for (const v of message.uint64s) {
if (BigInt.asUintN(64, v) !== v) {
throw new Error("a value provided in array field uint64s of type uint64 is too large");
}
writer.uint64(v.toString());
}
writer.ldelim();
Expand Down
56 changes: 54 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,24 @@ function getEncodeWriteSnippet(ctx: Context, field: FieldDescriptorProto): (plac
return (place) => code`writer.uint32(${tag}).${toReaderCall(field)}(${toNumber}(${place}))`;
} else if (isLong(field) && options.forceLong === LongOption.BIGINT) {
const tag = ((field.number << 3) | basicWireType(field.type)) >>> 0;
return (place) => code`writer.uint32(${tag}).${toReaderCall(field)}(${place}.toString())`;
const fieldType = toReaderCall(field);
switch (fieldType) {
case "int64":
case "sint64":
case "sfixed64":
return (place) => code`if (BigInt.asIntN(64, ${place}) !== ${place}) {
throw new Error('value provided for field ${place} of type ${fieldType} too large');
}
writer.uint32(${tag}).${toReaderCall(field)}(${place}.toString())`;
case "uint64":
case "fixed64":
return (place) => code`if (BigInt.asUintN(64, ${place}) !== ${place}) {
throw new Error('value provided for field ${place} of type ${fieldType} too large');
}
writer.uint32(${tag}).${toReaderCall(field)}(${place}.toString())`;
default:
throw new Error(`unexpected BigInt type: ${fieldType}`);
}
} else if (isScalar(field) || isEnum(field)) {
const tag = ((field.number << 3) | basicWireType(field.type)) >>> 0;
return (place) => code`writer.uint32(${tag}).${toReaderCall(field)}(${place})`;
Expand Down Expand Up @@ -1381,13 +1398,48 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP
// Ideally we'd reuse `writeSnippet` but it has tagging embedded inside of it.
const tag = ((field.number << 3) | 2) >>> 0;
const rhs = (x: string) => (isLong(field) && options.forceLong === LongOption.BIGINT ? `${x}.toString()` : x);
const listWriteSnippet = code`
let listWriteSnippet = code`
writer.uint32(${tag}).fork();
for (const v of message.${fieldName}) {
writer.${toReaderCall(field)}(${rhs("v")});
}
writer.ldelim();
`;

if (isLong(field) && options.forceLong === LongOption.BIGINT) {
const fieldType = toReaderCall(field);
switch (fieldType) {
case "int64":
case "sint64":
case "sfixed64":
listWriteSnippet = code`
writer.uint32(${tag}).fork();
for (const v of message.${fieldName}) {
if (BigInt.asIntN(64, v) !== v) {
throw new Error('a value provided in array field ${fieldName} of type ${fieldType} is too large');
}
writer.${toReaderCall(field)}(${rhs("v")});
}
writer.ldelim();
`;
break;
case "uint64":
case "fixed64":
listWriteSnippet = code`
writer.uint32(${tag}).fork();
for (const v of message.${fieldName}) {
if (BigInt.asUintN(64, v) !== v) {
throw new Error('a value provided in array field ${fieldName} of type ${fieldType} is too large');
}
writer.${toReaderCall(field)}(${rhs("v")});
}
writer.ldelim();
`;
break;
default:
throw new Error(`unexpected BigInt type: ${fieldType}`);
}
}
if (isOptional) {
chunks.push(code`
if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
Expand Down
Loading