THRIFT-5855: Add nodejs fuzzers
Add fuzzers for nodejs support, to improve the reliability/robustness of the implementation
diff --git a/lib/nodejs/test/fuzz/fuzz_common.js b/lib/nodejs/test/fuzz/fuzz_common.js
new file mode 100644
index 0000000..1eb5f79
--- /dev/null
+++ b/lib/nodejs/test/fuzz/fuzz_common.js
@@ -0,0 +1,107 @@
+const thrift = require("../../lib/thrift");
+const FuzzTest = require("./gen-nodejs/FuzzTestNoUuid_types");
+// const { FuzzedDataProvider } = require("@jazzer.js/core");
+
+/**
+ * Creates a parser fuzzer function for a specific protocol
+ * @param {Function} protocolFactory - The Thrift protocol factory function
+ * @param {boolean} [readMessageBegin=false] - Whether to call readMessageBegin before reading the test instance
+ * This is needed for protocols that do not support parsing just a struct, such as TJSONProtocol.
+ * @returns {Function} A fuzzer function that can be used with Jazzer.js
+ */
+function createParserFuzzer(protocolFactory, readMessageBegin = false) {
+ return function fuzz(data) {
+ if (data.length < 2) {
+ return;
+ }
+
+ try {
+ // Set up transport with input data
+ const transport = new thrift.TFramedTransport(data);
+ const protocol = protocolFactory(transport);
+ const testInstance = new FuzzTest.FuzzTest();
+ if (readMessageBegin) {
+ protocol.readMessageBegin();
+ }
+ testInstance[Symbol.for("read")](protocol);
+ } catch (e) {
+ if (
+ !(
+ e.name === "InputBufferUnderrunError" ||
+ e.name === "TProtocolException"
+ )
+ ) {
+ // TODO: Are other exceptions expected?
+ // console.log(e);
+ }
+ }
+ };
+}
+
+/**
+ * Creates a roundtrip fuzzer function for a specific protocol
+ * @param {Function} protocolFactory - The Thrift protocol factory function
+ * @param {boolean} [readMessageBegin=false] - Whether to call readMessageBegin before reading the test instance
+ * This is needed for protocols that do not support parsing just a struct, such as TJSONProtocol.
+ * @returns {Function} A fuzzer function that can be used with Jazzer.js
+ */
+function createRoundtripFuzzer(protocolFactory, readMessageBegin = false) {
+ return function fuzz(data) {
+ if (data.length < 2) {
+ return;
+ }
+
+ try {
+ // First deserialize using framed transport for input
+ const transport1 = new thrift.TFramedTransport(data);
+ const protocol1 = protocolFactory(transport1);
+ const testInstance = new FuzzTest.FuzzTest();
+ if (readMessageBegin) {
+ protocol1.readMessageBegin();
+ }
+ testInstance[Symbol.for("read")](protocol1);
+
+ // Then serialize using buffered transport with callback
+ let serializedData;
+ const transport2 = new thrift.TBufferedTransport(null, function (buf) {
+ serializedData = buf;
+ });
+ const protocol2 = protocolFactory(transport2);
+ testInstance[Symbol.for("write")](protocol2);
+ protocol2.flush();
+
+ if (!serializedData) {
+ throw new Error("Serialization failed - no data produced");
+ }
+
+ // Finally deserialize again and compare using framed transport
+ const transport3 = new thrift.TFramedTransport(serializedData);
+ const protocol3 = protocolFactory(transport3);
+ const deserialized = new FuzzTest.FuzzTest();
+ if (readMessageBegin) {
+ protocol3.readMessageBegin();
+ }
+ deserialized[Symbol.for("read")](protocol3);
+
+ // Compare the objects
+ if (!testInstance.equals(deserialized)) {
+ throw new Error("Roundtrip comparison failed");
+ }
+ } catch (e) {
+ if (
+ !(
+ e.name === "InputBufferUnderrunError" ||
+ e.name === "TProtocolException"
+ )
+ ) {
+ // TODO: Are other exceptions expected?
+ // console.log(e);
+ }
+ }
+ };
+}
+
+module.exports = {
+ createParserFuzzer,
+ createRoundtripFuzzer,
+};