/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 | 10.2k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
101 | 10.2k | FuzzedDataProvider prov(data, size); |
102 | 10.2k | std::string b1 = prov.ConsumeRandomLengthString(); |
103 | 10.2k | std::string b2 = prov.ConsumeRandomLengthString(); |
104 | 10.2k | std::string b3 = prov.ConsumeRandomLengthString(); |
105 | | |
106 | | // Build the whole script: header + 3 posts + footer |
107 | 10.2k | std::string js = std::string(kHeader); |
108 | 10.2k | js += FormatJs(kPostTemplate, ToSingleQuotedJsLiteral(b1)); |
109 | 10.2k | js += FormatJs(kPostTemplate, ToSingleQuotedJsLiteral(b2)); |
110 | 10.2k | js += FormatJs(kPostTemplate, ToSingleQuotedJsLiteral(b3)); |
111 | 10.2k | js += std::string(kFooter); |
112 | | |
113 | 10.2k | fuzz::IsolateScope iso; |
114 | 10.2k | if (!iso.ok()) return 0; |
115 | | |
116 | | // Give async networking a few ticks. |
117 | 10.2k | fuzz::EnvRunOptions opts; |
118 | 10.2k | opts.max_pumps = 8; |
119 | 10.2k | fuzz::RunEnvString(iso.isolate(), js.c_str(), opts); |
120 | 10.2k | return 0; |
121 | 10.2k | } |