Compare commits
10 Commits
9f8c456729
...
87f6ab1f33
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f6ab1f33 | ||
|
|
bf7be5724d | ||
|
|
69a46a3a67 | ||
|
|
3956f1c25b | ||
|
|
5ecad71650 | ||
|
|
235363be93 | ||
|
|
9a422afb58 | ||
|
|
d15dac7e21 | ||
|
|
f62a45736b | ||
|
|
ce983ceded |
34
CVE-2022-25881.patch
Normal file
34
CVE-2022-25881.patch
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
Index: node-v14.21.3/deps/npm/node_modules/http-cache-semantics/node4/index.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v14.21.3.orig/deps/npm/node_modules/http-cache-semantics/node4/index.js
|
||||||
|
+++ node-v14.21.3/deps/npm/node_modules/http-cache-semantics/node4/index.js
|
||||||
|
@@ -21,7 +21,7 @@ function parseCacheControl(header) {
|
||||||
|
|
||||||
|
// TODO: When there is more than one value present for a given directive (e.g., two Expires header fields, multiple Cache-Control: max-age directives),
|
||||||
|
// the directive's value is considered invalid. Caches are encouraged to consider responses that have invalid freshness information to be stale
|
||||||
|
- var parts = header.trim().split(/\s*,\s*/); // TODO: lame parsing
|
||||||
|
+ var parts = header.trim().split(/,/); // TODO: lame parsing
|
||||||
|
for (var _iterator = parts, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
|
||||||
|
var _ref;
|
||||||
|
|
||||||
|
@@ -36,11 +36,11 @@ function parseCacheControl(header) {
|
||||||
|
|
||||||
|
var part = _ref;
|
||||||
|
|
||||||
|
- var _part$split = part.split(/\s*=\s*/, 2),
|
||||||
|
+ var _part$split = part.split(/=/, 2),
|
||||||
|
k = _part$split[0],
|
||||||
|
v = _part$split[1];
|
||||||
|
|
||||||
|
- cc[k] = v === undefined ? true : v.replace(/^"|"$/g, ''); // TODO: lame unquoting
|
||||||
|
+ cc[k.trim()] = v === undefined ? true : v.trim().replace(/^"|"$/g, ''); // TODO: lame unquoting
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc;
|
||||||
|
@@ -556,4 +556,4 @@ module.exports = function () {
|
||||||
|
};
|
||||||
|
|
||||||
|
return CachePolicy;
|
||||||
|
-}();
|
||||||
|
\ No newline at end of file
|
||||||
|
+}();
|
||||||
@ -1,21 +1,18 @@
|
|||||||
From 1aa5036c31ac2a9b2a2528af454675ad412f1464 Mon Sep 17 00:00:00 2001
|
commit 1aa5036c31ac2a9b2a2528af454675ad412f1464
|
||||||
From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= <tniessen@tnie.de>
|
Author: Tobias Nießen <tniessen@tnie.de>
|
||||||
Date: Fri, 27 May 2022 21:18:49 +0000
|
Date: Fri May 27 21:18:49 2022 +0000
|
||||||
Subject: [PATCH] src: fix IPv4 validation in inspector_socket
|
|
||||||
|
|
||||||
Co-authored-by: RafaelGSS <rafael.nunu@hotmail.com>
|
src: fix IPv4 validation in inspector_socket
|
||||||
|
|
||||||
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
Co-authored-by: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
Reviewed-By: RafaelGSS <rafael.nunu@hotmail.com>
|
|
||||||
PR-URL: https://github.com/nodejs-private/node-private/pull/320
|
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
||||||
CVE-ID: CVE-2022-32212
|
Reviewed-By: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
---
|
PR-URL: https://github.com/nodejs-private/node-private/pull/320
|
||||||
src/inspector_socket.cc | 18 +++++--
|
CVE-ID: CVE-2022-32212
|
||||||
test/cctest/test_inspector_socket.cc | 74 ++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 87 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc
|
diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc
|
||||||
index 1650c3fe01de..79b50e6a452d 100644
|
index 1650c3fe01..79b50e6a45 100644
|
||||||
--- a/src/inspector_socket.cc
|
--- a/src/inspector_socket.cc
|
||||||
+++ b/src/inspector_socket.cc
|
+++ b/src/inspector_socket.cc
|
||||||
@@ -164,14 +164,22 @@ static std::string TrimPort(const std::string& host) {
|
@@ -164,14 +164,22 @@ static std::string TrimPort(const std::string& host) {
|
||||||
@ -47,7 +44,7 @@ index 1650c3fe01de..79b50e6a452d 100644
|
|||||||
|
|
||||||
// Constants for hybi-10 frame format.
|
// Constants for hybi-10 frame format.
|
||||||
diff --git a/test/cctest/test_inspector_socket.cc b/test/cctest/test_inspector_socket.cc
|
diff --git a/test/cctest/test_inspector_socket.cc b/test/cctest/test_inspector_socket.cc
|
||||||
index dc8cd962141e..c740d961d9b7 100644
|
index dc8cd96214..c740d961d9 100644
|
||||||
--- a/test/cctest/test_inspector_socket.cc
|
--- a/test/cctest/test_inspector_socket.cc
|
||||||
+++ b/test/cctest/test_inspector_socket.cc
|
+++ b/test/cctest/test_inspector_socket.cc
|
||||||
@@ -851,4 +851,78 @@ TEST_F(InspectorSocketTest, HostCheckedForUPGRADE) {
|
@@ -851,4 +851,78 @@ TEST_F(InspectorSocketTest, HostCheckedForUPGRADE) {
|
||||||
3349
CVE-2022-32213-CVE-2022-32214-CVE-2022-32215.patch
Normal file
3349
CVE-2022-32213-CVE-2022-32214-CVE-2022-32215.patch
Normal file
File diff suppressed because it is too large
Load Diff
1581
CVE-2022-35256.patch
Normal file
1581
CVE-2022-35256.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
|||||||
From b358fb27a4253c6827378a64163448c04301e19c Mon Sep 17 00:00:00 2001
|
|
||||||
From: RafaelGSS <rafael.nunu@hotmail.com>
|
|
||||||
Date: Wed, 13 Jul 2022 13:20:22 -0300
|
|
||||||
Subject: [PATCH] src: fix IPv4 non routable validation
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=UTF-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
|
||||||
Reviewed-By: James M Snell <jasnell@gmail.com>
|
|
||||||
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
|
|
||||||
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
|
|
||||||
PR-URL: https://github.com/nodejs-private/node-private/pull/337
|
|
||||||
CVE-ID: CVE-2022-32212, CVE-2018-7160
|
|
||||||
---
|
|
||||||
src/inspector_socket.cc | 1 +
|
|
||||||
test/cctest/test_inspector_socket.cc | 8 ++++++++
|
|
||||||
2 files changed, 9 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc
|
|
||||||
index 79b50e6a452d..ab1cdf1fa5bd 100644
|
|
||||||
--- a/src/inspector_socket.cc
|
|
||||||
+++ b/src/inspector_socket.cc
|
|
||||||
@@ -164,6 +164,7 @@ static std::string TrimPort(const std::string& host) {
|
|
||||||
static bool IsIPAddress(const std::string& host) {
|
|
||||||
if (host.length() >= 4 && host.front() == '[' && host.back() == ']')
|
|
||||||
return true;
|
|
||||||
+ if (host.front() == '0') return false;
|
|
||||||
uint_fast16_t accum = 0;
|
|
||||||
uint_fast8_t quads = 0;
|
|
||||||
bool empty = true;
|
|
||||||
diff --git a/test/cctest/test_inspector_socket.cc b/test/cctest/test_inspector_socket.cc
|
|
||||||
index c740d961d9b7..6ae92c4b27e2 100644
|
|
||||||
--- a/test/cctest/test_inspector_socket.cc
|
|
||||||
+++ b/test/cctest/test_inspector_socket.cc
|
|
||||||
@@ -925,4 +925,12 @@ TEST_F(InspectorSocketTest, HostIpTooManyOctetsChecked) {
|
|
||||||
expect_handshake_failure();
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST_F(InspectorSocketTest, HostIPNonRoutable) {
|
|
||||||
+ const std::string INVALID_HOST_IP_REQUEST = "GET /json HTTP/1.1\r\n"
|
|
||||||
+ "Host: 0.0.0.0:9229\r\n\r\n";
|
|
||||||
+ send_in_chunks(INVALID_HOST_IP_REQUEST.c_str(),
|
|
||||||
+ INVALID_HOST_IP_REQUEST.length());
|
|
||||||
+ expect_handshake_failure();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
} // anonymous namespace
|
|
||||||
@ -19,11 +19,11 @@ Reviewed-by: Rich Trott <rtrott@gmail.com>
|
|||||||
test/cctest/test_inspector_socket.cc | 80 ++++++++++++++++++++++++++++
|
test/cctest/test_inspector_socket.cc | 80 ++++++++++++++++++++++++++++
|
||||||
2 files changed, 142 insertions(+), 16 deletions(-)
|
2 files changed, 142 insertions(+), 16 deletions(-)
|
||||||
|
|
||||||
diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc
|
Index: node-v12.22.12/src/inspector_socket.cc
|
||||||
index 8cabdaec2821..a28bd557c8ab 100644
|
===================================================================
|
||||||
--- a/src/inspector_socket.cc
|
--- node-v12.22.12.orig/src/inspector_socket.cc
|
||||||
+++ b/src/inspector_socket.cc
|
+++ node-v12.22.12/src/inspector_socket.cc
|
||||||
@@ -6,6 +6,7 @@
|
@@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "openssl/sha.h" // Sha-1 hash
|
#include "openssl/sha.h" // Sha-1 hash
|
||||||
|
|
||||||
@ -31,13 +31,12 @@ index 8cabdaec2821..a28bd557c8ab 100644
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
@@ -162,25 +163,70 @@ static std::string TrimPort(const std::string& host) {
|
@@ -166,24 +167,70 @@ static std::string TrimPort(const std::s
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsIPAddress(const std::string& host) {
|
static bool IsIPAddress(const std::string& host) {
|
||||||
- if (host.length() >= 4 && host.front() == '[' && host.back() == ']')
|
- if (host.length() >= 4 && host.front() == '[' && host.back() == ']')
|
||||||
- return true;
|
- return true;
|
||||||
- if (host.front() == '0') return false;
|
|
||||||
- uint_fast16_t accum = 0;
|
- uint_fast16_t accum = 0;
|
||||||
- uint_fast8_t quads = 0;
|
- uint_fast8_t quads = 0;
|
||||||
- bool empty = true;
|
- bool empty = true;
|
||||||
@ -118,11 +117,11 @@ index 8cabdaec2821..a28bd557c8ab 100644
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constants for hybi-10 frame format.
|
// Constants for hybi-10 frame format.
|
||||||
diff --git a/test/cctest/test_inspector_socket.cc b/test/cctest/test_inspector_socket.cc
|
Index: node-v12.22.12/test/cctest/test_inspector_socket.cc
|
||||||
index 6ae92c4b27e2..b351a23002c9 100644
|
===================================================================
|
||||||
--- a/test/cctest/test_inspector_socket.cc
|
--- node-v12.22.12.orig/test/cctest/test_inspector_socket.cc
|
||||||
+++ b/test/cctest/test_inspector_socket.cc
|
+++ node-v12.22.12/test/cctest/test_inspector_socket.cc
|
||||||
@@ -925,6 +925,54 @@ TEST_F(InspectorSocketTest, HostIpTooManyOctetsChecked) {
|
@@ -925,4 +925,85 @@ TEST_F(InspectorSocketTest, HostIpTooMan
|
||||||
expect_handshake_failure();
|
expect_handshake_failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,13 +173,7 @@ index 6ae92c4b27e2..b351a23002c9 100644
|
|||||||
+ expect_handshake_failure();
|
+ expect_handshake_failure();
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
TEST_F(InspectorSocketTest, HostIPNonRoutable) {
|
+
|
||||||
const std::string INVALID_HOST_IP_REQUEST = "GET /json HTTP/1.1\r\n"
|
|
||||||
"Host: 0.0.0.0:9229\r\n\r\n";
|
|
||||||
@@ -933,4 +981,36 @@ TEST_F(InspectorSocketTest, HostIPNonRoutable) {
|
|
||||||
expect_handshake_failure();
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST_F(InspectorSocketTest, HostIPv6NonRoutable) {
|
+TEST_F(InspectorSocketTest, HostIPv6NonRoutable) {
|
||||||
+ const std::string INVALID_HOST_IP_REQUEST = "GET /json HTTP/1.1\r\n"
|
+ const std::string INVALID_HOST_IP_REQUEST = "GET /json HTTP/1.1\r\n"
|
||||||
+ "Host: [::]:9229\r\n\r\n";
|
+ "Host: [::]:9229\r\n\r\n";
|
||||||
|
|||||||
219
CVE-2023-0464.patch
Normal file
219
CVE-2023-0464.patch
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
From 879f7080d7e141f415c79eaa3a8ac4a3dad0348b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Pauli <pauli@openssl.org>
|
||||||
|
Date: Wed, 8 Mar 2023 15:28:20 +1100
|
||||||
|
Subject: [PATCH] x509: excessive resource use verifying policy constraints
|
||||||
|
|
||||||
|
A security vulnerability has been identified in all supported versions
|
||||||
|
of OpenSSL related to the verification of X.509 certificate chains
|
||||||
|
that include policy constraints. Attackers may be able to exploit this
|
||||||
|
vulnerability by creating a malicious certificate chain that triggers
|
||||||
|
exponential use of computational resources, leading to a denial-of-service
|
||||||
|
(DoS) attack on affected systems.
|
||||||
|
|
||||||
|
Fixes CVE-2023-0464
|
||||||
|
|
||||||
|
Reviewed-by: Tomas Mraz <tomas@openssl.org>
|
||||||
|
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
|
||||||
|
(Merged from https://github.com/openssl/openssl/pull/20569)
|
||||||
|
---
|
||||||
|
deps/openssl/openssl/crypto/x509v3/pcy_local.h | 8 +++++++-
|
||||||
|
deps/openssl/openssl/crypto/x509v3/pcy_node.c | 12 +++++++++---
|
||||||
|
deps/openssl/openssl/crypto/x509v3/pcy_tree.c | 37 +++++++++++++++++++++++++++----------
|
||||||
|
3 files changed, 43 insertions(+), 14 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/deps/openssl/openssl/crypto/x509v3/pcy_local.h b/deps/openssl/openssl/crypto/x509v3/pcy_local.h
|
||||||
|
index 5daf78de45850..344aa067659cd 100644
|
||||||
|
--- a/deps/openssl/openssl/crypto/x509v3/pcy_local.h
|
||||||
|
+++ b/deps/openssl/openssl/crypto/x509v3/pcy_local.h
|
||||||
|
@@ -111,6 +111,11 @@ struct X509_POLICY_LEVEL_st {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct X509_POLICY_TREE_st {
|
||||||
|
+ /* The number of nodes in the tree */
|
||||||
|
+ size_t node_count;
|
||||||
|
+ /* The maximum number of nodes in the tree */
|
||||||
|
+ size_t node_maximum;
|
||||||
|
+
|
||||||
|
/* This is the tree 'level' data */
|
||||||
|
X509_POLICY_LEVEL *levels;
|
||||||
|
int nlevel;
|
||||||
|
@@ -159,7 +164,8 @@ X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk,
|
||||||
|
X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
|
||||||
|
X509_POLICY_DATA *data,
|
||||||
|
X509_POLICY_NODE *parent,
|
||||||
|
- X509_POLICY_TREE *tree);
|
||||||
|
+ X509_POLICY_TREE *tree,
|
||||||
|
+ int extra_data);
|
||||||
|
void policy_node_free(X509_POLICY_NODE *node);
|
||||||
|
int policy_node_match(const X509_POLICY_LEVEL *lvl,
|
||||||
|
const X509_POLICY_NODE *node, const ASN1_OBJECT *oid);
|
||||||
|
diff --git a/deps/openssl/openssl/crypto/x509v3/pcy_node.c b/deps/openssl/openssl/crypto/x509v3/pcy_node.c
|
||||||
|
index e2d7b15322363..d574fb9d665dc 100644
|
||||||
|
--- a/deps/openssl/openssl/crypto/x509v3/pcy_node.c
|
||||||
|
+++ b/deps/openssl/openssl/crypto/x509v3/pcy_node.c
|
||||||
|
@@ -59,10 +59,15 @@ X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level,
|
||||||
|
X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
|
||||||
|
X509_POLICY_DATA *data,
|
||||||
|
X509_POLICY_NODE *parent,
|
||||||
|
- X509_POLICY_TREE *tree)
|
||||||
|
+ X509_POLICY_TREE *tree,
|
||||||
|
+ int extra_data)
|
||||||
|
{
|
||||||
|
X509_POLICY_NODE *node;
|
||||||
|
|
||||||
|
+ /* Verify that the tree isn't too large. This mitigates CVE-2023-0464 */
|
||||||
|
+ if (tree->node_maximum > 0 && tree->node_count >= tree->node_maximum)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
node = OPENSSL_zalloc(sizeof(*node));
|
||||||
|
if (node == NULL) {
|
||||||
|
X509V3err(X509V3_F_LEVEL_ADD_NODE, ERR_R_MALLOC_FAILURE);
|
||||||
|
@@ -70,7 +75,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
|
||||||
|
}
|
||||||
|
node->data = data;
|
||||||
|
node->parent = parent;
|
||||||
|
- if (level) {
|
||||||
|
+ if (level != NULL) {
|
||||||
|
if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) {
|
||||||
|
if (level->anyPolicy)
|
||||||
|
goto node_error;
|
||||||
|
@@ -90,7 +95,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (tree) {
|
||||||
|
+ if (extra_data) {
|
||||||
|
if (tree->extra_data == NULL)
|
||||||
|
tree->extra_data = sk_X509_POLICY_DATA_new_null();
|
||||||
|
if (tree->extra_data == NULL){
|
||||||
|
@@ -103,6 +108,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ tree->node_count++;
|
||||||
|
if (parent)
|
||||||
|
parent->nchild++;
|
||||||
|
|
||||||
|
diff --git a/deps/openssl/openssl/crypto/x509v3/pcy_tree.c b/deps/openssl/openssl/crypto/x509v3/pcy_tree.c
|
||||||
|
index 6e8322cbc5e38..6c7fd35405000 100644
|
||||||
|
--- a/deps/openssl/openssl/crypto/x509v3/pcy_tree.c
|
||||||
|
+++ b/deps/openssl/openssl/crypto/x509v3/pcy_tree.c
|
||||||
|
@@ -13,6 +13,18 @@
|
||||||
|
|
||||||
|
#include "pcy_local.h"
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * If the maximum number of nodes in the policy tree isn't defined, set it to
|
||||||
|
+ * a generous default of 1000 nodes.
|
||||||
|
+ *
|
||||||
|
+ * Defining this to be zero means unlimited policy tree growth which opens the
|
||||||
|
+ * door on CVE-2023-0464.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef OPENSSL_POLICY_TREE_NODES_MAX
|
||||||
|
+# define OPENSSL_POLICY_TREE_NODES_MAX 1000
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Enable this to print out the complete policy tree at various point during
|
||||||
|
* evaluation.
|
||||||
|
@@ -168,6 +180,9 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
|
||||||
|
return X509_PCY_TREE_INTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Limit the growth of the tree to mitigate CVE-2023-0464 */
|
||||||
|
+ tree->node_maximum = OPENSSL_POLICY_TREE_NODES_MAX;
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* http://tools.ietf.org/html/rfc5280#section-6.1.2, figure 3.
|
||||||
|
*
|
||||||
|
@@ -184,7 +199,7 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
|
||||||
|
level = tree->levels;
|
||||||
|
if ((data = policy_data_new(NULL, OBJ_nid2obj(NID_any_policy), 0)) == NULL)
|
||||||
|
goto bad_tree;
|
||||||
|
- if (level_add_node(level, data, NULL, tree) == NULL) {
|
||||||
|
+ if (level_add_node(level, data, NULL, tree, 1) == NULL) {
|
||||||
|
policy_data_free(data);
|
||||||
|
goto bad_tree;
|
||||||
|
}
|
||||||
|
@@ -243,7 +258,8 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
|
||||||
|
* Return value: 1 on success, 0 otherwise
|
||||||
|
*/
|
||||||
|
static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
|
||||||
|
- X509_POLICY_DATA *data)
|
||||||
|
+ X509_POLICY_DATA *data,
|
||||||
|
+ X509_POLICY_TREE *tree)
|
||||||
|
{
|
||||||
|
X509_POLICY_LEVEL *last = curr - 1;
|
||||||
|
int i, matched = 0;
|
||||||
|
@@ -253,13 +269,13 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
|
||||||
|
X509_POLICY_NODE *node = sk_X509_POLICY_NODE_value(last->nodes, i);
|
||||||
|
|
||||||
|
if (policy_node_match(last, node, data->valid_policy)) {
|
||||||
|
- if (level_add_node(curr, data, node, NULL) == NULL)
|
||||||
|
+ if (level_add_node(curr, data, node, tree, 0) == NULL)
|
||||||
|
return 0;
|
||||||
|
matched = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matched && last->anyPolicy) {
|
||||||
|
- if (level_add_node(curr, data, last->anyPolicy, NULL) == NULL)
|
||||||
|
+ if (level_add_node(curr, data, last->anyPolicy, tree, 0) == NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
@@ -272,7 +288,8 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
|
||||||
|
* Return value: 1 on success, 0 otherwise.
|
||||||
|
*/
|
||||||
|
static int tree_link_nodes(X509_POLICY_LEVEL *curr,
|
||||||
|
- const X509_POLICY_CACHE *cache)
|
||||||
|
+ const X509_POLICY_CACHE *cache,
|
||||||
|
+ X509_POLICY_TREE *tree)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
@@ -280,7 +297,7 @@ static int tree_link_nodes(X509_POLICY_LEVEL *curr,
|
||||||
|
X509_POLICY_DATA *data = sk_X509_POLICY_DATA_value(cache->data, i);
|
||||||
|
|
||||||
|
/* Look for matching nodes in previous level */
|
||||||
|
- if (!tree_link_matching_nodes(curr, data))
|
||||||
|
+ if (!tree_link_matching_nodes(curr, data, tree))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
@@ -311,7 +328,7 @@ static int tree_add_unmatched(X509_POLICY_LEVEL *curr,
|
||||||
|
/* Curr may not have anyPolicy */
|
||||||
|
data->qualifier_set = cache->anyPolicy->qualifier_set;
|
||||||
|
data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
|
||||||
|
- if (level_add_node(curr, data, node, tree) == NULL) {
|
||||||
|
+ if (level_add_node(curr, data, node, tree, 1) == NULL) {
|
||||||
|
policy_data_free(data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -373,7 +390,7 @@ static int tree_link_any(X509_POLICY_LEVEL *curr,
|
||||||
|
}
|
||||||
|
/* Finally add link to anyPolicy */
|
||||||
|
if (last->anyPolicy &&
|
||||||
|
- level_add_node(curr, cache->anyPolicy, last->anyPolicy, NULL) == NULL)
|
||||||
|
+ level_add_node(curr, cache->anyPolicy, last->anyPolicy, tree, 0) == NULL)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
@@ -555,7 +572,7 @@ static int tree_calculate_user_set(X509_POLICY_TREE *tree,
|
||||||
|
extra->qualifier_set = anyPolicy->data->qualifier_set;
|
||||||
|
extra->flags = POLICY_DATA_FLAG_SHARED_QUALIFIERS
|
||||||
|
| POLICY_DATA_FLAG_EXTRA_NODE;
|
||||||
|
- node = level_add_node(NULL, extra, anyPolicy->parent, tree);
|
||||||
|
+ node = level_add_node(NULL, extra, anyPolicy->parent, tree, 1);
|
||||||
|
}
|
||||||
|
if (!tree->user_policies) {
|
||||||
|
tree->user_policies = sk_X509_POLICY_NODE_new_null();
|
||||||
|
@@ -582,7 +599,7 @@ static int tree_evaluate(X509_POLICY_TREE *tree)
|
||||||
|
|
||||||
|
for (i = 1; i < tree->nlevel; i++, curr++) {
|
||||||
|
cache = policy_cache_set(curr->cert);
|
||||||
|
- if (!tree_link_nodes(curr, cache))
|
||||||
|
+ if (!tree_link_nodes(curr, cache, tree))
|
||||||
|
return X509_PCY_TREE_INTERNAL;
|
||||||
|
|
||||||
|
if (!(curr->flags & X509_V_FLAG_INHIBIT_ANY)
|
||||||
51
CVE-2023-0465.patch
Normal file
51
CVE-2023-0465.patch
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
From b013765abfa80036dc779dd0e50602c57bb3bf95 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Matt Caswell <matt@openssl.org>
|
||||||
|
Date: Tue, 7 Mar 2023 16:52:55 +0000
|
||||||
|
Subject: [PATCH] Ensure that EXFLAG_INVALID_POLICY is checked even in leaf
|
||||||
|
certs
|
||||||
|
|
||||||
|
Even though we check the leaf cert to confirm it is valid, we
|
||||||
|
later ignored the invalid flag and did not notice that the leaf
|
||||||
|
cert was bad.
|
||||||
|
|
||||||
|
Fixes: CVE-2023-0465
|
||||||
|
|
||||||
|
Reviewed-by: Hugo Landau <hlandau@openssl.org>
|
||||||
|
Reviewed-by: Tomas Mraz <tomas@openssl.org>
|
||||||
|
(Merged from https://github.com/openssl/openssl/pull/20588)
|
||||||
|
---
|
||||||
|
deps/openssl/openssl/crypto/x509/x509_vfy.c | 11 +++++++++--
|
||||||
|
1 file changed, 9 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/deps/openssl/openssl/crypto/x509/x509_vfy.c b/deps/openssl/openssl/crypto/x509/x509_vfy.c
|
||||||
|
index 925fbb5412583..1dfe4f9f31a58 100644
|
||||||
|
--- a/deps/openssl/openssl/crypto/x509/x509_vfy.c
|
||||||
|
+++ b/deps/openssl/openssl/crypto/x509/x509_vfy.c
|
||||||
|
@@ -1649,18 +1649,25 @@ static int check_policy(X509_STORE_CTX *ctx)
|
||||||
|
}
|
||||||
|
/* Invalid or inconsistent extensions */
|
||||||
|
if (ret == X509_PCY_TREE_INVALID) {
|
||||||
|
- int i;
|
||||||
|
+ int i, cbcalled = 0;
|
||||||
|
|
||||||
|
/* Locate certificates with bad extensions and notify callback. */
|
||||||
|
- for (i = 1; i < sk_X509_num(ctx->chain); i++) {
|
||||||
|
+ for (i = 0; i < sk_X509_num(ctx->chain); i++) {
|
||||||
|
X509 *x = sk_X509_value(ctx->chain, i);
|
||||||
|
|
||||||
|
if (!(x->ex_flags & EXFLAG_INVALID_POLICY))
|
||||||
|
continue;
|
||||||
|
+ cbcalled = 1;
|
||||||
|
if (!verify_cb_cert(ctx, x, i,
|
||||||
|
X509_V_ERR_INVALID_POLICY_EXTENSION))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
+ if (!cbcalled) {
|
||||||
|
+ /* Should not be able to get here */
|
||||||
|
+ X509err(X509_F_CHECK_POLICY, ERR_R_INTERNAL_ERROR);
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+ /* The callback ignored the error so we return success */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (ret == X509_PCY_TREE_FAILURE) {
|
||||||
699
CVE-2023-23918.patch
Normal file
699
CVE-2023-23918.patch
Normal file
@ -0,0 +1,699 @@
|
|||||||
|
commit f7892c16be6507e26c2ae478c261fa3fa2d84f52
|
||||||
|
Author: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Date: Mon Feb 13 15:41:30 2023 -0300
|
||||||
|
|
||||||
|
lib: makeRequireFunction patch when experimental policy
|
||||||
|
|
||||||
|
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/373
|
||||||
|
Refs: https://hackerone.com/bugs?subject=nodejs&report_id=1747642
|
||||||
|
CVE-ID: CVE-2023-23918
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/358
|
||||||
|
Reviewed-by: Bradley Farias <bradley.meck@gmail.com>
|
||||||
|
Reviewed-by: Michael Dawson <midawson@redhat.com>
|
||||||
|
|
||||||
|
commit 83975b7fb463e29c92c8e83693b725e269cee149
|
||||||
|
Author: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Date: Tue Oct 25 00:23:27 2022 -0300
|
||||||
|
|
||||||
|
policy: makeRequireFunction on mainModule.require
|
||||||
|
|
||||||
|
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Co-authored-by: Bradley Farias <bradley.meck@gmail.com>
|
||||||
|
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/373
|
||||||
|
Refs: https://hackerone.com/bugs?subject=nodejs&report_id=1747642
|
||||||
|
CVE-ID: CVE-2023-23918
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/358
|
||||||
|
Reviewed-by: Bradley Farias <bradley.meck@gmail.com>
|
||||||
|
Reviewed-by: Michael Dawson <midawson@redhat.com>
|
||||||
|
|
||||||
|
commit fa115ee8ac32883c96df4a93fafb600bd665a5aa
|
||||||
|
Author: Antoine du Hamel <duhamelantoine1995@gmail.com>
|
||||||
|
Date: Fri Aug 5 00:41:48 2022 +0200
|
||||||
|
|
||||||
|
module: protect against prototype mutation
|
||||||
|
|
||||||
|
Ensures that mutating the `Object` prototype does not influence the
|
||||||
|
parsing of `package.json` files.
|
||||||
|
|
||||||
|
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/373
|
||||||
|
PR-URL: https://github.com/nodejs/node/pull/44007
|
||||||
|
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
|
||||||
|
|
||||||
|
|
||||||
|
Index: node-v12.22.12/lib/internal/modules/cjs/helpers.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/lib/internal/modules/cjs/helpers.js
|
||||||
|
+++ node-v12.22.12/lib/internal/modules/cjs/helpers.js
|
||||||
|
@@ -5,6 +5,7 @@ const {
|
||||||
|
SafeMap,
|
||||||
|
} = primordials;
|
||||||
|
const {
|
||||||
|
+ ERR_INVALID_ARG_TYPE,
|
||||||
|
ERR_MANIFEST_DEPENDENCY_MISSING,
|
||||||
|
ERR_UNKNOWN_BUILTIN_MODULE
|
||||||
|
} = require('internal/errors').codes;
|
||||||
|
@@ -14,11 +15,22 @@ const { validateString } = require('inte
|
||||||
|
const path = require('path');
|
||||||
|
const { pathToFileURL, fileURLToPath } = require('internal/url');
|
||||||
|
const { URL } = require('url');
|
||||||
|
+const { setOwnProperty } = require('internal/util');
|
||||||
|
+
|
||||||
|
+const {
|
||||||
|
+ require_private_symbol,
|
||||||
|
+} = internalBinding('util');
|
||||||
|
|
||||||
|
let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
|
||||||
|
debug = fn;
|
||||||
|
});
|
||||||
|
|
||||||
|
+// TODO: Use this set when resolving pkg#exports conditions in loader.js.
|
||||||
|
+const cjsConditions = [
|
||||||
|
+ 'require',
|
||||||
|
+ 'node',
|
||||||
|
+];
|
||||||
|
+
|
||||||
|
function loadNativeModule(filename, request) {
|
||||||
|
const mod = NativeModule.map.get(filename);
|
||||||
|
if (mod) {
|
||||||
|
@@ -28,20 +40,31 @@ function loadNativeModule(filename, requ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+let $Module = null;
|
||||||
|
+function lazyModule() {
|
||||||
|
+ $Module = $Module || require('internal/modules/cjs/loader').Module;
|
||||||
|
+ return $Module;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
// Invoke with makeRequireFunction(module) where |module| is the Module object
|
||||||
|
// to use as the context for the require() function.
|
||||||
|
// Use redirects to set up a mapping from a policy and restrict dependencies
|
||||||
|
const urlToFileCache = new SafeMap();
|
||||||
|
function makeRequireFunction(mod, redirects) {
|
||||||
|
- const Module = mod.constructor;
|
||||||
|
+ // lazy due to cycle
|
||||||
|
+ const Module = lazyModule();
|
||||||
|
+ if (mod instanceof Module !== true) {
|
||||||
|
+ throw new ERR_INVALID_ARG_TYPE('mod', 'Module', mod);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
let require;
|
||||||
|
if (redirects) {
|
||||||
|
const { resolve, reaction } = redirects;
|
||||||
|
const id = mod.filename || mod.id;
|
||||||
|
+ const conditions = cjsConditions;
|
||||||
|
require = function require(path) {
|
||||||
|
let missing = true;
|
||||||
|
- const destination = resolve(path);
|
||||||
|
+ const destination = resolve(path, conditions);
|
||||||
|
if (destination === true) {
|
||||||
|
missing = false;
|
||||||
|
} else if (destination) {
|
||||||
|
@@ -61,17 +84,17 @@ function makeRequireFunction(mod, redire
|
||||||
|
filepath = fileURLToPath(destination);
|
||||||
|
urlToFileCache.set(href, filepath);
|
||||||
|
}
|
||||||
|
- return mod.require(filepath);
|
||||||
|
+ return mod[require_private_symbol](mod, filepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (missing) {
|
||||||
|
- reaction(new ERR_MANIFEST_DEPENDENCY_MISSING(id, path));
|
||||||
|
+ reaction(new ERR_MANIFEST_DEPENDENCY_MISSING(id, path, ArrayPrototypeJoin([...conditions], ', ')));
|
||||||
|
}
|
||||||
|
- return mod.require(path);
|
||||||
|
+ return mod[require_private_symbol](mod, path);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
require = function require(path) {
|
||||||
|
- return mod.require(path);
|
||||||
|
+ return mod[require_private_symbol](mod, path);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -89,7 +112,7 @@ function makeRequireFunction(mod, redire
|
||||||
|
|
||||||
|
resolve.paths = paths;
|
||||||
|
|
||||||
|
- require.main = process.mainModule;
|
||||||
|
+ setOwnProperty(require, 'main', process.mainModule);
|
||||||
|
|
||||||
|
// Enable support to add extra extension types.
|
||||||
|
require.extensions = Module._extensions;
|
||||||
|
Index: node-v12.22.12/lib/internal/modules/cjs/loader.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/lib/internal/modules/cjs/loader.js
|
||||||
|
+++ node-v12.22.12/lib/internal/modules/cjs/loader.js
|
||||||
|
@@ -59,7 +59,7 @@ const {
|
||||||
|
rekeySourceMap
|
||||||
|
} = require('internal/source_map/source_map_cache');
|
||||||
|
const { pathToFileURL, fileURLToPath, isURLInstance } = require('internal/url');
|
||||||
|
-const { deprecate } = require('internal/util');
|
||||||
|
+const { deprecate, filterOwnProperties, setOwnProperty } = require('internal/util');
|
||||||
|
const vm = require('vm');
|
||||||
|
const assert = require('internal/assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
@@ -70,6 +70,9 @@ const { internalModuleStat } = internalB
|
||||||
|
const packageJsonReader = require('internal/modules/package_json_reader');
|
||||||
|
const { safeGetenv } = internalBinding('credentials');
|
||||||
|
const {
|
||||||
|
+ require_private_symbol,
|
||||||
|
+} = internalBinding('util');
|
||||||
|
+const {
|
||||||
|
makeRequireFunction,
|
||||||
|
normalizeReferrerURL,
|
||||||
|
stripBOM,
|
||||||
|
@@ -120,6 +123,20 @@ const relativeResolveCache = ObjectCreat
|
||||||
|
let requireDepth = 0;
|
||||||
|
let statCache = null;
|
||||||
|
|
||||||
|
+function internalRequire(module, id) {
|
||||||
|
+ validateString(id, 'id');
|
||||||
|
+ if (id === '') {
|
||||||
|
+ throw new ERR_INVALID_ARG_VALUE('id', id,
|
||||||
|
+ 'must be a non-empty string');
|
||||||
|
+ }
|
||||||
|
+ requireDepth++;
|
||||||
|
+ try {
|
||||||
|
+ return Module._load(id, module, /* isMain */ false);
|
||||||
|
+ } finally {
|
||||||
|
+ requireDepth--;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
function stat(filename) {
|
||||||
|
filename = path.toNamespacedPath(filename);
|
||||||
|
if (statCache !== null) {
|
||||||
|
@@ -140,12 +157,21 @@ function updateChildren(parent, child, s
|
||||||
|
function Module(id = '', parent) {
|
||||||
|
this.id = id;
|
||||||
|
this.path = path.dirname(id);
|
||||||
|
- this.exports = {};
|
||||||
|
+ setOwnProperty(this, 'exports', {});
|
||||||
|
this.parent = parent;
|
||||||
|
updateChildren(parent, this, false);
|
||||||
|
this.filename = null;
|
||||||
|
this.loaded = false;
|
||||||
|
this.children = [];
|
||||||
|
+ let redirects;
|
||||||
|
+ if (manifest) {
|
||||||
|
+ const moduleURL = pathToFileURL(id);
|
||||||
|
+ redirects = manifest.getRedirector(moduleURL);
|
||||||
|
+ }
|
||||||
|
+ setOwnProperty(this, 'require', makeRequireFunction(this, redirects));
|
||||||
|
+ // Loads a module at the given file path. Returns that module's
|
||||||
|
+ // `exports` property.
|
||||||
|
+ this[require_private_symbol] = internalRequire;
|
||||||
|
}
|
||||||
|
|
||||||
|
const builtinModules = [];
|
||||||
|
@@ -242,14 +268,13 @@ function readPackage(requestPath) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
- const parsed = JSONParse(json);
|
||||||
|
- const filtered = {
|
||||||
|
- name: parsed.name,
|
||||||
|
- main: parsed.main,
|
||||||
|
- exports: parsed.exports,
|
||||||
|
- imports: parsed.imports,
|
||||||
|
- type: parsed.type
|
||||||
|
- };
|
||||||
|
+ const filtered = filterOwnProperties(JSONParse(json), [
|
||||||
|
+ 'name',
|
||||||
|
+ 'main',
|
||||||
|
+ 'exports',
|
||||||
|
+ 'imports',
|
||||||
|
+ 'type',
|
||||||
|
+ ]);
|
||||||
|
packageJsonCache.set(jsonPath, filtered);
|
||||||
|
return filtered;
|
||||||
|
} catch (e) {
|
||||||
|
@@ -684,6 +709,7 @@ Module._load = function(request, parent,
|
||||||
|
|
||||||
|
if (isMain) {
|
||||||
|
process.mainModule = module;
|
||||||
|
+ setOwnProperty(module.require, 'main', process.mainModule);
|
||||||
|
module.id = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -873,24 +899,6 @@ Module.prototype.load = function(filenam
|
||||||
|
ESMLoader.cjsCache.set(this, exports);
|
||||||
|
};
|
||||||
|
|
||||||
|
-
|
||||||
|
-// Loads a module at the given file path. Returns that module's
|
||||||
|
-// `exports` property.
|
||||||
|
-Module.prototype.require = function(id) {
|
||||||
|
- validateString(id, 'id');
|
||||||
|
- if (id === '') {
|
||||||
|
- throw new ERR_INVALID_ARG_VALUE('id', id,
|
||||||
|
- 'must be a non-empty string');
|
||||||
|
- }
|
||||||
|
- requireDepth++;
|
||||||
|
- try {
|
||||||
|
- return Module._load(id, this, /* isMain */ false);
|
||||||
|
- } finally {
|
||||||
|
- requireDepth--;
|
||||||
|
- }
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-
|
||||||
|
// Resolved path to process.argv[1] will be lazily placed here
|
||||||
|
// (needed for setting breakpoint when called with --inspect-brk)
|
||||||
|
let resolvedArgv;
|
||||||
|
@@ -952,10 +960,9 @@ function wrapSafe(filename, content, cjs
|
||||||
|
// Returns exception, if any.
|
||||||
|
Module.prototype._compile = function(content, filename) {
|
||||||
|
let moduleURL;
|
||||||
|
- let redirects;
|
||||||
|
if (manifest) {
|
||||||
|
moduleURL = pathToFileURL(filename);
|
||||||
|
- redirects = manifest.getRedirector(moduleURL);
|
||||||
|
+ manifest.getRedirector(moduleURL);
|
||||||
|
manifest.assertIntegrity(moduleURL, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -986,7 +993,6 @@ Module.prototype._compile = function(con
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const dirname = path.dirname(filename);
|
||||||
|
- const require = makeRequireFunction(this, redirects);
|
||||||
|
let result;
|
||||||
|
const exports = this.exports;
|
||||||
|
const thisValue = exports;
|
||||||
|
@@ -994,9 +1000,9 @@ Module.prototype._compile = function(con
|
||||||
|
if (requireDepth === 0) statCache = new Map();
|
||||||
|
if (inspectorWrapper) {
|
||||||
|
result = inspectorWrapper(compiledWrapper, thisValue, exports,
|
||||||
|
- require, module, filename, dirname);
|
||||||
|
+ module.require, module, filename, dirname);
|
||||||
|
} else {
|
||||||
|
- result = compiledWrapper.call(thisValue, exports, require, module,
|
||||||
|
+ result = compiledWrapper.call(thisValue, exports, module.require, module,
|
||||||
|
filename, dirname);
|
||||||
|
}
|
||||||
|
hasLoadedAnyUserCJSModule = true;
|
||||||
|
@@ -1038,7 +1044,7 @@ Module._extensions['.json'] = function(m
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
- module.exports = JSONParse(stripBOM(content));
|
||||||
|
+ setOwnProperty(module, 'exports', JSONParse(stripBOM(content)));
|
||||||
|
} catch (err) {
|
||||||
|
err.message = filename + ': ' + err.message;
|
||||||
|
throw err;
|
||||||
|
@@ -1144,7 +1150,7 @@ Module._preloadModules = function(reques
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let n = 0; n < requests.length; n++)
|
||||||
|
- parent.require(requests[n]);
|
||||||
|
+ internalRequire(parent, requests[n]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Module.syncBuiltinESMExports = function syncBuiltinESMExports() {
|
||||||
|
@@ -1155,5 +1161,14 @@ Module.syncBuiltinESMExports = function
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
+ObjectDefineProperty(Module.prototype, 'constructor', {
|
||||||
|
+ __proto__: null,
|
||||||
|
+ get: function() {
|
||||||
|
+ return manifest ? undefined : Module;
|
||||||
|
+ },
|
||||||
|
+ configurable: false,
|
||||||
|
+ enumerable: false,
|
||||||
|
+});
|
||||||
|
+
|
||||||
|
// Backwards compatibility
|
||||||
|
Module.Module = Module;
|
||||||
|
Index: node-v12.22.12/lib/internal/util.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/lib/internal/util.js
|
||||||
|
+++ node-v12.22.12/lib/internal/util.js
|
||||||
|
@@ -11,6 +11,7 @@ const {
|
||||||
|
ObjectGetOwnPropertyDescriptor,
|
||||||
|
ObjectGetOwnPropertyDescriptors,
|
||||||
|
ObjectGetPrototypeOf,
|
||||||
|
+ ObjectPrototypeHasOwnProperty,
|
||||||
|
ObjectSetPrototypeOf,
|
||||||
|
Promise,
|
||||||
|
ReflectConstruct,
|
||||||
|
@@ -404,6 +405,35 @@ function sleep(msec) {
|
||||||
|
_sleep(msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
+function filterOwnProperties(source, keys) {
|
||||||
|
+ const filtered = ObjectCreate(null);
|
||||||
|
+ for (let i = 0; i < keys.length; i++) {
|
||||||
|
+ const key = keys[i];
|
||||||
|
+ if (ObjectPrototypeHasOwnProperty(source, key)) {
|
||||||
|
+ filtered[key] = source[key];
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return filtered;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * Mimics `obj[key] = value` but ignoring potential prototype inheritance.
|
||||||
|
+ * @param {any} obj
|
||||||
|
+ * @param {string} key
|
||||||
|
+ * @param {any} value
|
||||||
|
+ * @returns {any}
|
||||||
|
+ */
|
||||||
|
+function setOwnProperty(obj, key, value) {
|
||||||
|
+ return ObjectDefineProperty(obj, key, {
|
||||||
|
+ __proto__: null,
|
||||||
|
+ configurable: true,
|
||||||
|
+ enumerable: true,
|
||||||
|
+ value,
|
||||||
|
+ writable: true,
|
||||||
|
+ });
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
module.exports = {
|
||||||
|
assertCrypto,
|
||||||
|
cachedResult,
|
||||||
|
@@ -413,6 +443,7 @@ module.exports = {
|
||||||
|
deprecate,
|
||||||
|
emitExperimentalWarning,
|
||||||
|
filterDuplicateStrings,
|
||||||
|
+ filterOwnProperties,
|
||||||
|
getConstructorOf,
|
||||||
|
getSystemErrorName,
|
||||||
|
isError,
|
||||||
|
@@ -435,5 +466,6 @@ module.exports = {
|
||||||
|
// Used by the buffer module to capture an internal reference to the
|
||||||
|
// default isEncoding implementation, just in case userland overrides it.
|
||||||
|
kIsEncodingSymbol: Symbol('kIsEncodingSymbol'),
|
||||||
|
- kVmBreakFirstLineSymbol: Symbol('kVmBreakFirstLineSymbol')
|
||||||
|
+ kVmBreakFirstLineSymbol: Symbol('kVmBreakFirstLineSymbol'),
|
||||||
|
+ setOwnProperty,
|
||||||
|
};
|
||||||
|
Index: node-v12.22.12/test/common/fixtures.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/common/fixtures.js
|
||||||
|
+++ node-v12.22.12/test/common/fixtures.js
|
||||||
|
@@ -3,6 +3,7 @@
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
+const { pathToFileURL } = require('url');
|
||||||
|
|
||||||
|
const fixturesDir = path.join(__dirname, '..', 'fixtures');
|
||||||
|
|
||||||
|
@@ -10,6 +11,10 @@ function fixturesPath(...args) {
|
||||||
|
return path.join(fixturesDir, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
+function fixturesFileURL(...args) {
|
||||||
|
+ return pathToFileURL(fixturesPath(...args));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
function readFixtureSync(args, enc) {
|
||||||
|
if (Array.isArray(args))
|
||||||
|
return fs.readFileSync(fixturesPath(...args), enc);
|
||||||
|
@@ -23,6 +28,7 @@ function readFixtureKey(name, enc) {
|
||||||
|
module.exports = {
|
||||||
|
fixturesDir,
|
||||||
|
path: fixturesPath,
|
||||||
|
+ fileURL: fixturesFileURL,
|
||||||
|
readSync: readFixtureSync,
|
||||||
|
readKey: readFixtureKey
|
||||||
|
};
|
||||||
|
Index: node-v12.22.12/test/fixtures/es-module-specifiers/index.mjs
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/fixtures/es-module-specifiers/index.mjs
|
||||||
|
+++ node-v12.22.12/test/fixtures/es-module-specifiers/index.mjs
|
||||||
|
@@ -1,10 +1,11 @@
|
||||||
|
import explicit from 'explicit-main';
|
||||||
|
import implicit from 'implicit-main';
|
||||||
|
import implicitModule from 'implicit-main-type-module';
|
||||||
|
+import noMain from 'no-main-field';
|
||||||
|
|
||||||
|
function getImplicitCommonjs () {
|
||||||
|
return import('implicit-main-type-commonjs');
|
||||||
|
}
|
||||||
|
|
||||||
|
-export {explicit, implicit, implicitModule, getImplicitCommonjs};
|
||||||
|
+export {explicit, implicit, implicitModule, getImplicitCommonjs, noMain};
|
||||||
|
export default 'success';
|
||||||
|
Index: node-v12.22.12/test/fixtures/es-module-specifiers/node_modules/no-main-field/index.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/es-module-specifiers/node_modules/no-main-field/index.js
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+'use strict';
|
||||||
|
+module.exports = 'no main field';
|
||||||
|
+
|
||||||
|
Index: node-v12.22.12/test/fixtures/es-module-specifiers/node_modules/no-main-field/package.json
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/es-module-specifiers/node_modules/no-main-field/package.json
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+{}
|
||||||
|
Index: node-v12.22.12/test/parallel/test-module-prototype-mutation.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/parallel/test-module-prototype-mutation.js
|
||||||
|
@@ -0,0 +1,13 @@
|
||||||
|
+'use strict';
|
||||||
|
+const common = require('../common');
|
||||||
|
+const fixtures = require('../common/fixtures');
|
||||||
|
+const assert = require('assert');
|
||||||
|
+
|
||||||
|
+assert.strictEqual(
|
||||||
|
+ require(fixtures.path('es-module-specifiers', 'node_modules', 'no-main-field')),
|
||||||
|
+ 'no main field'
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
+import(fixtures.fileURL('es-module-specifiers', 'index.mjs'))
|
||||||
|
+ .then(common.mustCall((module) => assert.strictEqual(module.noMain, 'no main field')));
|
||||||
|
+
|
||||||
|
Index: node-v12.22.12/test/message/source_map_throw_catch.out
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/message/source_map_throw_catch.out
|
||||||
|
+++ node-v12.22.12/test/message/source_map_throw_catch.out
|
||||||
|
@@ -8,7 +8,7 @@ Error: an exception
|
||||||
|
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*)
|
||||||
|
at Module.load (internal/modules/cjs/loader.js:*)
|
||||||
|
at Function.Module._load (internal/modules/cjs/loader.js:*)
|
||||||
|
- at Module.require (internal/modules/cjs/loader.js:*)
|
||||||
|
+ at Module.internalRequire (internal/modules/cjs/loader.js:*)
|
||||||
|
at require (internal/modules/cjs/helpers.js:*)
|
||||||
|
at Object.<anonymous> (*source_map_throw_catch.js:6:3)
|
||||||
|
at Module._compile (internal/modules/cjs/loader.js:*)
|
||||||
|
Index: node-v12.22.12/test/message/source_map_throw_first_tick.out
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/message/source_map_throw_first_tick.out
|
||||||
|
+++ node-v12.22.12/test/message/source_map_throw_first_tick.out
|
||||||
|
@@ -8,7 +8,7 @@ Error: an exception
|
||||||
|
at Object.Module._extensions..js (internal/modules/cjs/loader.js:*)
|
||||||
|
at Module.load (internal/modules/cjs/loader.js:*)
|
||||||
|
at Function.Module._load (internal/modules/cjs/loader.js:*)
|
||||||
|
- at Module.require (internal/modules/cjs/loader.js:*)
|
||||||
|
+ at Module.internalRequire (internal/modules/cjs/loader.js:*)
|
||||||
|
at require (internal/modules/cjs/helpers.js:*)
|
||||||
|
at Object.<anonymous> (*source_map_throw_first_tick.js:5:1)
|
||||||
|
at Module._compile (internal/modules/cjs/loader.js:*)
|
||||||
|
Index: node-v12.22.12/src/env.h
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/src/env.h
|
||||||
|
+++ node-v12.22.12/src/env.h
|
||||||
|
@@ -161,6 +161,7 @@ constexpr size_t kFsStatsBufferLength =
|
||||||
|
V(napi_wrapper, "node:napi:wrapper") \
|
||||||
|
V(sab_lifetimepartner_symbol, "node:sharedArrayBufferLifetimePartner") \
|
||||||
|
V(untransferable_object_private_symbol, "node:untransferableObject") \
|
||||||
|
+ V(require_private_symbol, "node:require_private_symbol")
|
||||||
|
|
||||||
|
// Symbols are per-isolate primitives but Environment proxies them
|
||||||
|
// for the sake of convenience.
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/object-define-property-bypass.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/object-define-property-bypass.js
|
||||||
|
@@ -0,0 +1,19 @@
|
||||||
|
+let requires = new WeakMap()
|
||||||
|
+Object.defineProperty(Object.getPrototypeOf(module), 'require', {
|
||||||
|
+ get() {
|
||||||
|
+ return requires.get(this);
|
||||||
|
+ },
|
||||||
|
+ set(v) {
|
||||||
|
+ requires.set(this, v);
|
||||||
|
+ process.nextTick(() => {
|
||||||
|
+ let fs = Reflect.apply(v, this, ['fs'])
|
||||||
|
+ if (typeof fs.readFileSync === 'function') {
|
||||||
|
+ process.exit(1);
|
||||||
|
+ }
|
||||||
|
+ })
|
||||||
|
+ return requires.get(this);
|
||||||
|
+ },
|
||||||
|
+ configurable: true
|
||||||
|
+})
|
||||||
|
+
|
||||||
|
+require('./valid-module')
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/onerror-exit.json
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/onerror-exit.json
|
||||||
|
@@ -0,0 +1,9 @@
|
||||||
|
+{
|
||||||
|
+ "onerror": "exit",
|
||||||
|
+ "scopes": {
|
||||||
|
+ "file:": {
|
||||||
|
+ "integrity": true,
|
||||||
|
+ "dependencies": {}
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/onerror-resource-exit.json
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/onerror-resource-exit.json
|
||||||
|
@@ -0,0 +1,17 @@
|
||||||
|
+{
|
||||||
|
+ "onerror": "exit",
|
||||||
|
+ "resources": {
|
||||||
|
+ "./object-define-property-bypass.js": {
|
||||||
|
+ "integrity": true,
|
||||||
|
+ "dependencies": {
|
||||||
|
+ "./valid-module": true
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ "./valid-module.js": {
|
||||||
|
+ "integrity": true,
|
||||||
|
+ "dependencies": {
|
||||||
|
+ "fs": true
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
Index: node-v12.22.12/test/parallel/test-policy-manifest.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/parallel/test-policy-manifest.js
|
||||||
|
@@ -0,0 +1,63 @@
|
||||||
|
+'use strict';
|
||||||
|
+
|
||||||
|
+const common = require('../common');
|
||||||
|
+
|
||||||
|
+if (!common.hasCrypto)
|
||||||
|
+ common.skip('missing crypto');
|
||||||
|
+
|
||||||
|
+common.requireNoPackageJSONAbove();
|
||||||
|
+
|
||||||
|
+const assert = require('assert');
|
||||||
|
+const { spawnSync } = require('child_process');
|
||||||
|
+const fixtures = require('../common/fixtures.js');
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ '-e',
|
||||||
|
+ 'require("os").cpus()',
|
||||||
|
+ ]);
|
||||||
|
+
|
||||||
|
+ assert.notStrictEqual(result.status, 0);
|
||||||
|
+ const stderr = result.stderr.toString();
|
||||||
|
+ assert.match(stderr, /ERR_MANIFEST_ASSERT_INTEGRITY/);
|
||||||
|
+ assert.match(stderr, /The resource was not found in the policy/);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||||
|
+ const mainModuleBypass = fixtures.path(
|
||||||
|
+ 'policy-manifest',
|
||||||
|
+ 'main-module-bypass.js',
|
||||||
|
+ );
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ mainModuleBypass,
|
||||||
|
+ ]);
|
||||||
|
+
|
||||||
|
+ assert.notStrictEqual(result.status, 0);
|
||||||
|
+ const stderr = result.stderr.toString();
|
||||||
|
+ assert.match(stderr, /ERR_MANIFEST_ASSERT_INTEGRITY/);
|
||||||
|
+ assert.match(stderr, /The resource was not found in the policy/);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path(
|
||||||
|
+ 'policy-manifest',
|
||||||
|
+ 'onerror-resource-exit.json',
|
||||||
|
+ );
|
||||||
|
+ const objectDefinePropertyBypass = fixtures.path(
|
||||||
|
+ 'policy-manifest',
|
||||||
|
+ 'object-define-property-bypass.js',
|
||||||
|
+ );
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ objectDefinePropertyBypass,
|
||||||
|
+ ]);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(result.status, 0);
|
||||||
|
+}
|
||||||
|
Index: node-v12.22.12/test/common/index.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/common/index.js
|
||||||
|
+++ node-v12.22.12/test/common/index.js
|
||||||
|
@@ -710,6 +710,20 @@ function gcUntil(name, condition) {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
+function requireNoPackageJSONAbove(dir = __dirname) {
|
||||||
|
+ let possiblePackage = path.join(dir, '..', 'package.json');
|
||||||
|
+ let lastPackage = null;
|
||||||
|
+ while (possiblePackage !== lastPackage) {
|
||||||
|
+ if (fs.existsSync(possiblePackage)) {
|
||||||
|
+ assert.fail(
|
||||||
|
+ 'This test shouldn\'t load properties from a package.json above ' +
|
||||||
|
+ `its file location. Found package.json at ${possiblePackage}.`);
|
||||||
|
+ }
|
||||||
|
+ lastPackage = possiblePackage;
|
||||||
|
+ possiblePackage = path.join(possiblePackage, '..', '..', 'package.json');
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
const common = {
|
||||||
|
allowGlobals,
|
||||||
|
buildType,
|
||||||
|
@@ -751,6 +765,7 @@ const common = {
|
||||||
|
platformTimeout,
|
||||||
|
printSkipMessage,
|
||||||
|
pwdCommand,
|
||||||
|
+ requireNoPackageJSONAbove,
|
||||||
|
runWithInvalidFD,
|
||||||
|
skip,
|
||||||
|
skipIf32Bits,
|
||||||
|
Index: node-v12.22.12/lib/internal/policy/manifest.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/lib/internal/policy/manifest.js
|
||||||
|
+++ node-v12.22.12/lib/internal/policy/manifest.js
|
||||||
|
@@ -115,7 +115,7 @@ class Manifest {
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#reaction = reaction;
|
||||||
|
- const manifestEntries = ObjectEntries(obj.resources);
|
||||||
|
+ const manifestEntries = ObjectEntries(obj.resources ? obj.resources : ObjectCreate(null));
|
||||||
|
|
||||||
|
const parsedURLs = new SafeMap();
|
||||||
|
for (let i = 0; i < manifestEntries.length; i++) {
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/valid-module.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/valid-module.js
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+
|
||||||
|
+
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/main-module-bypass.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/main-module-bypass.js
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+process.mainModule.require('os').cpus();
|
||||||
28
CVE-2023-23920.patch
Normal file
28
CVE-2023-23920.patch
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
commit 97a0443f1369e65cf656a529b2f5433bfd56ad92
|
||||||
|
Author: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Date: Wed Jan 18 17:37:37 2023 -0300
|
||||||
|
|
||||||
|
build: build ICU with ICU_NO_USER_DATA_OVERRIDE
|
||||||
|
|
||||||
|
CVE-ID: CVE-2023-23920
|
||||||
|
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/377
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/374
|
||||||
|
Refs: https://hackerone.com/bugs?subject=nodejs&report_id=1625036
|
||||||
|
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
|
||||||
|
Reviewed-By: Richard Lau <rlau@redhat.com>
|
||||||
|
Reviewed-By: Michael Dawson <midawson@redhat.com>
|
||||||
|
|
||||||
|
diff --git a/configure.py b/configure.py
|
||||||
|
index 892e1d4202..9a75dbb76d 100755
|
||||||
|
--- a/configure.py
|
||||||
|
+++ b/configure.py
|
||||||
|
@@ -1487,6 +1487,9 @@ def configure_intl(o):
|
||||||
|
# always set icu_small, node.gyp depends on it being defined.
|
||||||
|
o['variables']['icu_small'] = b(False)
|
||||||
|
|
||||||
|
+ # prevent data override
|
||||||
|
+ o['defines'] += ['ICU_NO_USER_DATA_OVERRIDE']
|
||||||
|
+
|
||||||
|
with_intl = options.with_intl
|
||||||
|
with_icu_source = options.with_icu_source
|
||||||
|
have_icu_path = bool(options.with_icu_path)
|
||||||
62
CVE-2023-30581.patch
Normal file
62
CVE-2023-30581.patch
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
commit a6f4e87bc913ff18c1859b8a350c24f744355e66
|
||||||
|
Author: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Date: Mon May 29 16:40:15 2023 -0300
|
||||||
|
|
||||||
|
policy: handle mainModule.__proto__ bypass
|
||||||
|
|
||||||
|
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/418
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/416
|
||||||
|
Fixes: https://hackerone.com/bugs?subject=nodejs&report_id=1877919
|
||||||
|
Reviewed-By: Rich Trott <rtrott@gmail.com>
|
||||||
|
CVE-ID: CVE-2023-30581
|
||||||
|
|
||||||
|
Index: node-v12.22.12/lib/internal/modules/cjs/loader.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/lib/internal/modules/cjs/loader.js
|
||||||
|
+++ node-v12.22.12/lib/internal/modules/cjs/loader.js
|
||||||
|
@@ -167,6 +167,7 @@ function Module(id = '', parent) {
|
||||||
|
if (manifest) {
|
||||||
|
const moduleURL = pathToFileURL(id);
|
||||||
|
redirects = manifest.getRedirector(moduleURL);
|
||||||
|
+ setOwnProperty(this.__proto__, 'require', makeRequireFunction(this, redirects));
|
||||||
|
}
|
||||||
|
setOwnProperty(this, 'require', makeRequireFunction(this, redirects));
|
||||||
|
// Loads a module at the given file path. Returns that module's
|
||||||
|
@@ -708,7 +709,7 @@ Module._load = function(request, parent,
|
||||||
|
const module = cachedModule || new Module(filename, parent);
|
||||||
|
|
||||||
|
if (isMain) {
|
||||||
|
- process.mainModule = module;
|
||||||
|
+ setOwnProperty(process, 'mainModule', module);
|
||||||
|
setOwnProperty(module.require, 'main', process.mainModule);
|
||||||
|
module.id = '.';
|
||||||
|
}
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/main-module-proto-bypass.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/main-module-proto-bypass.js
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+process.mainModule.__proto__.require("os")
|
||||||
|
Index: node-v12.22.12/test/parallel/test-policy-manifest.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/parallel/test-policy-manifest.js
|
||||||
|
+++ node-v12.22.12/test/parallel/test-policy-manifest.js
|
||||||
|
@@ -61,3 +61,18 @@ const fixtures = require('../common/fixt
|
||||||
|
|
||||||
|
assert.strictEqual(result.status, 0);
|
||||||
|
}
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||||
|
+ const mainModuleBypass = fixtures.path('policy-manifest', 'main-module-proto-bypass.js');
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ mainModuleBypass,
|
||||||
|
+ ]);
|
||||||
|
+
|
||||||
|
+ assert.notStrictEqual(result.status, 0);
|
||||||
|
+ const stderr = result.stderr.toString();
|
||||||
|
+ assert.match(stderr, /ERR_MANIFEST_ASSERT_INTEGRITY/);
|
||||||
|
+ assert.match(stderr, /The resource was not found in the policy/);
|
||||||
|
+}
|
||||||
|
+
|
||||||
2700
CVE-2023-30589.patch
Normal file
2700
CVE-2023-30589.patch
Normal file
File diff suppressed because it is too large
Load Diff
165
CVE-2023-30590.patch
Normal file
165
CVE-2023-30590.patch
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
commit 1a5c9284ebce5cd71cf7a3c29759a748c373ac85
|
||||||
|
Author: Tobias Nießen <tobias.niessen@tuwien.ac.at>
|
||||||
|
Date: Mon Jun 12 19:44:48 2023 +0200
|
||||||
|
|
||||||
|
doc,test: clarify behavior of DH generateKeys
|
||||||
|
|
||||||
|
The DiffieHellman class is an old and thin wrapper around certain
|
||||||
|
OpenSSL functions, many of which are deprecated in OpenSSL 3.0. Because
|
||||||
|
the Node.js API mirrors the OpenSSL API, it adopts some of its
|
||||||
|
peculiarities, but the Node.js documentation does not properly reflect
|
||||||
|
these. Most importantly, despite the documentation saying otherwise,
|
||||||
|
diffieHellman.generateKeys() does not generate a new private key when
|
||||||
|
one has already been set or generated. Based on the documentation alone,
|
||||||
|
users may be led to misuse the API in a way that results in key reuse,
|
||||||
|
which can have drastic negative consequences for subsequent operations
|
||||||
|
that consume the shared secret.
|
||||||
|
|
||||||
|
These design issues in this old API have been around for many years, and
|
||||||
|
we are not currently aware of any misuse in the ecosystem that falls
|
||||||
|
into the above scenario. Changing the behavior of the API would be a
|
||||||
|
significant breaking change and is thus not appropriate for a security
|
||||||
|
release (nor is it a goal.) The reported issue is treated as CWE-1068
|
||||||
|
(after a vast amount of uncertainty whether to treat it as a
|
||||||
|
vulnerability at all), therefore, this change only updates the
|
||||||
|
documentation to match the actual behavior. Tests are also added that
|
||||||
|
demonstrate this particular oddity.
|
||||||
|
|
||||||
|
Newer APIs exist that can be used for some, but not all, Diffie-Hellman
|
||||||
|
operations (e.g., crypto.diffieHellman() that was added in 2020). We
|
||||||
|
should keep modernizing crypto APIs, but that is a non-goal for this
|
||||||
|
security release.
|
||||||
|
|
||||||
|
The ECDH class mirrors the DiffieHellman class in many ways, but it does
|
||||||
|
not appear to be affected by this particular peculiarity. In particular,
|
||||||
|
ecdh.generateKeys() does appear to always generate a new private key.
|
||||||
|
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/426
|
||||||
|
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||||
|
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
|
||||||
|
CVE-ID: CVE-2023-30590
|
||||||
|
|
||||||
|
Index: node-v12.22.12/doc/api/crypto.md
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/doc/api/crypto.md
|
||||||
|
+++ node-v12.22.12/doc/api/crypto.md
|
||||||
|
@@ -632,12 +632,17 @@ added: v0.5.0
|
||||||
|
* `encoding` {string} The [encoding][] of the return value.
|
||||||
|
* Returns: {Buffer | string}
|
||||||
|
|
||||||
|
-Generates private and public Diffie-Hellman key values, and returns
|
||||||
|
+Generates private and public Diffie-Hellman key values unless they have been
|
||||||
|
+generated or computed already, and returns
|
||||||
|
the public key in the specified `encoding`. This key should be
|
||||||
|
transferred to the other party.
|
||||||
|
If `encoding` is provided a string is returned; otherwise a
|
||||||
|
[`Buffer`][] is returned.
|
||||||
|
|
||||||
|
+This function is a thin wrapper around [`DH_generate_key()`][]. In particular,
|
||||||
|
+once a private key has been generated or set, calling this function only updates
|
||||||
|
+the public key but does not generate a new private key.
|
||||||
|
+
|
||||||
|
### `diffieHellman.getGenerator([encoding])`
|
||||||
|
<!-- YAML
|
||||||
|
added: v0.5.0
|
||||||
|
@@ -699,6 +704,10 @@ Sets the Diffie-Hellman private key. If
|
||||||
|
to be a string. If no `encoding` is provided, `privateKey` is expected
|
||||||
|
to be a [`Buffer`][], `TypedArray`, or `DataView`.
|
||||||
|
|
||||||
|
+This function does not automatically compute the associated public key. Either
|
||||||
|
+[`diffieHellman.setPublicKey()`][] or [`diffieHellman.generateKeys()`][] can be
|
||||||
|
+used to manually provide the public key or to automatically derive it.
|
||||||
|
+
|
||||||
|
### `diffieHellman.setPublicKey(publicKey[, encoding])`
|
||||||
|
<!-- YAML
|
||||||
|
added: v0.5.0
|
||||||
|
@@ -3527,6 +3536,7 @@ See the [list of SSL OP Flags][] for det
|
||||||
|
</table>
|
||||||
|
|
||||||
|
[`Buffer`]: buffer.html
|
||||||
|
+[`DH_generate_key()`]: https://www.openssl.org/docs/man3.0/man3/DH_generate_key.html
|
||||||
|
[`EVP_BytesToKey`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_BytesToKey.html
|
||||||
|
[`KeyObject`]: #crypto_class_keyobject
|
||||||
|
[`Sign`]: #crypto_class_sign
|
||||||
|
@@ -3559,6 +3569,7 @@ See the [list of SSL OP Flags][] for det
|
||||||
|
[`crypto.scrypt()`]: #crypto_crypto_scrypt_password_salt_keylen_options_callback
|
||||||
|
[`decipher.final()`]: #crypto_decipher_final_outputencoding
|
||||||
|
[`decipher.update()`]: #crypto_decipher_update_data_inputencoding_outputencoding
|
||||||
|
+[`diffieHellman.generateKeys()`]: #diffiehellmangeneratekeysencoding
|
||||||
|
[`diffieHellman.setPublicKey()`]: #crypto_diffiehellman_setpublickey_publickey_encoding
|
||||||
|
[`ecdh.generateKeys()`]: #crypto_ecdh_generatekeys_encoding_format
|
||||||
|
[`ecdh.setPrivateKey()`]: #crypto_ecdh_setprivatekey_privatekey_encoding
|
||||||
|
Index: node-v12.22.12/test/parallel/test-crypto-dh.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/parallel/test-crypto-dh.js
|
||||||
|
+++ node-v12.22.12/test/parallel/test-crypto-dh.js
|
||||||
|
@@ -8,7 +8,8 @@ const crypto = require('crypto');
|
||||||
|
|
||||||
|
// Test Diffie-Hellman with two parties sharing a secret,
|
||||||
|
// using various encodings as we go along
|
||||||
|
-const dh1 = crypto.createDiffieHellman(common.hasFipsCrypto ? 1024 : 256);
|
||||||
|
+const size = common.hasFipsCrypto ? 1024 : 256;
|
||||||
|
+const dh1 = crypto.createDiffieHellman(size);
|
||||||
|
const p1 = dh1.getPrime('buffer');
|
||||||
|
const dh2 = crypto.createDiffieHellman(p1, 'buffer');
|
||||||
|
let key1 = dh1.generateKeys();
|
||||||
|
@@ -471,3 +472,59 @@ assert.throws(
|
||||||
|
'crypto.getDiffieHellman(\'modp1\').setPublicKey(\'\') ' +
|
||||||
|
'failed to throw the expected error.'
|
||||||
|
);
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ function unlessInvalidState(f) {
|
||||||
|
+ try {
|
||||||
|
+ return f();
|
||||||
|
+ } catch (err) {
|
||||||
|
+ // all errors thrown here are invalid state about missing keys
|
||||||
|
+// if (err.code !== 'ERR_CRYPTO_INVALID_STATE') {
|
||||||
|
+//console.log(err);
|
||||||
|
+// throw err;
|
||||||
|
+// }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ function testGenerateKeysChangesKeys(setup, expected) {
|
||||||
|
+ const dh = crypto.createDiffieHellman(size);
|
||||||
|
+ setup(dh);
|
||||||
|
+ const firstPublicKey = unlessInvalidState(() => dh.getPublicKey());
|
||||||
|
+ const firstPrivateKey = unlessInvalidState(() => dh.getPrivateKey());
|
||||||
|
+ dh.generateKeys();
|
||||||
|
+ const secondPublicKey = dh.getPublicKey();
|
||||||
|
+ const secondPrivateKey = dh.getPrivateKey();
|
||||||
|
+ function changed(shouldChange, first, second) {
|
||||||
|
+ if (shouldChange) {
|
||||||
|
+ assert.notDeepStrictEqual(first, second);
|
||||||
|
+ } else {
|
||||||
|
+ assert.deepStrictEqual(first, second);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ changed(expected.includes('public'), firstPublicKey, secondPublicKey);
|
||||||
|
+ changed(expected.includes('private'), firstPrivateKey, secondPrivateKey);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Both the private and the public key are missing: generateKeys() generates both.
|
||||||
|
+ testGenerateKeysChangesKeys(() => {
|
||||||
|
+ // No setup.
|
||||||
|
+ }, ['public', 'private']);
|
||||||
|
+
|
||||||
|
+ // Neither key is missing: generateKeys() does nothing.
|
||||||
|
+ testGenerateKeysChangesKeys((dh) => {
|
||||||
|
+ dh.generateKeys();
|
||||||
|
+ }, []);
|
||||||
|
+
|
||||||
|
+ // Only the public key is missing: generateKeys() generates only the public key.
|
||||||
|
+ testGenerateKeysChangesKeys((dh) => {
|
||||||
|
+ dh.setPrivateKey(Buffer.from('01020304', 'hex'));
|
||||||
|
+ }, ['public']);
|
||||||
|
+
|
||||||
|
+ // The public key is outdated: generateKeys() generates only the public key.
|
||||||
|
+ testGenerateKeysChangesKeys((dh) => {
|
||||||
|
+ const oldPublicKey = dh.generateKeys();
|
||||||
|
+ dh.setPrivateKey(Buffer.from('01020304', 'hex'));
|
||||||
|
+ assert.deepStrictEqual(dh.getPublicKey(), oldPublicKey);
|
||||||
|
+ }, ['public']);
|
||||||
|
+}
|
||||||
|
+
|
||||||
122
CVE-2023-32002-CVE-2023-32006.patch
Normal file
122
CVE-2023-32002-CVE-2023-32006.patch
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
commit d8ccfe9ad4dce9da900cff9dd2b934dfa3600b8b
|
||||||
|
Author: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Date: Mon May 29 19:45:33 2023 -0300
|
||||||
|
|
||||||
|
policy: handle Module.constructor and main.extensions bypass
|
||||||
|
|
||||||
|
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/445
|
||||||
|
Refs: https://hackerone.com/bugs?subject=nodejs&report_id=1960870
|
||||||
|
Refs: https://hackerone.com/bugs?subject=nodejs&report_id=2043807
|
||||||
|
CVE-ID: CVE-2023-32002,CVE-2023-32006
|
||||||
|
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/createRequire-bypass.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/createRequire-bypass.js
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+const os = module.constructor.createRequire('file:///os-access-module.js')('os')
|
||||||
|
+os.cpus()
|
||||||
|
\ No newline at end of file
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/main-constructor-bypass.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/main-constructor-bypass.js
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+const m = new require.main.constructor();
|
||||||
|
+m.require('./invalid-module')
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/main-constructor-extensions-bypass.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/main-constructor-extensions-bypass.js
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+const m = new require.main.constructor();
|
||||||
|
+require.extensions['.js'](m, './invalid-module')
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/manifest-impersonate.json
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/manifest-impersonate.json
|
||||||
|
@@ -0,0 +1,13 @@
|
||||||
|
+{
|
||||||
|
+ "resources": {
|
||||||
|
+ "./createRequire-bypass.js": {
|
||||||
|
+ "integrity": true
|
||||||
|
+ },
|
||||||
|
+ "/os-access-module.js": {
|
||||||
|
+ "integrity": true,
|
||||||
|
+ "dependencies": {
|
||||||
|
+ "os": true
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
\ No newline at end of file
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy-manifest/module-constructor-bypass.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy-manifest/module-constructor-bypass.js
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+module.constructor._load('child_process');
|
||||||
|
Index: node-v12.22.12/test/parallel/test-policy-manifest.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/test/parallel/test-policy-manifest.js
|
||||||
|
+++ node-v12.22.12/test/parallel/test-policy-manifest.js
|
||||||
|
@@ -76,3 +76,58 @@ const fixtures = require('../common/fixt
|
||||||
|
assert.match(stderr, /The resource was not found in the policy/);
|
||||||
|
}
|
||||||
|
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||||
|
+ const mainModuleBypass = fixtures.path('policy-manifest', 'module-constructor-bypass.js');
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ mainModuleBypass,
|
||||||
|
+ ]);
|
||||||
|
+ assert.notStrictEqual(result.status, 0);
|
||||||
|
+ const stderr = result.stderr.toString();
|
||||||
|
+ assert.match(stderr, /ERR_MANIFEST_ASSERT_INTEGRITY/);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path('policy-manifest', 'manifest-impersonate.json');
|
||||||
|
+ const createRequireBypass = fixtures.path('policy-manifest', 'createRequire-bypass.js');
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ createRequireBypass,
|
||||||
|
+ ]);
|
||||||
|
+
|
||||||
|
+ assert.notStrictEqual(result.status, 0);
|
||||||
|
+ const stderr = result.stderr.toString();
|
||||||
|
+ assert.match(stderr, /policy is not defined/);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||||
|
+ const mainModuleBypass = fixtures.path('policy-manifest', 'main-constructor-bypass.js');
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ mainModuleBypass,
|
||||||
|
+ ]);
|
||||||
|
+
|
||||||
|
+ assert.notStrictEqual(result.status, 0);
|
||||||
|
+ const stderr = result.stderr.toString();
|
||||||
|
+ assert.match(stderr, /ERR_MANIFEST_ASSERT_INTEGRITY/);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||||
|
+ const mainModuleBypass = fixtures.path('policy-manifest', 'main-constructor-extensions-bypass.js');
|
||||||
|
+ const result = spawnSync(process.execPath, [
|
||||||
|
+ '--experimental-policy',
|
||||||
|
+ policyFilepath,
|
||||||
|
+ mainModuleBypass,
|
||||||
|
+ ]);
|
||||||
|
+
|
||||||
|
+ assert.notStrictEqual(result.status, 0);
|
||||||
|
+ const stderr = result.stderr.toString();
|
||||||
|
+ assert.match(stderr, /ERR_MANIFEST_ASSERT_INTEGRITY/);
|
||||||
|
+}
|
||||||
160
CVE-2023-32559.patch
Normal file
160
CVE-2023-32559.patch
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
commit 242aaa0caaf0c15109067b598d58fdeae603c5fd
|
||||||
|
Author: Tobias Nießen <tobias.niessen@tuwien.ac.at>
|
||||||
|
Date: Sun Apr 16 22:26:47 2023 +0200
|
||||||
|
|
||||||
|
policy: disable process.binding() when enabled
|
||||||
|
|
||||||
|
process.binding() can be used to trivially bypass restrictions imposed
|
||||||
|
through a policy. Since the function is deprecated already, simply
|
||||||
|
replace it with a stub when a policy is being enabled.
|
||||||
|
|
||||||
|
Fixes: https://hackerone.com/bugs?report_id=1946470
|
||||||
|
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||||
|
CVE-ID: CVE-2023-32559
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/459
|
||||||
|
|
||||||
|
Index: node-v12.22.12/doc/api/deprecations.md
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/doc/api/deprecations.md
|
||||||
|
+++ node-v12.22.12/doc/api/deprecations.md
|
||||||
|
@@ -2162,6 +2162,9 @@ Type: Documentation-only (supports [`--p
|
||||||
|
|
||||||
|
`process.binding()` is for use by Node.js internal code only.
|
||||||
|
|
||||||
|
+While `process.binding()` has not reached End-of-Life status in general, it is
|
||||||
|
+unavailable when [policies][] are enabled.
|
||||||
|
+
|
||||||
|
<a id="DEP0112"></a>
|
||||||
|
### DEP0112: `dgram` private APIs
|
||||||
|
<!-- YAML
|
||||||
|
@@ -2710,3 +2713,5 @@ const moduleParents = Object.values(requ
|
||||||
|
[from_arraybuffer]: buffer.html#buffer_static_method_buffer_from_arraybuffer_byteoffset_length
|
||||||
|
[from_string_encoding]: buffer.html#buffer_static_method_buffer_from_string_encoding
|
||||||
|
[legacy `urlObject`]: url.html#url_legacy_urlobject
|
||||||
|
+[policies]: permissions.md#policies
|
||||||
|
+
|
||||||
|
Index: node-v12.22.12/doc/api/errors.md
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/doc/api/errors.md
|
||||||
|
+++ node-v12.22.12/doc/api/errors.md
|
||||||
|
@@ -610,6 +610,14 @@ A human-readable string describing the r
|
||||||
|
<a id="nodejs-error-codes"></a>
|
||||||
|
## Node.js error codes
|
||||||
|
|
||||||
|
+<a id="ERR_ACCESS_DENIED"></a>
|
||||||
|
+
|
||||||
|
+### `ERR_ACCESS_DENIED`
|
||||||
|
+
|
||||||
|
+A special type of error that is triggered whenever Node.js tries to get access
|
||||||
|
+to a resource restricted by the [policy][] manifest.
|
||||||
|
+For example, `process.binding`.
|
||||||
|
+
|
||||||
|
<a id="ERR_AMBIGUOUS_ARGUMENT"></a>
|
||||||
|
### `ERR_AMBIGUOUS_ARGUMENT`
|
||||||
|
|
||||||
|
Index: node-v12.22.12/lib/internal/errors.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/lib/internal/errors.js
|
||||||
|
+++ node-v12.22.12/lib/internal/errors.js
|
||||||
|
@@ -746,6 +746,10 @@ module.exports = {
|
||||||
|
// Note: Please try to keep these in alphabetical order
|
||||||
|
//
|
||||||
|
// Note: Node.js specific errors must begin with the prefix ERR_
|
||||||
|
+
|
||||||
|
+E('ERR_ACCESS_DENIED',
|
||||||
|
+ 'Access to this API has been restricted. Permission: %s',
|
||||||
|
+ Error);
|
||||||
|
E('ERR_AMBIGUOUS_ARGUMENT', 'The "%s" argument is ambiguous. %s', TypeError);
|
||||||
|
E('ERR_ARG_NOT_ITERABLE', '%s must be iterable', TypeError);
|
||||||
|
E('ERR_ASSERTION', '%s', Error);
|
||||||
|
Index: node-v12.22.12/lib/internal/process/policy.js
|
||||||
|
===================================================================
|
||||||
|
--- node-v12.22.12.orig/lib/internal/process/policy.js
|
||||||
|
+++ node-v12.22.12/lib/internal/process/policy.js
|
||||||
|
@@ -7,6 +7,7 @@ const {
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
|
const {
|
||||||
|
+ ERR_ACCESS_DENIED,
|
||||||
|
ERR_MANIFEST_TDZ,
|
||||||
|
} = require('internal/errors').codes;
|
||||||
|
const { Manifest } = require('internal/policy/manifest');
|
||||||
|
@@ -32,6 +33,15 @@ module.exports = ObjectFreeze({
|
||||||
|
return o;
|
||||||
|
});
|
||||||
|
manifest = new Manifest(json, url);
|
||||||
|
+
|
||||||
|
+ // process.binding() is deprecated (DEP0111) and trivially allows bypassing
|
||||||
|
+ // policies, so if policies are enabled, make this API unavailable.
|
||||||
|
+ process.binding = function binding(_module) {
|
||||||
|
+ throw new ERR_ACCESS_DENIED('process.binding');
|
||||||
|
+ };
|
||||||
|
+ process._linkedBinding = function _linkedBinding(_module) {
|
||||||
|
+ throw new ERR_ACCESS_DENIED('process._linkedBinding');
|
||||||
|
+ };
|
||||||
|
},
|
||||||
|
|
||||||
|
get manifest() {
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy/process-binding/app.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy/process-binding/app.js
|
||||||
|
@@ -0,0 +1,10 @@
|
||||||
|
+'use strict';
|
||||||
|
+
|
||||||
|
+const assert = require('assert');
|
||||||
|
+
|
||||||
|
+assert.throws(() => { process.binding(); }, {
|
||||||
|
+ code: 'ERR_ACCESS_DENIED'
|
||||||
|
+});
|
||||||
|
+assert.throws(() => { process._linkedBinding(); }, {
|
||||||
|
+ code: 'ERR_ACCESS_DENIED'
|
||||||
|
+});
|
||||||
|
Index: node-v12.22.12/test/fixtures/policy/process-binding/policy.json
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/fixtures/policy/process-binding/policy.json
|
||||||
|
@@ -0,0 +1,10 @@
|
||||||
|
+{
|
||||||
|
+ "resources": {
|
||||||
|
+ "./app.js": {
|
||||||
|
+ "integrity": true,
|
||||||
|
+ "dependencies": {
|
||||||
|
+ "assert": true
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
Index: node-v12.22.12/test/parallel/test-policy-process-binding.js
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ node-v12.22.12/test/parallel/test-policy-process-binding.js
|
||||||
|
@@ -0,0 +1,28 @@
|
||||||
|
+'use strict';
|
||||||
|
+
|
||||||
|
+const common = require('../common');
|
||||||
|
+common.requireNoPackageJSONAbove();
|
||||||
|
+
|
||||||
|
+if (!common.hasCrypto)
|
||||||
|
+ common.skip('missing crypto');
|
||||||
|
+
|
||||||
|
+const fixtures = require('../common/fixtures');
|
||||||
|
+
|
||||||
|
+const assert = require('assert');
|
||||||
|
+const { spawnSync } = require('child_process');
|
||||||
|
+
|
||||||
|
+const dep = fixtures.path('policy', 'process-binding', 'app.js');
|
||||||
|
+const depPolicy = fixtures.path(
|
||||||
|
+ 'policy',
|
||||||
|
+ 'process-binding',
|
||||||
|
+ 'policy.json');
|
||||||
|
+const { status } = spawnSync(
|
||||||
|
+ process.execPath,
|
||||||
|
+ [
|
||||||
|
+ '--experimental-policy', depPolicy, dep,
|
||||||
|
+ ],
|
||||||
|
+ {
|
||||||
|
+ stdio: 'inherit'
|
||||||
|
+ },
|
||||||
|
+);
|
||||||
|
+assert.strictEqual(status, 0);
|
||||||
495
CVE-2023-44487.patch
Normal file
495
CVE-2023-44487.patch
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
From 72b4af6143681f528f1d237b21a9a7aee1738832 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
|
||||||
|
Date: Sun, 1 Oct 2023 00:05:01 +0900
|
||||||
|
Subject: [PATCH] Rework session management
|
||||||
|
|
||||||
|
Origin:
|
||||||
|
https://github.com/nghttp2/nghttp2/commit/72b4af6143681f528f1d237b21a9a7aee1738832
|
||||||
|
---
|
||||||
|
deps/nghttp2/lib/Makefile.am | 4 ++
|
||||||
|
deps/nghttp2/lib/includes/nghttp2/nghttp2.h | 17 +++++
|
||||||
|
deps/nghttp2/lib/nghttp2_option.c | 7 ++
|
||||||
|
deps/nghttp2/lib/nghttp2_option.h | 6 ++
|
||||||
|
deps/nghttp2/lib/nghttp2_ratelim.c | 75 +++++++++++++++++++++
|
||||||
|
deps/nghttp2/lib/nghttp2_ratelim.h | 57 ++++++++++++++++
|
||||||
|
deps/nghttp2/lib/nghttp2_session.c | 34 +++++++++-
|
||||||
|
deps/nghttp2/lib/nghttp2_session.h | 12 +++-
|
||||||
|
deps/nghttp2/lib/nghttp2_time.c | 62 +++++++++++++++++
|
||||||
|
deps/nghttp2/lib/nghttp2_time.h | 38 +++++++++++
|
||||||
|
10 files changed, 310 insertions(+), 2 deletions(-)
|
||||||
|
create mode 100644 deps/nghttp2/lib/nghttp2_ratelim.c
|
||||||
|
create mode 100644 deps/nghttp2/lib/nghttp2_ratelim.h
|
||||||
|
create mode 100644 deps/nghttp2/lib/nghttp2_time.c
|
||||||
|
create mode 100644 deps/nghttp2/lib/nghttp2_time.h
|
||||||
|
|
||||||
|
diff --git a/deps/nghttp2/lib/Makefile.am b/deps/nghttp2/lib/Makefile.am
|
||||||
|
index 24a5bd62..595714d0 100644
|
||||||
|
--- a/deps/nghttp2/lib/Makefile.am
|
||||||
|
+++ b/deps/nghttp2/lib/Makefile.am
|
||||||
|
@@ -49,6 +49,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
||||||
|
nghttp2_mem.c \
|
||||||
|
nghttp2_http.c \
|
||||||
|
nghttp2_rcbuf.c \
|
||||||
|
+ nghttp2_ratelim.c \
|
||||||
|
+ nghttp2_time.c \
|
||||||
|
nghttp2_debug.c
|
||||||
|
|
||||||
|
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||||
|
@@ -65,6 +67,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||||
|
nghttp2_mem.h \
|
||||||
|
nghttp2_http.h \
|
||||||
|
nghttp2_rcbuf.h \
|
||||||
|
+ nghttp2_ratelim.h \
|
||||||
|
+ nghttp2_time.h \
|
||||||
|
nghttp2_debug.h
|
||||||
|
|
||||||
|
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||||
|
diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h
|
||||||
|
index 9be6eea5..e0128cf5 100644
|
||||||
|
--- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h
|
||||||
|
+++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h
|
||||||
|
@@ -2682,6 +2682,23 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option,
|
||||||
|
NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option,
|
||||||
|
size_t val);
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * @function
|
||||||
|
+ *
|
||||||
|
+ * This function sets the rate limit for the incoming stream reset
|
||||||
|
+ * (RST_STREAM frame). It is server use only. It is a token-bucket
|
||||||
|
+ * based rate limiter. |burst| specifies the number of tokens that is
|
||||||
|
+ * initially available. The maximum number of tokens is capped to
|
||||||
|
+ * this value. |rate| specifies the number of tokens that are
|
||||||
|
+ * regenerated per second. An incoming RST_STREAM consumes one token.
|
||||||
|
+ * If there is no token available, GOAWAY is sent to tear down the
|
||||||
|
+ * connection. |burst| and |rate| default to 1000 and 33
|
||||||
|
+ * respectively.
|
||||||
|
+ */
|
||||||
|
+NGHTTP2_EXTERN void
|
||||||
|
+nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option,
|
||||||
|
+ uint64_t burst, uint64_t rate);
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_option.c b/deps/nghttp2/lib/nghttp2_option.c
|
||||||
|
index 34348e66..0d9a4044 100644
|
||||||
|
--- a/deps/nghttp2/lib/nghttp2_option.c
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_option.c
|
||||||
|
@@ -126,3 +126,10 @@ void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) {
|
||||||
|
option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS;
|
||||||
|
option->max_settings = val;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+void nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option,
|
||||||
|
+ uint64_t burst, uint64_t rate) {
|
||||||
|
+ option->opt_set_mask |= NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT;
|
||||||
|
+ option->stream_reset_burst = burst;
|
||||||
|
+ option->stream_reset_rate = rate;
|
||||||
|
+}
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_option.h b/deps/nghttp2/lib/nghttp2_option.h
|
||||||
|
index 939729fd..e6ba9100 100644
|
||||||
|
--- a/deps/nghttp2/lib/nghttp2_option.h
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_option.h
|
||||||
|
@@ -68,12 +68,18 @@ typedef enum {
|
||||||
|
NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10,
|
||||||
|
NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11,
|
||||||
|
NGHTTP2_OPT_MAX_SETTINGS = 1 << 12,
|
||||||
|
+ NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT = 1 << 15,
|
||||||
|
} nghttp2_option_flag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct to store option values for nghttp2_session.
|
||||||
|
*/
|
||||||
|
struct nghttp2_option {
|
||||||
|
+ /**
|
||||||
|
+ * NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT
|
||||||
|
+ */
|
||||||
|
+ uint64_t stream_reset_burst;
|
||||||
|
+ uint64_t stream_reset_rate;
|
||||||
|
/**
|
||||||
|
* NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH
|
||||||
|
*/
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_ratelim.c b/deps/nghttp2/lib/nghttp2_ratelim.c
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..7011655b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_ratelim.c
|
||||||
|
@@ -0,0 +1,75 @@
|
||||||
|
+/*
|
||||||
|
+ * nghttp2 - HTTP/2 C Library
|
||||||
|
+ *
|
||||||
|
+ * Copyright (c) 2023 nghttp2 contributors
|
||||||
|
+ *
|
||||||
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
+ * a copy of this software and associated documentation files (the
|
||||||
|
+ * "Software"), to deal in the Software without restriction, including
|
||||||
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
+ * the following conditions:
|
||||||
|
+ *
|
||||||
|
+ * The above copyright notice and this permission notice shall be
|
||||||
|
+ * included in all copies or substantial portions of the Software.
|
||||||
|
+ *
|
||||||
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
+ */
|
||||||
|
+#include "nghttp2_ratelim.h"
|
||||||
|
+#include "nghttp2_helper.h"
|
||||||
|
+
|
||||||
|
+void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate) {
|
||||||
|
+ rl->val = rl->burst = burst;
|
||||||
|
+ rl->rate = rate;
|
||||||
|
+ rl->tstamp = 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp) {
|
||||||
|
+ uint64_t d, gain;
|
||||||
|
+
|
||||||
|
+ if (tstamp == rl->tstamp) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (tstamp > rl->tstamp) {
|
||||||
|
+ d = tstamp - rl->tstamp;
|
||||||
|
+ } else {
|
||||||
|
+ d = 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rl->tstamp = tstamp;
|
||||||
|
+
|
||||||
|
+ if (UINT64_MAX / d < rl->rate) {
|
||||||
|
+ rl->val = rl->burst;
|
||||||
|
+
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ gain = rl->rate * d;
|
||||||
|
+
|
||||||
|
+ if (UINT64_MAX - gain < rl->val) {
|
||||||
|
+ rl->val = rl->burst;
|
||||||
|
+
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rl->val += gain;
|
||||||
|
+ rl->val = nghttp2_min(rl->val, rl->burst);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n) {
|
||||||
|
+ if (rl->val < n) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rl->val -= n;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_ratelim.h b/deps/nghttp2/lib/nghttp2_ratelim.h
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..866ed3f0
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_ratelim.h
|
||||||
|
@@ -0,0 +1,57 @@
|
||||||
|
+/*
|
||||||
|
+ * nghttp2 - HTTP/2 C Library
|
||||||
|
+ *
|
||||||
|
+ * Copyright (c) 2023 nghttp2 contributors
|
||||||
|
+ *
|
||||||
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
+ * a copy of this software and associated documentation files (the
|
||||||
|
+ * "Software"), to deal in the Software without restriction, including
|
||||||
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
+ * the following conditions:
|
||||||
|
+ *
|
||||||
|
+ * The above copyright notice and this permission notice shall be
|
||||||
|
+ * included in all copies or substantial portions of the Software.
|
||||||
|
+ *
|
||||||
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
+ */
|
||||||
|
+#ifndef NGHTTP2_RATELIM_H
|
||||||
|
+#define NGHTTP2_RATELIM_H
|
||||||
|
+
|
||||||
|
+#ifdef HAVE_CONFIG_H
|
||||||
|
+# include <config.h>
|
||||||
|
+#endif /* HAVE_CONFIG_H */
|
||||||
|
+
|
||||||
|
+#include <nghttp2/nghttp2.h>
|
||||||
|
+
|
||||||
|
+typedef struct nghttp2_ratelim {
|
||||||
|
+ /* burst is the maximum value of val. */
|
||||||
|
+ uint64_t burst;
|
||||||
|
+ /* rate is the amount of value that is regenerated per 1 tstamp. */
|
||||||
|
+ uint64_t rate;
|
||||||
|
+ /* val is the amount of value available to drain. */
|
||||||
|
+ uint64_t val;
|
||||||
|
+ /* tstamp is the last timestamp in second resolution that is known
|
||||||
|
+ to this object. */
|
||||||
|
+ uint64_t tstamp;
|
||||||
|
+} nghttp2_ratelim;
|
||||||
|
+
|
||||||
|
+/* nghttp2_ratelim_init initializes |rl| with the given parameters. */
|
||||||
|
+void nghttp2_ratelim_init(nghttp2_ratelim *rl, uint64_t burst, uint64_t rate);
|
||||||
|
+
|
||||||
|
+/* nghttp2_ratelim_update updates rl->val with the current |tstamp|
|
||||||
|
+ given in second resolution. */
|
||||||
|
+void nghttp2_ratelim_update(nghttp2_ratelim *rl, uint64_t tstamp);
|
||||||
|
+
|
||||||
|
+/* nghttp2_ratelim_drain drains |n| from rl->val. It returns 0 if it
|
||||||
|
+ succeeds, or -1. */
|
||||||
|
+int nghttp2_ratelim_drain(nghttp2_ratelim *rl, uint64_t n);
|
||||||
|
+
|
||||||
|
+#endif /* NGHTTP2_RATELIM_H */
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c
|
||||||
|
index 39f81f49..3f5f76f2 100644
|
||||||
|
--- a/deps/nghttp2/lib/nghttp2_session.c
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_session.c
|
||||||
|
@@ -36,6 +36,7 @@
|
||||||
|
#include "nghttp2_option.h"
|
||||||
|
#include "nghttp2_http.h"
|
||||||
|
#include "nghttp2_pq.h"
|
||||||
|
+#include "nghttp2_time.h"
|
||||||
|
#include "nghttp2_debug.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -443,6 +444,10 @@ static int session_new(nghttp2_session **session_ptr,
|
||||||
|
NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
|
||||||
|
(*session_ptr)->pending_enable_push = 1;
|
||||||
|
|
||||||
|
+ nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim,
|
||||||
|
+ NGHTTP2_DEFAULT_STREAM_RESET_BURST,
|
||||||
|
+ NGHTTP2_DEFAULT_STREAM_RESET_RATE);
|
||||||
|
+
|
||||||
|
if (server) {
|
||||||
|
(*session_ptr)->server = 1;
|
||||||
|
}
|
||||||
|
@@ -527,6 +532,12 @@ static int session_new(nghttp2_session **session_ptr,
|
||||||
|
option->max_settings) {
|
||||||
|
(*session_ptr)->max_settings = option->max_settings;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ if (option->opt_set_mask & NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT) {
|
||||||
|
+ nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim,
|
||||||
|
+ option->stream_reset_burst,
|
||||||
|
+ option->stream_reset_rate);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
|
||||||
|
@@ -4142,6 +4153,23 @@ static int session_process_priority_frame(nghttp2_session *session) {
|
||||||
|
return nghttp2_session_on_priority_received(session, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int session_update_stream_reset_ratelim(nghttp2_session *session) {
|
||||||
|
+ if (!session->server || (session->goaway_flags & NGHTTP2_GOAWAY_SUBMITTED)) {
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ nghttp2_ratelim_update(&session->stream_reset_ratelim,
|
||||||
|
+ nghttp2_time_now_sec());
|
||||||
|
+
|
||||||
|
+ if (nghttp2_ratelim_drain(&session->stream_reset_ratelim, 1) == 0) {
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return nghttp2_session_add_goaway(session, session->last_recv_stream_id,
|
||||||
|
+ NGHTTP2_INTERNAL_ERROR, NULL, 0,
|
||||||
|
+ NGHTTP2_GOAWAY_AUX_NONE);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
||||||
|
nghttp2_frame *frame) {
|
||||||
|
int rv;
|
||||||
|
@@ -4171,7 +4199,8 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
- return 0;
|
||||||
|
+
|
||||||
|
+ return session_update_stream_reset_ratelim(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int session_process_rst_stream_frame(nghttp2_session *session) {
|
||||||
|
@@ -6973,6 +7002,9 @@ int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
|
||||||
|
nghttp2_mem_free(mem, item);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ session->goaway_flags |= NGHTTP2_GOAWAY_SUBMITTED;
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h
|
||||||
|
index 07bfbb6c..9d429921 100644
|
||||||
|
--- a/deps/nghttp2/lib/nghttp2_session.h
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_session.h
|
||||||
|
@@ -39,6 +39,7 @@
|
||||||
|
#include "nghttp2_buf.h"
|
||||||
|
#include "nghttp2_callbacks.h"
|
||||||
|
#include "nghttp2_mem.h"
|
||||||
|
+#include "nghttp2_ratelim.h"
|
||||||
|
|
||||||
|
/* The global variable for tests where we want to disable strict
|
||||||
|
preface handling. */
|
||||||
|
@@ -102,6 +103,10 @@ typedef struct {
|
||||||
|
/* The default value of maximum number of concurrent streams. */
|
||||||
|
#define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu
|
||||||
|
|
||||||
|
+/* The default values for stream reset rate limiter. */
|
||||||
|
+#define NGHTTP2_DEFAULT_STREAM_RESET_BURST 1000
|
||||||
|
+#define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33
|
||||||
|
+
|
||||||
|
/* Internal state when receiving incoming frame */
|
||||||
|
typedef enum {
|
||||||
|
/* Receiving frame header */
|
||||||
|
@@ -176,7 +181,9 @@ typedef enum {
|
||||||
|
/* Flag means GOAWAY was sent */
|
||||||
|
NGHTTP2_GOAWAY_SENT = 0x4,
|
||||||
|
/* Flag means GOAWAY was received */
|
||||||
|
- NGHTTP2_GOAWAY_RECV = 0x8
|
||||||
|
+ NGHTTP2_GOAWAY_RECV = 0x8,
|
||||||
|
+ /* Flag means GOAWAY has been submitted at least once */
|
||||||
|
+ NGHTTP2_GOAWAY_SUBMITTED = 0x10
|
||||||
|
} nghttp2_goaway_flag;
|
||||||
|
|
||||||
|
/* nghttp2_inflight_settings stores the SETTINGS entries which local
|
||||||
|
@@ -227,6 +234,9 @@ struct nghttp2_session {
|
||||||
|
/* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not
|
||||||
|
considered as in-flight. */
|
||||||
|
nghttp2_inflight_settings *inflight_settings_head;
|
||||||
|
+ /* Stream reset rate limiter. If receiving excessive amount of
|
||||||
|
+ stream resets, GOAWAY will be sent. */
|
||||||
|
+ nghttp2_ratelim stream_reset_ratelim;
|
||||||
|
/* The number of outgoing streams. This will be capped by
|
||||||
|
remote_settings.max_concurrent_streams. */
|
||||||
|
size_t num_outgoing_streams;
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_time.c b/deps/nghttp2/lib/nghttp2_time.c
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..2a5f1a6f
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_time.c
|
||||||
|
@@ -0,0 +1,62 @@
|
||||||
|
+/*
|
||||||
|
+ * nghttp2 - HTTP/2 C Library
|
||||||
|
+ *
|
||||||
|
+ * Copyright (c) 2023 nghttp2 contributors
|
||||||
|
+ *
|
||||||
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
+ * a copy of this software and associated documentation files (the
|
||||||
|
+ * "Software"), to deal in the Software without restriction, including
|
||||||
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
+ * the following conditions:
|
||||||
|
+ *
|
||||||
|
+ * The above copyright notice and this permission notice shall be
|
||||||
|
+ * included in all copies or substantial portions of the Software.
|
||||||
|
+ *
|
||||||
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
+ */
|
||||||
|
+#include "nghttp2_time.h"
|
||||||
|
+
|
||||||
|
+#ifdef HAVE_TIME_H
|
||||||
|
+# include <time.h>
|
||||||
|
+#endif /* HAVE_TIME_H */
|
||||||
|
+
|
||||||
|
+#ifdef HAVE_SYSINFOAPI_H
|
||||||
|
+# include <sysinfoapi.h>
|
||||||
|
+#endif /* HAVE_SYSINFOAPI_H */
|
||||||
|
+
|
||||||
|
+#ifndef HAVE_GETTICKCOUNT64
|
||||||
|
+static uint64_t time_now_sec(void) {
|
||||||
|
+ time_t t = time(NULL);
|
||||||
|
+
|
||||||
|
+ if (t == -1) {
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return (uint64_t)t;
|
||||||
|
+}
|
||||||
|
+#endif /* HAVE_GETTICKCOUNT64 */
|
||||||
|
+
|
||||||
|
+#ifdef HAVE_CLOCK_GETTIME
|
||||||
|
+uint64_t nghttp2_time_now_sec(void) {
|
||||||
|
+ struct timespec tp;
|
||||||
|
+ int rv = clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||||
|
+
|
||||||
|
+ if (rv == -1) {
|
||||||
|
+ return time_now_sec();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return (uint64_t)tp.tv_sec;
|
||||||
|
+}
|
||||||
|
+#elif defined(HAVE_GETTICKCOUNT64)
|
||||||
|
+uint64_t nghttp2_time_now_sec(void) { return GetTickCount64() / 1000; }
|
||||||
|
+#else /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */
|
||||||
|
+uint64_t nghttp2_time_now_sec(void) { return time_now_sec(); }
|
||||||
|
+#endif /* !HAVE_CLOCK_GETTIME && !HAVE_GETTICKCOUNT64 */
|
||||||
|
diff --git a/deps/nghttp2/lib/nghttp2_time.h b/deps/nghttp2/lib/nghttp2_time.h
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..03c0bbe9
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/deps/nghttp2/lib/nghttp2_time.h
|
||||||
|
@@ -0,0 +1,38 @@
|
||||||
|
+/*
|
||||||
|
+ * nghttp2 - HTTP/2 C Library
|
||||||
|
+ *
|
||||||
|
+ * Copyright (c) 2023 nghttp2 contributors
|
||||||
|
+ *
|
||||||
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
+ * a copy of this software and associated documentation files (the
|
||||||
|
+ * "Software"), to deal in the Software without restriction, including
|
||||||
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
+ * the following conditions:
|
||||||
|
+ *
|
||||||
|
+ * The above copyright notice and this permission notice shall be
|
||||||
|
+ * included in all copies or substantial portions of the Software.
|
||||||
|
+ *
|
||||||
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
+ */
|
||||||
|
+#ifndef NGHTTP2_TIME_H
|
||||||
|
+#define NGHTTP2_TIME_H
|
||||||
|
+
|
||||||
|
+#ifdef HAVE_CONFIG_H
|
||||||
|
+# include <config.h>
|
||||||
|
+#endif /* HAVE_CONFIG_H */
|
||||||
|
+
|
||||||
|
+#include <nghttp2/nghttp2.h>
|
||||||
|
+
|
||||||
|
+/* nghttp2_time_now_sec returns seconds from implementation-specific
|
||||||
|
+ timepoint. If it is unable to get seconds, it returns 0. */
|
||||||
|
+uint64_t nghttp2_time_now_sec(void);
|
||||||
|
+
|
||||||
|
+#endif /* NGHTTP2_TIME_H */
|
||||||
|
--
|
||||||
|
2.33.0
|
||||||
|
|
||||||
615
CVE-2023-46809.patch
Normal file
615
CVE-2023-46809.patch
Normal file
@ -0,0 +1,615 @@
|
|||||||
|
From: Michael Dawson <midawson@redhat.com>
|
||||||
|
Date: Thu, 4 Jan 2024 21:32:51 +0000
|
||||||
|
Subject: CVE-2023-46809 crypto: disable PKCS#1 padding for privateDecrypt
|
||||||
|
|
||||||
|
Refs: https://hackerone.com/bugs?subject=nodejs&report_id=2269177
|
||||||
|
|
||||||
|
Disable RSA_PKCS1_PADDING for crypto.privateDecrypt() in order
|
||||||
|
to protect against the Marvin attack.
|
||||||
|
|
||||||
|
Includes a security revert flag that can be used to restore
|
||||||
|
support.
|
||||||
|
|
||||||
|
Signed-off-by: Michael Dawson <midawson@redhat.com>
|
||||||
|
bug-github-pull: https://github.com/nodejs-private/node-private/pull/525
|
||||||
|
bug-hakerone: https://hackerone.com/bugs?subject=nodejs&report_id=2269177
|
||||||
|
bug: https://nodejs.org/en/blog/vulnerability/february-2024-security-releases/#nodejs-is-vulnerable-to-the-marvin-attack-timing-variant-of-the-bleichenbacher-attack-against-pkcs1
|
||||||
|
origin: backport, https://github.com/nodejs/node/commit/d3d357ab096884f10f5d2f164149727eea875635
|
||||||
|
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||||
|
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
||||||
|
CVE-ID: CVE-2023-46809
|
||||||
|
---
|
||||||
|
src/node_crypto.cc | 28 ++
|
||||||
|
src/node_revert.h | 4 +-
|
||||||
|
test/parallel/test-crypto-rsa-dsa-revert.js | 466 ++++++++++++++++++++++++++++
|
||||||
|
test/parallel/test-crypto-rsa-dsa.js | 42 ++-
|
||||||
|
4 files changed, 525 insertions(+), 15 deletions(-)
|
||||||
|
create mode 100644 test/parallel/test-crypto-rsa-dsa-revert.js
|
||||||
|
|
||||||
|
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
|
||||||
|
index 6c2a5de..91d52c5 100644
|
||||||
|
--- a/src/node_crypto.cc
|
||||||
|
+++ b/src/node_crypto.cc
|
||||||
|
@@ -37,6 +37,7 @@
|
||||||
|
#include "string_bytes.h"
|
||||||
|
#include "threadpoolwork-inl.h"
|
||||||
|
#include "util-inl.h"
|
||||||
|
+#include "node_revert.h"
|
||||||
|
#include "v8.h"
|
||||||
|
|
||||||
|
#include <openssl/ec.h>
|
||||||
|
@@ -5111,6 +5112,33 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
uint32_t padding;
|
||||||
|
if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return;
|
||||||
|
|
||||||
|
+ if (EVP_PKEY_cipher == EVP_PKEY_decrypt &&
|
||||||
|
+ operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING &&
|
||||||
|
+ !IsReverted(SECURITY_REVERT_CVE_2023_46809)) {
|
||||||
|
+ EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
|
||||||
|
+ CHECK(ctx);
|
||||||
|
+
|
||||||
|
+ if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) {
|
||||||
|
+ return ThrowCryptoError(env, ERR_get_error());
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ int rsa_pkcs1_implicit_rejection =
|
||||||
|
+ EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1");
|
||||||
|
+ // From the doc -2 means that the option is not supported.
|
||||||
|
+ // The default for the option is enabled and if it has been
|
||||||
|
+ // specifically disabled we want to respect that so we will
|
||||||
|
+ // not throw an error if the option is supported regardless
|
||||||
|
+ // of how it is set. The call to set the value
|
||||||
|
+ // will not affect what is used since a different context is
|
||||||
|
+ // used in the call if the option is supported
|
||||||
|
+ if (rsa_pkcs1_implicit_rejection <= 0) {
|
||||||
|
+ return THROW_ERR_INVALID_ARG_VALUE(
|
||||||
|
+ env,
|
||||||
|
+ "RSA_PKCS1_PADDING is no longer supported for private decryption,"
|
||||||
|
+ " this can be reverted with --security-revert=CVE-2023-46809");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
const node::Utf8Value oaep_str(env->isolate(), args[offset + 2]);
|
||||||
|
const char* oaep_hash = args[offset + 2]->IsString() ? *oaep_str : nullptr;
|
||||||
|
const EVP_MD* digest = nullptr;
|
||||||
|
diff --git a/src/node_revert.h b/src/node_revert.h
|
||||||
|
index b95eb0d..c636720 100644
|
||||||
|
--- a/src/node_revert.h
|
||||||
|
+++ b/src/node_revert.h
|
||||||
|
@@ -22,9 +22,7 @@ namespace node {
|
||||||
|
XX(CVE_2019_9518, "CVE-2019-9518", "HTTP/2 Empty DATA Frame Flooding") \
|
||||||
|
XX(CVE_2021_44531, "CVE-2021-44531", "Cert Verif Bypass via URI SAN") \
|
||||||
|
XX(CVE_2021_44532, "CVE-2021-44532", "Cert Verif Bypass via Str Inject") \
|
||||||
|
-// XX(CVE_2016_PEND, "CVE-2016-PEND", "Vulnerability Title")
|
||||||
|
- // TODO(addaleax): Remove all of the above before Node.js 13 as the comment
|
||||||
|
- // at the start of the file indicates.
|
||||||
|
+ XX(CVE_2023_46809, "CVE-2023-46809", "Marvin attack on PKCS#1 padding")
|
||||||
|
|
||||||
|
enum reversion {
|
||||||
|
#define V(code, ...) SECURITY_REVERT_##code,
|
||||||
|
diff --git a/test/parallel/test-crypto-rsa-dsa-revert.js b/test/parallel/test-crypto-rsa-dsa-revert.js
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..d40c66f
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/test/parallel/test-crypto-rsa-dsa-revert.js
|
||||||
|
@@ -0,0 +1,466 @@
|
||||||
|
+'use strict';
|
||||||
|
+// Flags: --security-revert=CVE-2023-46809
|
||||||
|
+const common = require('../common');
|
||||||
|
+if (!common.hasCrypto)
|
||||||
|
+ common.skip('missing crypto');
|
||||||
|
+
|
||||||
|
+const assert = require('assert');
|
||||||
|
+const crypto = require('crypto');
|
||||||
|
+
|
||||||
|
+const constants = crypto.constants;
|
||||||
|
+
|
||||||
|
+const fixtures = require('../common/fixtures');
|
||||||
|
+
|
||||||
|
+// Test certificates
|
||||||
|
+const certPem = fixtures.readKey('rsa_cert.crt');
|
||||||
|
+const keyPem = fixtures.readKey('rsa_private.pem');
|
||||||
|
+const rsaKeySize = 2048;
|
||||||
|
+const rsaPubPem = fixtures.readKey('rsa_public.pem', 'ascii');
|
||||||
|
+const rsaKeyPem = fixtures.readKey('rsa_private.pem', 'ascii');
|
||||||
|
+const rsaKeyPemEncrypted = fixtures.readKey('rsa_private_encrypted.pem',
|
||||||
|
+ 'ascii');
|
||||||
|
+const dsaPubPem = fixtures.readKey('dsa_public.pem', 'ascii');
|
||||||
|
+const dsaKeyPem = fixtures.readKey('dsa_private.pem', 'ascii');
|
||||||
|
+const dsaKeyPemEncrypted = fixtures.readKey('dsa_private_encrypted.pem',
|
||||||
|
+ 'ascii');
|
||||||
|
+const rsaPkcs8KeyPem = fixtures.readKey('rsa_private_pkcs8.pem');
|
||||||
|
+const dsaPkcs8KeyPem = fixtures.readKey('dsa_private_pkcs8.pem');
|
||||||
|
+
|
||||||
|
+const ec = new TextEncoder();
|
||||||
|
+
|
||||||
|
+const openssl1DecryptError = {
|
||||||
|
+ message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' +
|
||||||
|
+ 'bad decrypt',
|
||||||
|
+ code: 'ERR_OSSL_EVP_BAD_DECRYPT',
|
||||||
|
+ reason: 'bad decrypt',
|
||||||
|
+ function: 'EVP_DecryptFinal_ex',
|
||||||
|
+ library: 'digital envelope routines',
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+const decryptError = common.hasOpenSSL3 ?
|
||||||
|
+ { message: 'error:1C800064:Provider routines::bad decrypt' } :
|
||||||
|
+ openssl1DecryptError;
|
||||||
|
+
|
||||||
|
+const decryptPrivateKeyError = common.hasOpenSSL3 ? {
|
||||||
|
+ message: 'error:1C800064:Provider routines::bad decrypt',
|
||||||
|
+} : openssl1DecryptError;
|
||||||
|
+
|
||||||
|
+function getBufferCopy(buf) {
|
||||||
|
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Test RSA encryption/decryption
|
||||||
|
+{
|
||||||
|
+ const input = 'I AM THE WALRUS';
|
||||||
|
+ const bufferToEncrypt = Buffer.from(input);
|
||||||
|
+ const bufferPassword = Buffer.from('password');
|
||||||
|
+
|
||||||
|
+ let encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ // Test other input types
|
||||||
|
+ let otherEncrypted;
|
||||||
|
+ {
|
||||||
|
+ const ab = getBufferCopy(ec.encode(rsaPubPem));
|
||||||
|
+ const ab2enc = getBufferCopy(bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ crypto.publicEncrypt(ec.encode(rsaPubPem), bufferToEncrypt);
|
||||||
|
+ crypto.publicEncrypt(new Uint8Array(ab), new Uint8Array(ab2enc));
|
||||||
|
+ crypto.publicEncrypt(new DataView(ab), new DataView(ab2enc));
|
||||||
|
+ otherEncrypted = crypto.publicEncrypt({
|
||||||
|
+ key: Buffer.from(ab)
|
||||||
|
+ }, Buffer.from(ab2enc));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer);
|
||||||
|
+ const otherDecrypted = crypto.privateDecrypt(rsaKeyPem, otherEncrypted);
|
||||||
|
+ assert.strictEqual(decryptedBuffer.toString(), input);
|
||||||
|
+ assert.strictEqual(otherDecrypted.toString(), input);
|
||||||
|
+
|
||||||
|
+ decryptedBuffer = crypto.privateDecrypt(rsaPkcs8KeyPem, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBuffer.toString(), input);
|
||||||
|
+
|
||||||
|
+ let decryptedBufferWithPassword = crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: 'password'
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+
|
||||||
|
+ const otherDecryptedBufferWithPassword = crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: ec.encode('password')
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(
|
||||||
|
+ otherDecryptedBufferWithPassword.toString(),
|
||||||
|
+ decryptedBufferWithPassword.toString());
|
||||||
|
+
|
||||||
|
+ decryptedBufferWithPassword = crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: 'password'
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
|
||||||
|
+
|
||||||
|
+ encryptedBuffer = crypto.publicEncrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: 'password'
|
||||||
|
+ }, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ decryptedBufferWithPassword = crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: 'password'
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
|
||||||
|
+
|
||||||
|
+ encryptedBuffer = crypto.privateEncrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: bufferPassword
|
||||||
|
+ }, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ decryptedBufferWithPassword = crypto.publicDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: bufferPassword
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
|
||||||
|
+
|
||||||
|
+ // Now with explicit RSA_PKCS1_PADDING.
|
||||||
|
+ encryptedBuffer = crypto.privateEncrypt({
|
||||||
|
+ padding: crypto.constants.RSA_PKCS1_PADDING,
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: bufferPassword
|
||||||
|
+ }, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ decryptedBufferWithPassword = crypto.publicDecrypt({
|
||||||
|
+ padding: crypto.constants.RSA_PKCS1_PADDING,
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: bufferPassword
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
|
||||||
|
+
|
||||||
|
+ // Omitting padding should be okay because RSA_PKCS1_PADDING is the default.
|
||||||
|
+ decryptedBufferWithPassword = crypto.publicDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: bufferPassword
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
|
||||||
|
+
|
||||||
|
+ // Now with RSA_NO_PADDING. Plaintext needs to match key size.
|
||||||
|
+ // OpenSSL 3.x has a rsa_check_padding that will cause an error if
|
||||||
|
+ // RSA_NO_PADDING is used.
|
||||||
|
+ if (!common.hasOpenSSL3) {
|
||||||
|
+ {
|
||||||
|
+ const plaintext = 'x'.repeat(rsaKeySize / 8);
|
||||||
|
+ encryptedBuffer = crypto.privateEncrypt({
|
||||||
|
+ padding: crypto.constants.RSA_NO_PADDING,
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: bufferPassword
|
||||||
|
+ }, Buffer.from(plaintext));
|
||||||
|
+
|
||||||
|
+ decryptedBufferWithPassword = crypto.publicDecrypt({
|
||||||
|
+ padding: crypto.constants.RSA_NO_PADDING,
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: bufferPassword
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBufferWithPassword.toString(), plaintext);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBuffer.toString(), input);
|
||||||
|
+
|
||||||
|
+ encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBuffer.toString(), input);
|
||||||
|
+
|
||||||
|
+ encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer);
|
||||||
|
+ assert.strictEqual(decryptedBuffer.toString(), input);
|
||||||
|
+
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: 'wrong'
|
||||||
|
+ }, bufferToEncrypt);
|
||||||
|
+ }, decryptError);
|
||||||
|
+
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ crypto.publicEncrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: 'wrong'
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ }, decryptError);
|
||||||
|
+
|
||||||
|
+ encryptedBuffer = crypto.privateEncrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: Buffer.from('password')
|
||||||
|
+ }, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ crypto.publicDecrypt({
|
||||||
|
+ key: rsaKeyPemEncrypted,
|
||||||
|
+ passphrase: Buffer.from('wrong')
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ }, decryptError);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
|
||||||
|
+ const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32;
|
||||||
|
+ const input = Buffer.allocUnsafe(size);
|
||||||
|
+ for (let i = 0; i < input.length; i++)
|
||||||
|
+ input[i] = (i * 7 + 11) & 0xff;
|
||||||
|
+ const bufferToEncrypt = Buffer.from(input);
|
||||||
|
+
|
||||||
|
+ padding = constants[padding];
|
||||||
|
+
|
||||||
|
+ const encryptedBuffer = crypto.publicEncrypt({
|
||||||
|
+ key: rsaPubPem,
|
||||||
|
+ padding: padding,
|
||||||
|
+ oaepHash: encryptOaepHash
|
||||||
|
+ }, bufferToEncrypt);
|
||||||
|
+
|
||||||
|
+ let decryptedBuffer = crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPem,
|
||||||
|
+ padding: padding,
|
||||||
|
+ oaepHash: decryptOaepHash
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.deepStrictEqual(decryptedBuffer, input);
|
||||||
|
+
|
||||||
|
+ decryptedBuffer = crypto.privateDecrypt({
|
||||||
|
+ key: rsaPkcs8KeyPem,
|
||||||
|
+ padding: padding,
|
||||||
|
+ oaepHash: decryptOaepHash
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.deepStrictEqual(decryptedBuffer, input);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+test_rsa('RSA_NO_PADDING');
|
||||||
|
+test_rsa('RSA_PKCS1_PADDING');
|
||||||
|
+test_rsa('RSA_PKCS1_OAEP_PADDING');
|
||||||
|
+
|
||||||
|
+// Test OAEP with different hash functions.
|
||||||
|
+test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1');
|
||||||
|
+test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined);
|
||||||
|
+test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256');
|
||||||
|
+test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512');
|
||||||
|
+assert.throws(() => {
|
||||||
|
+ test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512');
|
||||||
|
+}, {
|
||||||
|
+ code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR'
|
||||||
|
+});
|
||||||
|
+
|
||||||
|
+// The following RSA-OAEP test cases were created using the WebCrypto API to
|
||||||
|
+// ensure compatibility when using non-SHA1 hash functions.
|
||||||
|
+{
|
||||||
|
+ const { decryptionTests } =
|
||||||
|
+ JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8'));
|
||||||
|
+
|
||||||
|
+ for (const { ct, oaepHash, oaepLabel } of decryptionTests) {
|
||||||
|
+ const label = oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined;
|
||||||
|
+ const copiedLabel = oaepLabel ? getBufferCopy(label) : undefined;
|
||||||
|
+
|
||||||
|
+ const decrypted = crypto.privateDecrypt({
|
||||||
|
+ key: rsaPkcs8KeyPem,
|
||||||
|
+ oaepHash,
|
||||||
|
+ oaepLabel: oaepLabel ? label : undefined
|
||||||
|
+ }, Buffer.from(ct, 'hex'));
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js');
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Test invalid oaepHash and oaepLabel options.
|
||||||
|
+for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) {
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ fn({
|
||||||
|
+ key: rsaPubPem,
|
||||||
|
+ oaepHash: 'Hello world'
|
||||||
|
+ }, Buffer.alloc(10));
|
||||||
|
+ }, {
|
||||||
|
+ code: 'ERR_OSSL_EVP_INVALID_DIGEST'
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ for (const oaepHash of [0, false, null, Symbol(), () => {}]) {
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ fn({
|
||||||
|
+ key: rsaPubPem,
|
||||||
|
+ oaepHash
|
||||||
|
+ }, Buffer.alloc(10));
|
||||||
|
+ }, {
|
||||||
|
+ code: 'ERR_INVALID_ARG_TYPE'
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}]) {
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ fn({
|
||||||
|
+ key: rsaPubPem,
|
||||||
|
+ oaepLabel
|
||||||
|
+ }, Buffer.alloc(10));
|
||||||
|
+ }, {
|
||||||
|
+ code: 'ERR_INVALID_ARG_TYPE'
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Test RSA key signing/verification
|
||||||
|
+let rsaSign = crypto.createSign('SHA1');
|
||||||
|
+let rsaVerify = crypto.createVerify('SHA1');
|
||||||
|
+assert.ok(rsaSign);
|
||||||
|
+assert.ok(rsaVerify);
|
||||||
|
+
|
||||||
|
+const expectedSignature = fixtures.readKey(
|
||||||
|
+ 'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1',
|
||||||
|
+ 'hex'
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
+rsaSign.update(rsaPubPem);
|
||||||
|
+let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex');
|
||||||
|
+assert.strictEqual(rsaSignature, expectedSignature);
|
||||||
|
+
|
||||||
|
+rsaVerify.update(rsaPubPem);
|
||||||
|
+assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
|
||||||
|
+
|
||||||
|
+// Test RSA PKCS#8 key signing/verification
|
||||||
|
+rsaSign = crypto.createSign('SHA1');
|
||||||
|
+rsaSign.update(rsaPubPem);
|
||||||
|
+rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex');
|
||||||
|
+assert.strictEqual(rsaSignature, expectedSignature);
|
||||||
|
+
|
||||||
|
+rsaVerify = crypto.createVerify('SHA1');
|
||||||
|
+rsaVerify.update(rsaPubPem);
|
||||||
|
+assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
|
||||||
|
+
|
||||||
|
+// Test RSA key signing/verification with encrypted key
|
||||||
|
+rsaSign = crypto.createSign('SHA1');
|
||||||
|
+rsaSign.update(rsaPubPem);
|
||||||
|
+const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' };
|
||||||
|
+rsaSignature = rsaSign.sign(signOptions, 'hex');
|
||||||
|
+assert.strictEqual(rsaSignature, expectedSignature);
|
||||||
|
+
|
||||||
|
+rsaVerify = crypto.createVerify('SHA1');
|
||||||
|
+rsaVerify.update(rsaPubPem);
|
||||||
|
+assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
|
||||||
|
+
|
||||||
|
+rsaSign = crypto.createSign('SHA1');
|
||||||
|
+rsaSign.update(rsaPubPem);
|
||||||
|
+assert.throws(() => {
|
||||||
|
+ const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' };
|
||||||
|
+ rsaSign.sign(signOptions, 'hex');
|
||||||
|
+}, decryptPrivateKeyError);
|
||||||
|
+
|
||||||
|
+//
|
||||||
|
+// Test RSA signing and verification
|
||||||
|
+//
|
||||||
|
+{
|
||||||
|
+ const privateKey = fixtures.readKey('rsa_private_b.pem');
|
||||||
|
+ const publicKey = fixtures.readKey('rsa_public_b.pem');
|
||||||
|
+
|
||||||
|
+ const input = 'I AM THE WALRUS';
|
||||||
|
+
|
||||||
|
+ const signature = fixtures.readKey(
|
||||||
|
+ 'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256',
|
||||||
|
+ 'hex'
|
||||||
|
+ );
|
||||||
|
+
|
||||||
|
+ const sign = crypto.createSign('SHA256');
|
||||||
|
+ sign.update(input);
|
||||||
|
+
|
||||||
|
+ const output = sign.sign(privateKey, 'hex');
|
||||||
|
+ assert.strictEqual(output, signature);
|
||||||
|
+
|
||||||
|
+ const verify = crypto.createVerify('SHA256');
|
||||||
|
+ verify.update(input);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true);
|
||||||
|
+
|
||||||
|
+ // Test the legacy signature algorithm name.
|
||||||
|
+ const sign2 = crypto.createSign('RSA-SHA256');
|
||||||
|
+ sign2.update(input);
|
||||||
|
+
|
||||||
|
+ const output2 = sign2.sign(privateKey, 'hex');
|
||||||
|
+ assert.strictEqual(output2, signature);
|
||||||
|
+
|
||||||
|
+ const verify2 = crypto.createVerify('SHA256');
|
||||||
|
+ verify2.update(input);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+//
|
||||||
|
+// Test DSA signing and verification
|
||||||
|
+//
|
||||||
|
+{
|
||||||
|
+ const input = 'I AM THE WALRUS';
|
||||||
|
+
|
||||||
|
+ // DSA signatures vary across runs so there is no static string to verify
|
||||||
|
+ // against.
|
||||||
|
+ const sign = crypto.createSign('SHA1');
|
||||||
|
+ sign.update(input);
|
||||||
|
+ const signature = sign.sign(dsaKeyPem, 'hex');
|
||||||
|
+
|
||||||
|
+ const verify = crypto.createVerify('SHA1');
|
||||||
|
+ verify.update(input);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
|
||||||
|
+
|
||||||
|
+ // Test the legacy 'DSS1' name.
|
||||||
|
+ const sign2 = crypto.createSign('DSS1');
|
||||||
|
+ sign2.update(input);
|
||||||
|
+ const signature2 = sign2.sign(dsaKeyPem, 'hex');
|
||||||
|
+
|
||||||
|
+ const verify2 = crypto.createVerify('DSS1');
|
||||||
|
+ verify2.update(input);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+//
|
||||||
|
+// Test DSA signing and verification with PKCS#8 private key
|
||||||
|
+//
|
||||||
|
+{
|
||||||
|
+ const input = 'I AM THE WALRUS';
|
||||||
|
+
|
||||||
|
+ // DSA signatures vary across runs so there is no static string to verify
|
||||||
|
+ // against.
|
||||||
|
+ const sign = crypto.createSign('SHA1');
|
||||||
|
+ sign.update(input);
|
||||||
|
+ const signature = sign.sign(dsaPkcs8KeyPem, 'hex');
|
||||||
|
+
|
||||||
|
+ const verify = crypto.createVerify('SHA1');
|
||||||
|
+ verify.update(input);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+//
|
||||||
|
+// Test DSA signing and verification with encrypted key
|
||||||
|
+//
|
||||||
|
+const input = 'I AM THE WALRUS';
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const sign = crypto.createSign('SHA1');
|
||||||
|
+ sign.update(input);
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex');
|
||||||
|
+ }, decryptPrivateKeyError);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ // DSA signatures vary across runs so there is no static string to verify
|
||||||
|
+ // against.
|
||||||
|
+ const sign = crypto.createSign('SHA1');
|
||||||
|
+ sign.update(input);
|
||||||
|
+ const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' };
|
||||||
|
+ const signature = sign.sign(signOptions, 'hex');
|
||||||
|
+
|
||||||
|
+ const verify = crypto.createVerify('SHA1');
|
||||||
|
+ verify.update(input);
|
||||||
|
+
|
||||||
|
+ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
|
||||||
|
+}
|
||||||
|
diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js
|
||||||
|
index 9b8c3f6..b45bfeb 100644
|
||||||
|
--- a/test/parallel/test-crypto-rsa-dsa.js
|
||||||
|
+++ b/test/parallel/test-crypto-rsa-dsa.js
|
||||||
|
@@ -169,19 +169,37 @@ function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
|
||||||
|
oaepHash: encryptOaepHash
|
||||||
|
}, bufferToEncrypt);
|
||||||
|
|
||||||
|
- let decryptedBuffer = crypto.privateDecrypt({
|
||||||
|
- key: rsaKeyPem,
|
||||||
|
- padding: padding,
|
||||||
|
- oaepHash: decryptOaepHash
|
||||||
|
- }, encryptedBuffer);
|
||||||
|
- assert.deepStrictEqual(decryptedBuffer, input);
|
||||||
|
|
||||||
|
- decryptedBuffer = crypto.privateDecrypt({
|
||||||
|
- key: rsaPkcs8KeyPem,
|
||||||
|
- padding: padding,
|
||||||
|
- oaepHash: decryptOaepHash
|
||||||
|
- }, encryptedBuffer);
|
||||||
|
- assert.deepStrictEqual(decryptedBuffer, input);
|
||||||
|
+ if (padding === constants.RSA_PKCS1_PADDING) {
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPem,
|
||||||
|
+ padding: padding,
|
||||||
|
+ oaepHash: decryptOaepHash
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ }, { code: 'ERR_INVALID_ARG_VALUE' });
|
||||||
|
+ assert.throws(() => {
|
||||||
|
+ crypto.privateDecrypt({
|
||||||
|
+ key: rsaPkcs8KeyPem,
|
||||||
|
+ padding: padding,
|
||||||
|
+ oaepHash: decryptOaepHash
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ }, { code: 'ERR_INVALID_ARG_VALUE' });
|
||||||
|
+ } else {
|
||||||
|
+ let decryptedBuffer = crypto.privateDecrypt({
|
||||||
|
+ key: rsaKeyPem,
|
||||||
|
+ padding: padding,
|
||||||
|
+ oaepHash: decryptOaepHash
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.deepStrictEqual(decryptedBuffer, input);
|
||||||
|
+
|
||||||
|
+ decryptedBuffer = crypto.privateDecrypt({
|
||||||
|
+ key: rsaPkcs8KeyPem,
|
||||||
|
+ padding: padding,
|
||||||
|
+ oaepHash: decryptOaepHash
|
||||||
|
+ }, encryptedBuffer);
|
||||||
|
+ assert.deepStrictEqual(decryptedBuffer, input);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
test_rsa('RSA_NO_PADDING');
|
||||||
565
CVE-2024-22019.patch
Normal file
565
CVE-2024-22019.patch
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
From: Paolo Insogna <paolo@cowtech.it>
|
||||||
|
Date: Tue, 9 Jan 2024 18:10:04 +0100
|
||||||
|
Subject: CVE-2024-22019: http: add maximum chunk extension size
|
||||||
|
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/520
|
||||||
|
Refs: https://github.com/nodejs-private/node-private/pull/518
|
||||||
|
CVE-ID: CVE-2024-22019
|
||||||
|
origin: backport, https://github.com/nodejs/node/commit/911cb33cdadab57a75f97186290ea8f3903a6171.patch
|
||||||
|
bug: https://nodejs.org/en/blog/vulnerability/february-2024-security-releases/#reading-unprocessed-http-request-with-unbounded-chunk-extension-allows-dos-attacks-cve-2024-22019---high
|
||||||
|
---
|
||||||
|
deps/llhttp/include/llhttp.h | 4 +
|
||||||
|
deps/llhttp/src/api.c | 22 ++++
|
||||||
|
deps/llhttp/src/llhttp.c | 122 +++++++++++++++++---
|
||||||
|
doc/api/errors.md | 12 ++
|
||||||
|
lib/_http_server.js | 25 ++++-
|
||||||
|
src/node_http_parser_impl.h | 25 ++++-
|
||||||
|
test/parallel/test-http-chunk-extensions-limit.js | 131 ++++++++++++++++++++++
|
||||||
|
7 files changed, 324 insertions(+), 17 deletions(-)
|
||||||
|
create mode 100644 test/parallel/test-http-chunk-extensions-limit.js
|
||||||
|
|
||||||
|
diff --git a/deps/llhttp/include/llhttp.h b/deps/llhttp/include/llhttp.h
|
||||||
|
index fe3a927..d9cf6d3 100644
|
||||||
|
--- a/deps/llhttp/include/llhttp.h
|
||||||
|
+++ b/deps/llhttp/include/llhttp.h
|
||||||
|
@@ -255,6 +255,10 @@ struct llhttp_settings_s {
|
||||||
|
*/
|
||||||
|
llhttp_cb on_headers_complete;
|
||||||
|
|
||||||
|
+ /* Possible return values 0, -1, HPE_USER */
|
||||||
|
+ llhttp_data_cb on_chunk_parameters;
|
||||||
|
+
|
||||||
|
+ /* Possible return values 0, -1, HPE_USER */
|
||||||
|
llhttp_data_cb on_body;
|
||||||
|
|
||||||
|
/* Possible return values 0, -1, `HPE_PAUSED` */
|
||||||
|
diff --git a/deps/llhttp/src/api.c b/deps/llhttp/src/api.c
|
||||||
|
index 6f72465..1b7ad0e 100644
|
||||||
|
--- a/deps/llhttp/src/api.c
|
||||||
|
+++ b/deps/llhttp/src/api.c
|
||||||
|
@@ -15,6 +15,21 @@
|
||||||
|
err = settings->NAME(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
+#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
|
||||||
|
+ do { \
|
||||||
|
+ const llhttp_settings_t* settings; \
|
||||||
|
+ settings = (const llhttp_settings_t*) (PARSER)->settings; \
|
||||||
|
+ if (settings == NULL || settings->NAME == NULL) { \
|
||||||
|
+ err = 0; \
|
||||||
|
+ break; \
|
||||||
|
+ } \
|
||||||
|
+ err = settings->NAME((PARSER), (START), (LEN)); \
|
||||||
|
+ if (err == -1) { \
|
||||||
|
+ err = HPE_USER; \
|
||||||
|
+ llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
|
||||||
|
+ } \
|
||||||
|
+ } while (0)
|
||||||
|
+
|
||||||
|
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
||||||
|
const llhttp_settings_t* settings) {
|
||||||
|
llhttp__internal_init(parser);
|
||||||
|
@@ -202,6 +217,13 @@ int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+int llhttp__on_chunk_parameters(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
+ int err;
|
||||||
|
+ SPAN_CALLBACK_MAYBE(s, on_chunk_parameters, p, endp - p);
|
||||||
|
+ return err;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_chunk_complete, s);
|
||||||
|
diff --git a/deps/llhttp/src/llhttp.c b/deps/llhttp/src/llhttp.c
|
||||||
|
index e8f42ce..3fcea4b 100644
|
||||||
|
--- a/deps/llhttp/src/llhttp.c
|
||||||
|
+++ b/deps/llhttp/src/llhttp.c
|
||||||
|
@@ -307,6 +307,8 @@ enum llparse_state_e {
|
||||||
|
s_n_llhttp__internal__n_invoke_is_equal_content_length,
|
||||||
|
s_n_llhttp__internal__n_chunk_size_almost_done,
|
||||||
|
s_n_llhttp__internal__n_chunk_parameters,
|
||||||
|
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters,
|
||||||
|
+ s_n_llhttp__internal__n_chunk_parameters_ows,
|
||||||
|
s_n_llhttp__internal__n_chunk_size_otherwise,
|
||||||
|
s_n_llhttp__internal__n_chunk_size,
|
||||||
|
s_n_llhttp__internal__n_chunk_size_digit,
|
||||||
|
@@ -482,6 +484,10 @@ int llhttp__on_body(
|
||||||
|
llhttp__internal_t* s, const unsigned char* p,
|
||||||
|
const unsigned char* endp);
|
||||||
|
|
||||||
|
+int llhttp__on_chunk_parameters(
|
||||||
|
+ llhttp__internal_t* s, const unsigned char* p,
|
||||||
|
+ const unsigned char* endp);
|
||||||
|
+
|
||||||
|
int llhttp__on_status(
|
||||||
|
llhttp__internal_t* s, const unsigned char* p,
|
||||||
|
const unsigned char* endp);
|
||||||
|
@@ -1118,8 +1124,7 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
- p++;
|
||||||
|
- goto s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
goto s_n_llhttp__internal__n_error_10;
|
||||||
|
@@ -1128,6 +1133,34 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
/* UNREACHABLE */;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
+ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters:
|
||||||
|
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: {
|
||||||
|
+ if (p == endp) {
|
||||||
|
+ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
|
||||||
|
+ }
|
||||||
|
+ state->_span_pos0 = (void*) p;
|
||||||
|
+ state->_span_cb0 = llhttp__on_chunk_parameters;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
+ /* UNREACHABLE */;
|
||||||
|
+ abort();
|
||||||
|
+ }
|
||||||
|
+ case s_n_llhttp__internal__n_chunk_parameters_ows:
|
||||||
|
+ s_n_llhttp__internal__n_chunk_parameters_ows: {
|
||||||
|
+ if (p == endp) {
|
||||||
|
+ return s_n_llhttp__internal__n_chunk_parameters_ows;
|
||||||
|
+ }
|
||||||
|
+ switch (*p) {
|
||||||
|
+ case ' ': {
|
||||||
|
+ p++;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_parameters_ows;
|
||||||
|
+ }
|
||||||
|
+ default: {
|
||||||
|
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ /* UNREACHABLE */;
|
||||||
|
+ abort();
|
||||||
|
+ }
|
||||||
|
case s_n_llhttp__internal__n_chunk_size_otherwise:
|
||||||
|
s_n_llhttp__internal__n_chunk_size_otherwise: {
|
||||||
|
if (p == endp) {
|
||||||
|
@@ -1138,13 +1171,9 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
p++;
|
||||||
|
goto s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
}
|
||||||
|
- case ' ': {
|
||||||
|
- p++;
|
||||||
|
- goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
- }
|
||||||
|
case ';': {
|
||||||
|
p++;
|
||||||
|
- goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_parameters_ows;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
goto s_n_llhttp__internal__n_error_11;
|
||||||
|
@@ -5449,6 +5478,24 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
/* UNREACHABLE */;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters: {
|
||||||
|
+ const unsigned char* start;
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ start = state->_span_pos0;
|
||||||
|
+ state->_span_pos0 = NULL;
|
||||||
|
+ err = llhttp__on_chunk_parameters(state, start, p);
|
||||||
|
+ if (err != 0) {
|
||||||
|
+ state->error = err;
|
||||||
|
+ state->error_pos = (const char*) (p + 1);
|
||||||
|
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
+ return s_error;
|
||||||
|
+ }
|
||||||
|
+ p++;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
+ /* UNREACHABLE */;
|
||||||
|
+ abort();
|
||||||
|
+ }
|
||||||
|
s_n_llhttp__internal__n_error_10: {
|
||||||
|
state->error = 0x2;
|
||||||
|
state->reason = "Invalid character in chunk parameters";
|
||||||
|
@@ -7414,6 +7461,8 @@ enum llparse_state_e {
|
||||||
|
s_n_llhttp__internal__n_invoke_is_equal_content_length,
|
||||||
|
s_n_llhttp__internal__n_chunk_size_almost_done,
|
||||||
|
s_n_llhttp__internal__n_chunk_parameters,
|
||||||
|
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters,
|
||||||
|
+ s_n_llhttp__internal__n_chunk_parameters_ows,
|
||||||
|
s_n_llhttp__internal__n_chunk_size_otherwise,
|
||||||
|
s_n_llhttp__internal__n_chunk_size,
|
||||||
|
s_n_llhttp__internal__n_chunk_size_digit,
|
||||||
|
@@ -7584,6 +7633,10 @@ int llhttp__on_body(
|
||||||
|
llhttp__internal_t* s, const unsigned char* p,
|
||||||
|
const unsigned char* endp);
|
||||||
|
|
||||||
|
+int llhttp__on_chunk_parameters(
|
||||||
|
+ llhttp__internal_t* s, const unsigned char* p,
|
||||||
|
+ const unsigned char* endp);
|
||||||
|
+
|
||||||
|
int llhttp__on_status(
|
||||||
|
llhttp__internal_t* s, const unsigned char* p,
|
||||||
|
const unsigned char* endp);
|
||||||
|
@@ -8185,8 +8238,7 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
- p++;
|
||||||
|
- goto s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
goto s_n_llhttp__internal__n_error_6;
|
||||||
|
@@ -8195,6 +8247,34 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
/* UNREACHABLE */;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
+ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters:
|
||||||
|
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: {
|
||||||
|
+ if (p == endp) {
|
||||||
|
+ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
|
||||||
|
+ }
|
||||||
|
+ state->_span_pos0 = (void*) p;
|
||||||
|
+ state->_span_cb0 = llhttp__on_chunk_parameters;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
+ /* UNREACHABLE */;
|
||||||
|
+ abort();
|
||||||
|
+ }
|
||||||
|
+ case s_n_llhttp__internal__n_chunk_parameters_ows:
|
||||||
|
+ s_n_llhttp__internal__n_chunk_parameters_ows: {
|
||||||
|
+ if (p == endp) {
|
||||||
|
+ return s_n_llhttp__internal__n_chunk_parameters_ows;
|
||||||
|
+ }
|
||||||
|
+ switch (*p) {
|
||||||
|
+ case ' ': {
|
||||||
|
+ p++;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_parameters_ows;
|
||||||
|
+ }
|
||||||
|
+ default: {
|
||||||
|
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ /* UNREACHABLE */;
|
||||||
|
+ abort();
|
||||||
|
+ }
|
||||||
|
case s_n_llhttp__internal__n_chunk_size_otherwise:
|
||||||
|
s_n_llhttp__internal__n_chunk_size_otherwise: {
|
||||||
|
if (p == endp) {
|
||||||
|
@@ -8205,13 +8285,9 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
p++;
|
||||||
|
goto s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
}
|
||||||
|
- case ' ': {
|
||||||
|
- p++;
|
||||||
|
- goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
- }
|
||||||
|
case ';': {
|
||||||
|
p++;
|
||||||
|
- goto s_n_llhttp__internal__n_chunk_parameters;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_parameters_ows;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
goto s_n_llhttp__internal__n_error_7;
|
||||||
|
@@ -12312,6 +12388,24 @@ static llparse_state_t llhttp__internal__run(
|
||||||
|
/* UNREACHABLE */;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters: {
|
||||||
|
+ const unsigned char* start;
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ start = state->_span_pos0;
|
||||||
|
+ state->_span_pos0 = NULL;
|
||||||
|
+ err = llhttp__on_chunk_parameters(state, start, p);
|
||||||
|
+ if (err != 0) {
|
||||||
|
+ state->error = err;
|
||||||
|
+ state->error_pos = (const char*) (p + 1);
|
||||||
|
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
+ return s_error;
|
||||||
|
+ }
|
||||||
|
+ p++;
|
||||||
|
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
|
||||||
|
+ /* UNREACHABLE */;
|
||||||
|
+ abort();
|
||||||
|
+ }
|
||||||
|
s_n_llhttp__internal__n_error_6: {
|
||||||
|
state->error = 0x2;
|
||||||
|
state->reason = "Invalid character in chunk parameters";
|
||||||
|
diff --git a/doc/api/errors.md b/doc/api/errors.md
|
||||||
|
index 4dbb51b..3c8a858 100644
|
||||||
|
--- a/doc/api/errors.md
|
||||||
|
+++ b/doc/api/errors.md
|
||||||
|
@@ -2212,6 +2212,18 @@ malconfigured clients, if more than 8KB of HTTP header data is received then
|
||||||
|
HTTP parsing will abort without a request or response object being created, and
|
||||||
|
an `Error` with this code will be emitted.
|
||||||
|
|
||||||
|
+<a id="HPE_CHUNK_EXTENSIONS_OVERFLOW"></a>
|
||||||
|
+
|
||||||
|
+### `HPE_CHUNK_EXTENSIONS_OVERFLOW`
|
||||||
|
+
|
||||||
|
+<!-- YAML
|
||||||
|
+added: REPLACEME
|
||||||
|
+-->
|
||||||
|
+
|
||||||
|
+Too much data was received for a chunk extensions. In order to protect against
|
||||||
|
+malicious or malconfigured clients, if more than 16 KiB of data is received
|
||||||
|
+then an `Error` with this code will be emitted.
|
||||||
|
+
|
||||||
|
<a id="HPE_UNEXPECTED_CONTENT_LENGTH"></a>
|
||||||
|
### `HPE_UNEXPECTED_CONTENT_LENGTH`
|
||||||
|
|
||||||
|
diff --git a/lib/_http_server.js b/lib/_http_server.js
|
||||||
|
index 4d1f8e4..31ebae0 100644
|
||||||
|
--- a/lib/_http_server.js
|
||||||
|
+++ b/lib/_http_server.js
|
||||||
|
@@ -591,6 +591,11 @@ const requestHeaderFieldsTooLargeResponse = Buffer.from(
|
||||||
|
`HTTP/1.1 431 ${STATUS_CODES[431]}${CRLF}` +
|
||||||
|
`Connection: close${CRLF}${CRLF}`, 'ascii'
|
||||||
|
);
|
||||||
|
+const requestChunkExtensionsTooLargeResponse = Buffer.from(
|
||||||
|
+ `HTTP/1.1 413 ${STATUS_CODES[413]}\r\n` +
|
||||||
|
+ 'Connection: close\r\n\r\n', 'ascii',
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
function socketOnError(e) {
|
||||||
|
// Ignore further errors
|
||||||
|
this.removeListener('error', socketOnError);
|
||||||
|
@@ -598,8 +603,24 @@ function socketOnError(e) {
|
||||||
|
|
||||||
|
if (!this.server.emit('clientError', e, this)) {
|
||||||
|
if (this.writable && this.bytesWritten === 0) {
|
||||||
|
- const response = e.code === 'HPE_HEADER_OVERFLOW' ?
|
||||||
|
- requestHeaderFieldsTooLargeResponse : badRequestResponse;
|
||||||
|
+ let response;
|
||||||
|
+
|
||||||
|
+ switch (e.code) {
|
||||||
|
+ case 'HPE_HEADER_OVERFLOW':
|
||||||
|
+ response = requestHeaderFieldsTooLargeResponse;
|
||||||
|
+ break;
|
||||||
|
+ case 'HPE_CHUNK_EXTENSIONS_OVERFLOW':
|
||||||
|
+ response = requestChunkExtensionsTooLargeResponse;
|
||||||
|
+ break;
|
||||||
|
+/* case 'ERR_HTTP_REQUEST_TIMEOUT':
|
||||||
|
+ response = requestTimeoutResponse;
|
||||||
|
+ break;
|
||||||
|
+*/
|
||||||
|
+ default:
|
||||||
|
+ response = badRequestResponse;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
this.write(response);
|
||||||
|
}
|
||||||
|
this.destroy(e);
|
||||||
|
diff --git a/src/node_http_parser_impl.h b/src/node_http_parser_impl.h
|
||||||
|
index 77d09a9..891a108 100644
|
||||||
|
--- a/src/node_http_parser_impl.h
|
||||||
|
+++ b/src/node_http_parser_impl.h
|
||||||
|
@@ -84,6 +84,8 @@ const uint32_t kOnExecute = 4;
|
||||||
|
const uint32_t kOnTimeout = 5;
|
||||||
|
// Any more fields than this will be flushed into JS
|
||||||
|
const size_t kMaxHeaderFieldsCount = 32;
|
||||||
|
+// Maximum size of chunk extensions
|
||||||
|
+const size_t kMaxChunkExtensionsSize = 16384;
|
||||||
|
|
||||||
|
inline bool IsOWS(char c) {
|
||||||
|
return c == ' ' || c == '\t';
|
||||||
|
@@ -187,6 +189,9 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||||
|
|
||||||
|
int on_message_begin() {
|
||||||
|
num_fields_ = num_values_ = 0;
|
||||||
|
+#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
+ chunk_extensions_nread_ = 0;
|
||||||
|
+#endif
|
||||||
|
url_.Reset();
|
||||||
|
status_message_.Reset();
|
||||||
|
header_parsing_start_time_ = uv_hrtime();
|
||||||
|
@@ -432,9 +437,22 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
- // Reset nread for the next chunk
|
||||||
|
+ int on_chunk_extension(const char* at, size_t length) {
|
||||||
|
+ chunk_extensions_nread_ += length;
|
||||||
|
+
|
||||||
|
+ if (chunk_extensions_nread_ > kMaxChunkExtensionsSize) {
|
||||||
|
+ llhttp_set_error_reason(&parser_,
|
||||||
|
+ "HPE_CHUNK_EXTENSIONS_OVERFLOW:Chunk extensions overflow");
|
||||||
|
+ return HPE_USER;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Reset nread for the next chunk and also reset the extensions counter
|
||||||
|
int on_chunk_header() {
|
||||||
|
header_nread_ = 0;
|
||||||
|
+ chunk_extensions_nread_ = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -857,6 +875,7 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||||
|
llhttp_init(&parser_, type, &settings);
|
||||||
|
llhttp_set_lenient(&parser_, lenient);
|
||||||
|
header_nread_ = 0;
|
||||||
|
+ chunk_extensions_nread_ = 0;
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
http_parser_init(&parser_, type);
|
||||||
|
parser_.lenient_http_headers = lenient;
|
||||||
|
@@ -916,6 +935,7 @@ class Parser : public AsyncWrap, public StreamListener {
|
||||||
|
unsigned int execute_depth_ = 0;
|
||||||
|
bool pending_pause_ = false;
|
||||||
|
uint64_t header_nread_ = 0;
|
||||||
|
+ uint64_t chunk_extensions_nread_ = 0;
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
uint64_t headers_timeout_;
|
||||||
|
uint64_t header_parsing_start_time_ = 0;
|
||||||
|
@@ -948,6 +968,9 @@ const parser_settings_t Parser::settings = {
|
||||||
|
Proxy<DataCall, &Parser::on_header_field>::Raw,
|
||||||
|
Proxy<DataCall, &Parser::on_header_value>::Raw,
|
||||||
|
Proxy<Call, &Parser::on_headers_complete>::Raw,
|
||||||
|
+#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
+ Proxy<DataCall, &Parser::on_chunk_extension>::Raw,
|
||||||
|
+#endif
|
||||||
|
Proxy<DataCall, &Parser::on_body>::Raw,
|
||||||
|
Proxy<Call, &Parser::on_message_complete>::Raw,
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
diff --git a/test/parallel/test-http-chunk-extensions-limit.js b/test/parallel/test-http-chunk-extensions-limit.js
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..6868b3d
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/test/parallel/test-http-chunk-extensions-limit.js
|
||||||
|
@@ -0,0 +1,131 @@
|
||||||
|
+'use strict';
|
||||||
|
+
|
||||||
|
+const common = require('../common');
|
||||||
|
+const http = require('http');
|
||||||
|
+const net = require('net');
|
||||||
|
+const assert = require('assert');
|
||||||
|
+
|
||||||
|
+// Verify that chunk extensions are limited in size when sent all together.
|
||||||
|
+{
|
||||||
|
+ const server = http.createServer((req, res) => {
|
||||||
|
+ req.on('end', () => {
|
||||||
|
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
|
+ res.end('bye');
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ req.resume();
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ server.listen(0, () => {
|
||||||
|
+ const sock = net.connect(server.address().port);
|
||||||
|
+ let data = '';
|
||||||
|
+
|
||||||
|
+ sock.on('data', (chunk) => data += chunk.toString('utf-8'));
|
||||||
|
+
|
||||||
|
+ sock.on('end', common.mustCall(function() {
|
||||||
|
+ assert.strictEqual(data, 'HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n\r\n');
|
||||||
|
+ server.close();
|
||||||
|
+ }));
|
||||||
|
+
|
||||||
|
+ sock.end('' +
|
||||||
|
+ 'GET / HTTP/1.1\r\n' +
|
||||||
|
+ 'Host: localhost:8080\r\n' +
|
||||||
|
+ 'Transfer-Encoding: chunked\r\n\r\n' +
|
||||||
|
+ '2;' + 'A'.repeat(20000) + '=bar\r\nAA\r\n' +
|
||||||
|
+ '0\r\n\r\n'
|
||||||
|
+ );
|
||||||
|
+ });
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Verify that chunk extensions are limited in size when sent in intervals.
|
||||||
|
+{
|
||||||
|
+ const server = http.createServer((req, res) => {
|
||||||
|
+ req.on('end', () => {
|
||||||
|
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
|
+ res.end('bye');
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ req.resume();
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ server.listen(0, () => {
|
||||||
|
+ const sock = net.connect(server.address().port);
|
||||||
|
+ let remaining = 20000;
|
||||||
|
+ let data = '';
|
||||||
|
+
|
||||||
|
+ const interval = setInterval(
|
||||||
|
+ () => {
|
||||||
|
+ if (remaining > 0) {
|
||||||
|
+ sock.write('A'.repeat(1000));
|
||||||
|
+ } else {
|
||||||
|
+ sock.write('=bar\r\nAA\r\n0\r\n\r\n');
|
||||||
|
+ clearInterval(interval);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ remaining -= 1000;
|
||||||
|
+ },
|
||||||
|
+ common.platformTimeout(20),
|
||||||
|
+ ).unref();
|
||||||
|
+
|
||||||
|
+ sock.on('data', (chunk) => data += chunk.toString('utf-8'));
|
||||||
|
+
|
||||||
|
+ sock.on('end', common.mustCall(function() {
|
||||||
|
+ assert.strictEqual(data, 'HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n\r\n');
|
||||||
|
+ server.close();
|
||||||
|
+ }));
|
||||||
|
+
|
||||||
|
+ sock.write('' +
|
||||||
|
+ 'GET / HTTP/1.1\r\n' +
|
||||||
|
+ 'Host: localhost:8080\r\n' +
|
||||||
|
+ 'Transfer-Encoding: chunked\r\n\r\n' +
|
||||||
|
+ '2;'
|
||||||
|
+ );
|
||||||
|
+ });
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Verify the chunk extensions is correctly reset after a chunk
|
||||||
|
+{
|
||||||
|
+ const server = http.createServer((req, res) => {
|
||||||
|
+ req.on('end', () => {
|
||||||
|
+ res.writeHead(200, { 'content-type': 'text/plain', 'connection': 'close', 'date': 'now' });
|
||||||
|
+ res.end('bye');
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ req.resume();
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ server.listen(0, () => {
|
||||||
|
+ const sock = net.connect(server.address().port);
|
||||||
|
+ let data = '';
|
||||||
|
+
|
||||||
|
+ sock.on('data', (chunk) => data += chunk.toString('utf-8'));
|
||||||
|
+
|
||||||
|
+ sock.on('end', common.mustCall(function() {
|
||||||
|
+ assert.strictEqual(
|
||||||
|
+ data,
|
||||||
|
+ 'HTTP/1.1 200 OK\r\n' +
|
||||||
|
+ 'content-type: text/plain\r\n' +
|
||||||
|
+ 'connection: close\r\n' +
|
||||||
|
+ 'date: now\r\n' +
|
||||||
|
+ 'Transfer-Encoding: chunked\r\n' +
|
||||||
|
+ '\r\n' +
|
||||||
|
+ '3\r\n' +
|
||||||
|
+ 'bye\r\n' +
|
||||||
|
+ '0\r\n' +
|
||||||
|
+ '\r\n',
|
||||||
|
+ );
|
||||||
|
+
|
||||||
|
+ server.close();
|
||||||
|
+ }));
|
||||||
|
+
|
||||||
|
+ sock.end('' +
|
||||||
|
+ 'GET / HTTP/1.1\r\n' +
|
||||||
|
+ 'Host: localhost:8080\r\n' +
|
||||||
|
+ 'Transfer-Encoding: chunked\r\n\r\n' +
|
||||||
|
+ '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' +
|
||||||
|
+ '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' +
|
||||||
|
+ '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' +
|
||||||
|
+ '0\r\n\r\n'
|
||||||
|
+ );
|
||||||
|
+ });
|
||||||
|
+}
|
||||||
154
CVE-2024-22025.patch
Normal file
154
CVE-2024-22025.patch
Normal file
File diff suppressed because one or more lines are too long
3031
CVE-2024-27982.patch
Normal file
3031
CVE-2024-27982.patch
Normal file
File diff suppressed because it is too large
Load Diff
34
CVE-2024-27983.patch
Normal file
34
CVE-2024-27983.patch
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
From: RafaelGSS <rafael.nunu@hotmail.com>
|
||||||
|
Date: Tue, 26 Mar 2024 15:55:13 -0300
|
||||||
|
Subject: CVE-2024-27983 ensure to close stream when destroying session
|
||||||
|
|
||||||
|
Co-Authored-By: Anna Henningsen <anna@addaleax.net>
|
||||||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/561
|
||||||
|
bug-hakerone: https://hackerone.com/reports/2319584
|
||||||
|
Reviewed-By: Michael Dawson <midawson@redhat.com>
|
||||||
|
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
|
||||||
|
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
||||||
|
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
|
||||||
|
CVE-ID: CVE-2024-27983
|
||||||
|
origin: backport, https://github.com/nodejs/node/commit/0fb816dbccde955cd24acc1b16497a91fab507c8.patch
|
||||||
|
---
|
||||||
|
src/node_http2.cc | 6 ++++++
|
||||||
|
1 file changed, 6 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/src/node_http2.cc b/src/node_http2.cc
|
||||||
|
index 5156aa3..c441921 100644
|
||||||
|
--- a/src/node_http2.cc
|
||||||
|
+++ b/src/node_http2.cc
|
||||||
|
@@ -590,6 +590,12 @@ Http2Session::Http2Session(Environment* env,
|
||||||
|
Http2Session::~Http2Session() {
|
||||||
|
CHECK_EQ(flags_ & SESSION_STATE_HAS_SCOPE, 0);
|
||||||
|
Debug(this, "freeing nghttp2 session");
|
||||||
|
+ // Ensure that all `Http2Stream` instances and the memory they hold
|
||||||
|
+ // on to are destroyed before the nghttp2 session is.
|
||||||
|
+ for (const auto& [id, stream] : streams_) {
|
||||||
|
+ stream->Detach();
|
||||||
|
+ }
|
||||||
|
+ streams_.clear();
|
||||||
|
nghttp2_session_del(session_);
|
||||||
|
CHECK_EQ(current_nghttp2_memory_, 0);
|
||||||
|
}
|
||||||
54
nodejs.spec
54
nodejs.spec
@ -1,5 +1,5 @@
|
|||||||
%bcond_with bootstrap
|
%bcond_with bootstrap
|
||||||
%global baserelease 3
|
%global baserelease 8
|
||||||
%{?!_pkgdocdir:%global _pkgdocdir %{_docdir}/%{name}-%{version}}
|
%{?!_pkgdocdir:%global _pkgdocdir %{_docdir}/%{name}-%{version}}
|
||||||
%global nodejs_epoch 1
|
%global nodejs_epoch 1
|
||||||
%global nodejs_major 12
|
%global nodejs_major 12
|
||||||
@ -83,13 +83,33 @@ Patch0001: 0001-Disable-running-gyp-on-shared-deps.patch
|
|||||||
Patch0002: 0002-Install-both-binaries-and-use-libdir.patch
|
Patch0002: 0002-Install-both-binaries-and-use-libdir.patch
|
||||||
Patch0003: 0004-Make-AARCH64-compile-on-64KB-physical-pages.patch
|
Patch0003: 0004-Make-AARCH64-compile-on-64KB-physical-pages.patch
|
||||||
Patch00010: 0005-use-getauxval-in-node_main_cc.patch
|
Patch00010: 0005-use-getauxval-in-node_main_cc.patch
|
||||||
Patch00011: CVE-2022-43548-pre-1.patch
|
Patch00011: CVE-2022-32212.patch
|
||||||
Patch00012: CVE-2022-43548-pre-2.patch
|
Patch00012: CVE-2022-32213-CVE-2022-32214-CVE-2022-32215.patch
|
||||||
Patch00013: CVE-2022-43548.patch
|
Patch00013: CVE-2022-35256.patch
|
||||||
Patch00014: CVE-2023-0286.patch
|
Patch00014: CVE-2022-43548.patch
|
||||||
Patch00015: CVE-2023-0215.patch
|
Patch00015: CVE-2023-0286.patch
|
||||||
Patch00016: CVE-2022-4304.patch
|
Patch00016: CVE-2023-0215.patch
|
||||||
Patch00017: CVE-2022-4450.patch
|
Patch00017: CVE-2022-4304.patch
|
||||||
|
Patch00018: CVE-2022-4450.patch
|
||||||
|
Patch00019: CVE-2023-23918.patch
|
||||||
|
Patch00020: CVE-2023-30581.patch
|
||||||
|
Patch00021: CVE-2023-32002-CVE-2023-32006.patch
|
||||||
|
Patch00022: CVE-2023-30589.patch
|
||||||
|
Patch00023: CVE-2023-30590.patch
|
||||||
|
Patch00024: CVE-2022-25881.patch
|
||||||
|
Patch00025: CVE-2023-23920.patch
|
||||||
|
Patch00026: CVE-2023-32559.patch
|
||||||
|
# https://github.com/openssl/openssl/commit/879f7080d7e141f415c79eaa3a8ac4a3dad0348b
|
||||||
|
Patch00027: CVE-2023-0464.patch
|
||||||
|
# https://github.com/openssl/openssl/commit/b013765abfa80036dc779dd0e50602c57bb3bf95
|
||||||
|
Patch00028: CVE-2023-0465.patch
|
||||||
|
# https://github.com/nghttp2/nghttp2/commit/72b4af6143681f528f1d237b21a9a7aee1738832
|
||||||
|
Patch00029: CVE-2023-44487.patch
|
||||||
|
Patch00030: CVE-2023-46809.patch
|
||||||
|
Patch00031: CVE-2024-22019.patch
|
||||||
|
Patch00032: CVE-2024-22025.patch
|
||||||
|
Patch00033: CVE-2024-27982.patch
|
||||||
|
Patch00034: CVE-2024-27983.patch
|
||||||
|
|
||||||
BuildRequires: python3-devel
|
BuildRequires: python3-devel
|
||||||
BuildRequires: zlib-devel
|
BuildRequires: zlib-devel
|
||||||
@ -492,6 +512,24 @@ end
|
|||||||
%{_pkgdocdir}/npm/docs
|
%{_pkgdocdir}/npm/docs
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Sep 19 2024 yaoxin <yao_xin001@hoperun.com>- 1:12.22.11-8
|
||||||
|
- Fix CVE-2023-46809,CVE-2024-22019,CVE-2024-22025,CVE-2024-27982 and CVE-2024-27983
|
||||||
|
|
||||||
|
* Tue Feb 06 2024 yaoxin <yao_xin001@hoperun.com> - 1:12.22.11-7
|
||||||
|
- Fix CVE-2023-44487
|
||||||
|
|
||||||
|
* Mon Feb 05 2024 yaoxin <yao_xin001@hoperun.com> - 1:12.22.11-6
|
||||||
|
- Fix CVE-2023-0464 and CVE-2023-0465
|
||||||
|
|
||||||
|
* Thu Oct 26 2023 wangkai <13474090681@163.com> - 1:12.22.11-5
|
||||||
|
- Update CVE-2023-23918.patch for fix nodejs-raw-body,nodejs-istanbul build error
|
||||||
|
|
||||||
|
* Tue Aug 29 2023 wangkai <13474090681@163.com> - 1:12.22.11-4
|
||||||
|
- Fix CVE-2022-32212,CVE-2022-32213,CVE-2022-32214,CVE-2022-32215,
|
||||||
|
- CVE-2022-25881,CVE-2023-23918,CVE-2023-23920,CVE-2023-30589,
|
||||||
|
- CVE-2023-30590,CVE-2023-30581,CVE-2023-32002,CVE-2023-32006,
|
||||||
|
- CVE-2023-32559,CVE-2022-35256
|
||||||
|
|
||||||
* Wed Feb 22 2023 yaoxin <yaoxin30@h-partners.com> - 1:12.22.11-3
|
* Wed Feb 22 2023 yaoxin <yaoxin30@h-partners.com> - 1:12.22.11-3
|
||||||
- Fix CVE-2023-0286,CVE-2023-0215,CVE-2022-4304 and CVE-2022-4450
|
- Fix CVE-2023-0286,CVE-2023-0215,CVE-2022-4304 and CVE-2022-4450
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user