/src/node/test/fuzzers/fuzz_tls_socket_request.cc
Line  | Count  | Source  | 
1  |  | // Copyright 2025 Google LLC  | 
2  |  | //  | 
3  |  | // Licensed under the Apache License, Version 2.0 (the "License");  | 
4  |  | // you may not use this file except in compliance with the License.  | 
5  |  | // You may obtain a copy of the License at  | 
6  |  | //  | 
7  |  | //      http://www.apache.org/licenses/LICENSE-2.0  | 
8  |  | //  | 
9  |  | // Unless required by applicable law or agreed to in writing, software  | 
10  |  | // distributed under the License is distributed on an "AS IS" BASIS,  | 
11  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
12  |  | // See the License for the specific language governing permissions and  | 
13  |  | // limitations under the License.  | 
14  |  |  | 
15  |  | #include <cstdint>  | 
16  |  | #include <string>  | 
17  |  | #include <fuzzer/FuzzedDataProvider.h>  | 
18  |  |  | 
19  |  | #include "fuzz_common.h"  | 
20  |  | #include "fuzz_js_format.h"  | 
21  |  |  | 
22  |  | // Static header (existing PEMs + setup; kept intact)  | 
23  |  | static constexpr std::string_view kHeader = R"(const tls = require('tls'); | 
24  |  | const https = require('https'); | 
25  |  | const { setEnvironmentData } = require('worker_threads'); | 
26  |  | const { send } = require('process'); | 
27  |  |  | 
28  |  | // Self-signed key/cert (same as your original)  | 
29  |  | const key = `-----BEGIN EC PARAMETERS-----  | 
30  |  | BggqhkjOPQMBBw==  | 
31  |  | -----END EC PARAMETERS-----  | 
32  |  | -----BEGIN EC PRIVATE KEY-----  | 
33  |  | MHcCAQEEIDKfHHbiJMdu2STyHL11fWC7psMY19/gUNpsUpkwgGACoAoGCCqGSM49  | 
34  |  | AwEHoUQDQgAEItqm+pYj3Ca8bi5mBs+H8xSMxuW2JNn4I+kw3aREsetLk8pn3o81  | 
35  |  | PWBiTdSZrGBGQSy+UAlQvYeE6Z/QXQk8aw==  | 
36  |  | -----END EC PRIVATE KEY-----`;  | 
37  |  |  | 
38  |  | const cert = `-----BEGIN CERTIFICATE-----  | 
39  |  | MIIBhjCCASsCFDJU1tCo88NYU//pE+DQKO9hUDsFMAoGCCqGSM49BAMCMEUxCzAJ  | 
40  |  | BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l  | 
41  |  | dCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAwOTIyMDg1NDU5WhcNNDgwMjA3MDg1NDU5  | 
42  |  | WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwY  | 
43  |  | SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD  | 
44  |  | QgAEItqm+pYj3Ca8bi5mBs+H8xSMxuW2JNn4I+kw3aREsetLk8pn3o81PWBiTdSZ  | 
45  |  | rGBGQSy+UAlQvYeE6Z/QXQk8azAKBggqhkjOPQQDAgNJADBGAiEA7Bdn4F87KqIe  | 
46  |  | Y/ABy/XIXXpFUb2nyv3zV7POQi2lPcECIQC3UWLmfiedpiIKsf9YRIyO0uEood7+  | 
47  |  | glj2R1NNr1X68w==  | 
48  |  | -----END CERTIFICATE-----`;  | 
49  |  |  | 
50  |  | const options = { key, cert }; | 
51  |  |  | 
52  |  | let srv;  | 
53  |  | let socket;  | 
54  |  | let receivedResponse = 0;  | 
55  |  |  | 
56  |  | async function send_requests() { | 
57  |  |   socket = tls.connect(4444, 'localhost', { rejectUnauthorized: false }, () => { | 
58  |  |     const httpRequest = `GET / HTTP/1.1\r\nHost: localhost\r\nConnection: Keep-alive\r\n\r\n`;  | 
59  |  |     socket.write(httpRequest);  | 
60  |  | )";  | 
61  |  |  | 
62  |  | // Template for a single POST using a precomputed body Buffer  | 
63  |  | static constexpr std::string_view kPostTemplate = R"(  | 
64  |  |     const body = Buffer.from({0}, 'latin1'); | 
65  |  |     const postRequest = `POST / HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nContent-Length: ${body.length}\r\n\r\n${body.toString('latin1')}`; | 
66  |  |     socket.write(postRequest);  | 
67  |  | )";  | 
68  |  |  | 
69  |  | static constexpr std::string_view kFooter = R"(  | 
70  |  |   });  | 
71  |  |  | 
72  |  |   socket.on('data', (data) => { | 
73  |  |     receivedResponse++;  | 
74  |  |     if (receivedResponse === 6) { | 
75  |  |       socket.end();  | 
76  |  |     }  | 
77  |  |   });  | 
78  |  |  | 
79  |  |   socket.on('end', () => { | 
80  |  |     srv.close(() => {}); | 
81  |  |   });  | 
82  |  | }  | 
83  |  |  | 
84  |  | function run_server() { | 
85  |  |   srv = https.createServer(options, function (req, res) { | 
86  |  |     let chunks = [];  | 
87  |  |     req.on('data', (c) => chunks.push(c)); | 
88  |  |     req.on('end', () => { | 
89  |  |       const body = Buffer.concat(chunks);  | 
90  |  |       res.writeHead(200, { 'Content-Type': 'text/html' }); | 
91  |  |       res.end('ok'); | 
92  |  |     });  | 
93  |  |   }).listen(4444);  | 
94  |  | }  | 
95  |  |  | 
96  |  | run_server();  | 
97  |  | send_requests();  | 
98  |  | )";  | 
99  |  |  | 
100  | 22.0k  | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { | 
101  | 22.0k  |   FuzzedDataProvider prov(data, size);  | 
102  | 22.0k  |   std::string b1 = prov.ConsumeRandomLengthString();  | 
103  | 22.0k  |   std::string b2 = prov.ConsumeRandomLengthString();  | 
104  | 22.0k  |   std::string b3 = prov.ConsumeRandomLengthString();  | 
105  |  |  | 
106  |  |   // Build the whole script: header + 3 posts + footer  | 
107  | 22.0k  |   std::string js = std::string(kHeader);  | 
108  | 22.0k  |   js += FormatJs(kPostTemplate, ToSingleQuotedJsLiteral(b1));  | 
109  | 22.0k  |   js += FormatJs(kPostTemplate, ToSingleQuotedJsLiteral(b2));  | 
110  | 22.0k  |   js += FormatJs(kPostTemplate, ToSingleQuotedJsLiteral(b3));  | 
111  | 22.0k  |   js += std::string(kFooter);  | 
112  |  |  | 
113  | 22.0k  |   fuzz::IsolateScope iso;  | 
114  | 22.0k  |   if (!iso.ok()) return 0;  | 
115  |  |  | 
116  |  |   // Give async networking a few ticks.  | 
117  | 22.0k  |   fuzz::EnvRunOptions opts;  | 
118  | 22.0k  |   opts.max_pumps = 8;  | 
119  | 22.0k  |   fuzz::RunEnvString(iso.isolate(), js.c_str(), opts);  | 
120  | 22.0k  |   return 0;  | 
121  | 22.0k  | }  |