diff --git a/README.md b/README.md
index 09c90397618220..cf3f3dd98cfedd 100644
--- a/README.md
+++ b/README.md
@@ -114,11 +114,11 @@ documentation of the latest stable version.
### Verifying Binaries
-Current, LTS and Nightly download directories all contain a _SHASUM256.txt_
+Current, LTS and Nightly download directories all contain a _SHASUMS256.txt_
file that lists the SHA checksums for each file available for
download.
-The _SHASUM256.txt_ can be downloaded using curl.
+The _SHASUMS256.txt_ can be downloaded using curl.
```console
$ curl -O https://nodejs.org/dist/vx.y.z/SHASUMS256.txt
@@ -135,10 +135,10 @@ _(Where "node-vx.y.z.tar.gz" is the name of the file you have
downloaded)_
Additionally, Current and LTS releases (not Nightlies) have GPG signed
-copies of SHASUM256.txt files available as SHASUM256.txt.asc. You can use
+copies of SHASUMS256.txt files available as SHASUMS256.txt.asc. You can use
`gpg` to verify that the file has not been tampered with.
-To verify a SHASUM256.txt.asc, you will first need to import all of
+To verify a SHASUMS256.txt.asc, you will first need to import all of
the GPG keys of individuals authorized to create releases. They are
listed at the bottom of this README under [Release Team](#release-team).
Use a command such as this to import the keys:
diff --git a/deps/openssl/config/opensslconf.h b/deps/openssl/config/opensslconf.h
index 9b20fb6485aa84..1c89babcf6c864 100644
--- a/deps/openssl/config/opensslconf.h
+++ b/deps/openssl/config/opensslconf.h
@@ -37,6 +37,8 @@
| solaris | x64 | solaris64-x86_64-gcc | o |
| freebsd | ia32 | BSD-x86 | o |
| freebsd | x64 | BSD-x86_64 | o |
+ | netbsd | ia32 | BSD-x86 | o |
+ | netbsd | x64 | BSD-x86_64 | o |
| openbsd | ia32 | BSD-x86 | - |
| openbsd | x64 | BSD-x86_64 | - |
| others | others | linux-elf | - |
@@ -51,6 +53,7 @@
| mac | __APPLE__ && __MACH__ |
| solaris | __sun |
| freebsd | __FreeBSD__ |
+ | netbsd | __NetBSD__ |
| openbsd | __OpenBSD__ |
| linux (not andorid)| __linux__ && !__ANDROID__ |
| android | __ANDROID__ |
@@ -94,6 +97,11 @@
# define OPENSSL_LINUX 1
#endif
+#undef OPENSSL_BSD
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# define OPENSSL_BSD 1
+#endif
+
#if defined(OPENSSL_LINUX) && defined(__i386__)
# include "./archs/linux-elf/opensslconf.h"
#elif defined(OPENSSL_LINUX) && defined(__ILP32__)
@@ -112,9 +120,9 @@
# include "./archs/VC-WIN32/opensslconf.h"
#elif defined(_WIN32) && defined(_M_X64)
# include "./archs/VC-WIN64A/opensslconf.h"
-#elif (defined(__FreeBSD__) || defined(__OpenBSD__)) && defined(__i386__)
+#elif defined(OPENSSL_BSD) && defined(__i386__)
# include "./archs/BSD-x86/opensslconf.h"
-#elif (defined(__FreeBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)
+#elif defined(OPENSSL_BSD) && defined(__x86_64__)
# include "./archs/BSD-x86_64/opensslconf.h"
#elif defined(__sun) && defined(__i386__)
# include "./archs/solaris-x86-gcc/opensslconf.h"
diff --git a/doc/api/crypto.md b/doc/api/crypto.md
index 79c7a3c733e90a..e0f3689bc21025 100644
--- a/doc/api/crypto.md
+++ b/doc/api/crypto.md
@@ -896,6 +896,10 @@ console.log(sign.sign(privateKey).toString('hex'));
### sign.sign(private_key[, output_format])
Calculates the signature on all the data passed through using either
@@ -903,10 +907,21 @@ Calculates the signature on all the data passed through using either
The `private_key` argument can be an object or a string. If `private_key` is a
string, it is treated as a raw key with no passphrase. If `private_key` is an
-object, it is interpreted as a hash containing two properties:
+object, it must contain one or more of the following properties:
-* `key`: {string} - PEM encoded private key
+* `key`: {string} - PEM encoded private key (required)
* `passphrase`: {string} - passphrase for the private key
+* `padding`: {integer} - Optional padding value for RSA, one of the following:
+ * `crypto.constants.RSA_PKCS1_PADDING` (default)
+ * `crypto.constants.RSA_PKCS1_PSS_PADDING`
+
+ Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function
+ used to sign the message as specified in section 3.1 of [RFC 4055][].
+* `saltLength`: {integer} - salt length for when padding is
+ `RSA_PKCS1_PSS_PADDING`. The special value
+ `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
+ size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the
+ maximum permissible value.
The `output_format` can specify one of `'latin1'`, `'hex'` or `'base64'`. If
`output_format` is provided a string is returned; otherwise a [`Buffer`][] is
@@ -989,11 +1004,33 @@ This can be called many times with new data as it is streamed.
### verifier.verify(object, signature[, signature_format])
+- `object` {string | Object}
+- `signature` {string | Buffer | Uint8Array}
+- `signature_format` {string}
Verifies the provided data using the given `object` and `signature`.
-The `object` argument is a string containing a PEM encoded object, which can be
-one an RSA public key, a DSA public key, or an X.509 certificate.
+The `object` argument can be either a string containing a PEM encoded object,
+which can be an RSA public key, a DSA public key, or an X.509 certificate,
+or an object with one or more of the following properties:
+
+* `key`: {string} - PEM encoded public key (required)
+* `padding`: {integer} - Optional padding value for RSA, one of the following:
+ * `crypto.constants.RSA_PKCS1_PADDING` (default)
+ * `crypto.constants.RSA_PKCS1_PSS_PADDING`
+
+ Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function
+ used to verify the message as specified in section 3.1 of [RFC 4055][].
+* `saltLength`: {integer} - salt length for when padding is
+ `RSA_PKCS1_PSS_PADDING`. The special value
+ `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
+ size, `crypto.constants.RSA_PSS_SALTLEN_AUTO` (default) causes it to be
+ determined automatically.
+
The `signature` argument is the previously calculated signature for the data, in
the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`.
If a `signature_format` is specified, the `signature` is expected to be a
@@ -1900,6 +1937,21 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL.
RSA_PKCS1_PSS_PADDING
+
+ RSA_PSS_SALTLEN_DIGEST
+ Sets the salt length for `RSA_PKCS1_PSS_PADDING` to the digest size
+ when signing or verifying.
+
+
+ RSA_PSS_SALTLEN_MAX_SIGN
+ Sets the salt length for `RSA_PKCS1_PSS_PADDING` to the maximum
+ permissible value when signing data.
+
+
+ RSA_PSS_SALTLEN_AUTO
+ Causes the salt length for `RSA_PKCS1_PSS_PADDING` to be determined
+ automatically when verifying a signature.
+
POINT_CONVERSION_COMPRESSED
@@ -1975,6 +2027,7 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL.
[publicly trusted list of CAs]: https://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt
[RFC 2412]: https://www.rfc-editor.org/rfc/rfc2412.txt
[RFC 3526]: https://www.rfc-editor.org/rfc/rfc3526.txt
+[RFC 4055]: https://www.rfc-editor.org/rfc/rfc4055.txt
[stream]: stream.html
[stream-writable-write]: stream.html#stream_writable_write_chunk_encoding_callback
[Crypto Constants]: #crypto_crypto_constants_1
diff --git a/lib/crypto.js b/lib/crypto.js
index f2dede115487b0..f4eeb7baa3aad5 100644
--- a/lib/crypto.js
+++ b/lib/crypto.js
@@ -283,7 +283,28 @@ Sign.prototype.sign = function sign(options, encoding) {
var key = options.key || options;
var passphrase = options.passphrase || null;
- var ret = this._handle.sign(toBuf(key), null, passphrase);
+
+ // Options specific to RSA
+ var rsaPadding = constants.RSA_PKCS1_PADDING;
+ if (options.hasOwnProperty('padding')) {
+ if (options.padding === options.padding >> 0) {
+ rsaPadding = options.padding;
+ } else {
+ throw new TypeError('padding must be an integer');
+ }
+ }
+
+ var pssSaltLength = constants.RSA_PSS_SALTLEN_AUTO;
+ if (options.hasOwnProperty('saltLength')) {
+ if (options.saltLength === options.saltLength >> 0) {
+ pssSaltLength = options.saltLength;
+ } else {
+ throw new TypeError('saltLength must be an integer');
+ }
+ }
+
+ var ret = this._handle.sign(toBuf(key), null, passphrase, rsaPadding,
+ pssSaltLength);
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
@@ -309,9 +330,31 @@ util.inherits(Verify, stream.Writable);
Verify.prototype._write = Sign.prototype._write;
Verify.prototype.update = Sign.prototype.update;
-Verify.prototype.verify = function verify(object, signature, sigEncoding) {
+Verify.prototype.verify = function verify(options, signature, sigEncoding) {
+ var key = options.key || options;
sigEncoding = sigEncoding || exports.DEFAULT_ENCODING;
- return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding));
+
+ // Options specific to RSA
+ var rsaPadding = constants.RSA_PKCS1_PADDING;
+ if (options.hasOwnProperty('padding')) {
+ if (options.padding === options.padding >> 0) {
+ rsaPadding = options.padding;
+ } else {
+ throw new TypeError('padding must be an integer');
+ }
+ }
+
+ var pssSaltLength = constants.RSA_PSS_SALTLEN_AUTO;
+ if (options.hasOwnProperty('saltLength')) {
+ if (options.saltLength === options.saltLength >> 0) {
+ pssSaltLength = options.saltLength;
+ } else {
+ throw new TypeError('saltLength must be an integer');
+ }
+ }
+
+ return this._handle.verify(toBuf(key), toBuf(signature, sigEncoding), null,
+ rsaPadding, pssSaltLength);
};
function rsaPublic(method, defaultPadding) {
diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js
index de215dc2dce62b..310dc9dd029375 100644
--- a/lib/internal/bootstrap_node.js
+++ b/lib/internal/bootstrap_node.js
@@ -317,20 +317,14 @@
}
function tryGetCwd(path) {
- var threw = true;
- var cwd;
try {
- cwd = process.cwd();
- threw = false;
- } finally {
- if (threw) {
- // getcwd(3) can fail if the current working directory has been deleted.
- // Fall back to the directory name of the (absolute) executable path.
- // It's not really correct but what are the alternatives?
- return path.dirname(process.execPath);
- }
+ return process.cwd();
+ } catch (ex) {
+ // getcwd(3) can fail if the current working directory has been deleted.
+ // Fall back to the directory name of the (absolute) executable path.
+ // It's not really correct but what are the alternatives?
+ return path.dirname(process.execPath);
}
- return cwd;
}
function evalScript(name) {
diff --git a/lib/path.js b/lib/path.js
index dc8a33f4996e5f..2d09eb595a2e2a 100644
--- a/lib/path.js
+++ b/lib/path.js
@@ -14,6 +14,7 @@ function normalizeStringWin32(path, allowAboveRoot) {
var lastSlash = -1;
var dots = 0;
var code;
+ var isAboveRoot = false;
for (var i = 0; i <= path.length; ++i) {
if (i < path.length)
code = path.charCodeAt(i);
@@ -25,7 +26,7 @@ function normalizeStringWin32(path, allowAboveRoot) {
if (lastSlash === i - 1 || dots === 1) {
// NOOP
} else if (lastSlash !== i - 1 && dots === 2) {
- if (res.length < 2 ||
+ if (res.length < 2 || !isAboveRoot ||
res.charCodeAt(res.length - 1) !== 46/*.*/ ||
res.charCodeAt(res.length - 2) !== 46/*.*/) {
if (res.length > 2) {
@@ -42,12 +43,14 @@ function normalizeStringWin32(path, allowAboveRoot) {
res = res.slice(0, j);
lastSlash = i;
dots = 0;
+ isAboveRoot = false;
continue;
}
} else if (res.length === 2 || res.length === 1) {
res = '';
lastSlash = i;
dots = 0;
+ isAboveRoot = false;
continue;
}
}
@@ -56,12 +59,14 @@ function normalizeStringWin32(path, allowAboveRoot) {
res += '\\..';
else
res = '..';
+ isAboveRoot = true;
}
} else {
if (res.length > 0)
res += '\\' + path.slice(lastSlash + 1, i);
else
res = path.slice(lastSlash + 1, i);
+ isAboveRoot = false;
}
lastSlash = i;
dots = 0;
@@ -80,6 +85,7 @@ function normalizeStringPosix(path, allowAboveRoot) {
var lastSlash = -1;
var dots = 0;
var code;
+ var isAboveRoot = false;
for (var i = 0; i <= path.length; ++i) {
if (i < path.length)
code = path.charCodeAt(i);
@@ -91,7 +97,7 @@ function normalizeStringPosix(path, allowAboveRoot) {
if (lastSlash === i - 1 || dots === 1) {
// NOOP
} else if (lastSlash !== i - 1 && dots === 2) {
- if (res.length < 2 ||
+ if (res.length < 2 || !isAboveRoot ||
res.charCodeAt(res.length - 1) !== 46/*.*/ ||
res.charCodeAt(res.length - 2) !== 46/*.*/) {
if (res.length > 2) {
@@ -108,12 +114,14 @@ function normalizeStringPosix(path, allowAboveRoot) {
res = res.slice(0, j);
lastSlash = i;
dots = 0;
+ isAboveRoot = false;
continue;
}
} else if (res.length === 2 || res.length === 1) {
res = '';
lastSlash = i;
dots = 0;
+ isAboveRoot = false;
continue;
}
}
@@ -122,12 +130,14 @@ function normalizeStringPosix(path, allowAboveRoot) {
res += '/..';
else
res = '..';
+ isAboveRoot = true;
}
} else {
if (res.length > 0)
res += '/' + path.slice(lastSlash + 1, i);
else
res = path.slice(lastSlash + 1, i);
+ isAboveRoot = false;
}
lastSlash = i;
dots = 0;
diff --git a/src/node_constants.cc b/src/node_constants.cc
index 750df9c669bad3..be6e034a923a92 100644
--- a/src/node_constants.cc
+++ b/src/node_constants.cc
@@ -974,6 +974,18 @@ void DefineOpenSSLConstants(Local target) {
NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING);
#endif
+#ifdef RSA_PSS_SALTLEN_DIGEST
+ NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_DIGEST);
+#endif
+
+#ifdef RSA_PSS_SALTLEN_MAX_SIGN
+ NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_MAX_SIGN);
+#endif
+
+#ifdef RSA_PSS_SALTLEN_AUTO
+ NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_AUTO);
+#endif
+
#if HAVE_OPENSSL
// NOTE: These are not defines
NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_COMPRESSED);
diff --git a/src/node_constants.h b/src/node_constants.h
index 7ba6ec3bd1b015..db365f3d87a7fc 100644
--- a/src/node_constants.h
+++ b/src/node_constants.h
@@ -7,6 +7,19 @@
#include "v8.h"
#if HAVE_OPENSSL
+
+#ifndef RSA_PSS_SALTLEN_DIGEST
+#define RSA_PSS_SALTLEN_DIGEST -1
+#endif
+
+#ifndef RSA_PSS_SALTLEN_MAX_SIGN
+#define RSA_PSS_SALTLEN_MAX_SIGN -2
+#endif
+
+#ifndef RSA_PSS_SALTLEN_AUTO
+#define RSA_PSS_SALTLEN_AUTO -2
+#endif
+
#define DEFAULT_CIPHER_LIST_CORE "ECDHE-RSA-AES128-GCM-SHA256:" \
"ECDHE-ECDSA-AES128-GCM-SHA256:" \
"ECDHE-RSA-AES256-GCM-SHA384:" \
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index aa2dafebc5cf81..335972bab2a466 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -1,5 +1,6 @@
#include "node.h"
#include "node_buffer.h"
+#include "node_constants.h"
#include "node_crypto.h"
#include "node_crypto_bio.h"
#include "node_crypto_groups.h"
@@ -80,6 +81,7 @@ using v8::HandleScope;
using v8::Integer;
using v8::Isolate;
using v8::Local;
+using v8::Maybe;
using v8::Null;
using v8::Object;
using v8::Persistent;
@@ -3990,6 +3992,19 @@ void SignBase::CheckThrow(SignBase::Error error) {
}
}
+static bool ApplyRSAOptions(EVP_PKEY* pkey, EVP_PKEY_CTX* pkctx, int padding,
+ int salt_len) {
+ if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) {
+ if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0)
+ return false;
+ if (padding == RSA_PKCS1_PSS_PADDING) {
+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len) <= 0)
+ return false;
+ }
+ }
+
+ return true;
+}
@@ -4019,7 +4034,7 @@ SignBase::Error Sign::SignInit(const char* sign_type) {
return kSignUnknownDigest;
EVP_MD_CTX_init(&mdctx_);
- if (!EVP_SignInit_ex(&mdctx_, md, nullptr))
+ if (!EVP_DigestInit_ex(&mdctx_, md, nullptr))
return kSignInit;
initialised_ = true;
@@ -4046,7 +4061,7 @@ void Sign::SignInit(const FunctionCallbackInfo& args) {
SignBase::Error Sign::SignUpdate(const char* data, int len) {
if (!initialised_)
return kSignNotInitialised;
- if (!EVP_SignUpdate(&mdctx_, data, len))
+ if (!EVP_DigestUpdate(&mdctx_, data, len))
return kSignUpdate;
return kSignOk;
}
@@ -4076,12 +4091,54 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) {
sign->CheckThrow(err);
}
+static int Node_SignFinal(EVP_MD_CTX* mdctx, unsigned char* md,
+ unsigned int* sig_len, EVP_PKEY* pkey, int padding,
+ int pss_salt_len) {
+ unsigned char m[EVP_MAX_MD_SIZE];
+ unsigned int m_len;
+ int rv = 0;
+ EVP_PKEY_CTX* pkctx = nullptr;
+
+ *sig_len = 0;
+ if (!EVP_DigestFinal_ex(mdctx, m, &m_len))
+ return rv;
+
+ if (mdctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) {
+ size_t sltmp = static_cast(EVP_PKEY_size(pkey));
+ pkctx = EVP_PKEY_CTX_new(pkey, nullptr);
+ if (pkctx == nullptr)
+ goto err;
+ if (EVP_PKEY_sign_init(pkctx) <= 0)
+ goto err;
+ if (!ApplyRSAOptions(pkey, pkctx, padding, pss_salt_len))
+ goto err;
+ if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx->digest) <= 0)
+ goto err;
+ if (EVP_PKEY_sign(pkctx, md, &sltmp, m, m_len) <= 0)
+ goto err;
+ *sig_len = sltmp;
+ rv = 1;
+ err:
+ EVP_PKEY_CTX_free(pkctx);
+ return rv;
+ }
+
+ if (mdctx->digest->sign == nullptr) {
+ EVPerr(EVP_F_EVP_SIGNFINAL, EVP_R_NO_SIGN_FUNCTION_CONFIGURED);
+ return 0;
+ }
+
+ return mdctx->digest->sign(mdctx->digest->type, m, m_len, md, sig_len,
+ pkey->pkey.ptr);
+}
SignBase::Error Sign::SignFinal(const char* key_pem,
int key_pem_len,
const char* passphrase,
unsigned char** sig,
- unsigned int *sig_len) {
+ unsigned int* sig_len,
+ int padding,
+ int salt_len) {
if (!initialised_)
return kSignNotInitialised;
@@ -4127,7 +4184,7 @@ SignBase::Error Sign::SignFinal(const char* key_pem,
}
#endif // NODE_FIPS_MODE
- if (EVP_SignFinal(&mdctx_, *sig, sig_len, pkey))
+ if (Node_SignFinal(&mdctx_, *sig, sig_len, pkey, padding, salt_len))
fatal = false;
initialised_ = false;
@@ -4168,6 +4225,16 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) {
size_t buf_len = Buffer::Length(args[0]);
char* buf = Buffer::Data(args[0]);
+ CHECK(args[3]->IsInt32());
+ Maybe maybe_padding = args[3]->Int32Value(env->context());
+ CHECK(maybe_padding.IsJust());
+ int padding = maybe_padding.FromJust();
+
+ CHECK(args[4]->IsInt32());
+ Maybe maybe_salt_len = args[4]->Int32Value(env->context());
+ CHECK(maybe_salt_len.IsJust());
+ int salt_len = maybe_salt_len.FromJust();
+
md_len = 8192; // Maximum key size is 8192 bits
md_value = new unsigned char[md_len];
@@ -4179,7 +4246,9 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) {
buf_len,
len >= 3 && !args[2]->IsNull() ? *passphrase : nullptr,
&md_value,
- &md_len);
+ &md_len,
+ padding,
+ salt_len);
if (err != kSignOk) {
delete[] md_value;
md_value = nullptr;
@@ -4223,7 +4292,7 @@ SignBase::Error Verify::VerifyInit(const char* verify_type) {
return kSignUnknownDigest;
EVP_MD_CTX_init(&mdctx_);
- if (!EVP_VerifyInit_ex(&mdctx_, md, nullptr))
+ if (!EVP_DigestInit_ex(&mdctx_, md, nullptr))
return kSignInit;
initialised_ = true;
@@ -4251,7 +4320,7 @@ SignBase::Error Verify::VerifyUpdate(const char* data, int len) {
if (!initialised_)
return kSignNotInitialised;
- if (!EVP_VerifyUpdate(&mdctx_, data, len))
+ if (!EVP_DigestUpdate(&mdctx_, data, len))
return kSignUpdate;
return kSignOk;
@@ -4287,6 +4356,8 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem,
int key_pem_len,
const char* sig,
int siglen,
+ int padding,
+ int saltlen,
bool* verify_result) {
if (!initialised_)
return kSignNotInitialised;
@@ -4298,7 +4369,10 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem,
BIO* bp = nullptr;
X509* x509 = nullptr;
bool fatal = true;
+ unsigned char m[EVP_MAX_MD_SIZE];
+ unsigned int m_len;
int r = 0;
+ EVP_PKEY_CTX* pkctx = nullptr;
bp = BIO_new_mem_buf(const_cast(key_pem), key_pem_len);
if (bp == nullptr)
@@ -4333,11 +4407,29 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem,
goto exit;
}
+ if (!EVP_DigestFinal_ex(&mdctx_, m, &m_len)) {
+ goto exit;
+ }
+
fatal = false;
- r = EVP_VerifyFinal(&mdctx_,
+
+ pkctx = EVP_PKEY_CTX_new(pkey, nullptr);
+ if (pkctx == nullptr)
+ goto err;
+ if (EVP_PKEY_verify_init(pkctx) <= 0)
+ goto err;
+ if (!ApplyRSAOptions(pkey, pkctx, padding, saltlen))
+ goto err;
+ if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx_.digest) <= 0)
+ goto err;
+ r = EVP_PKEY_verify(pkctx,
reinterpret_cast(sig),
siglen,
- pkey);
+ m,
+ m_len);
+
+ err:
+ EVP_PKEY_CTX_free(pkctx);
exit:
if (pkey != nullptr)
@@ -4391,8 +4483,19 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) {
hbuf = Buffer::Data(args[1]);
}
+ CHECK(args[3]->IsInt32());
+ Maybe maybe_padding = args[3]->Int32Value(env->context());
+ CHECK(maybe_padding.IsJust());
+ int padding = maybe_padding.FromJust();
+
+ CHECK(args[4]->IsInt32());
+ Maybe maybe_salt_len = args[4]->Int32Value(env->context());
+ CHECK(maybe_salt_len.IsJust());
+ int salt_len = maybe_salt_len.FromJust();
+
bool verify_result;
- Error err = verify->VerifyFinal(kbuf, klen, hbuf, hlen, &verify_result);
+ Error err = verify->VerifyFinal(kbuf, klen, hbuf, hlen, padding, salt_len,
+ &verify_result);
if (args[1]->IsString())
delete[] hbuf;
if (err != kSignOk)
diff --git a/src/node_crypto.h b/src/node_crypto.h
index 746c954b26fb43..373d859530bcf5 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -569,7 +569,9 @@ class Sign : public SignBase {
int key_pem_len,
const char* passphrase,
unsigned char** sig,
- unsigned int *sig_len);
+ unsigned int *sig_len,
+ int padding,
+ int saltlen);
protected:
static void New(const v8::FunctionCallbackInfo& args);
@@ -592,6 +594,8 @@ class Verify : public SignBase {
int key_pem_len,
const char* sig,
int siglen,
+ int padding,
+ int saltlen,
bool* verify_result);
protected:
diff --git a/test/fixtures/pss-vectors.json b/test/fixtures/pss-vectors.json
new file mode 100644
index 00000000000000..b540d13a540646
--- /dev/null
+++ b/test/fixtures/pss-vectors.json
@@ -0,0 +1,89 @@
+{
+ "example01": {
+ "publicKey": [
+ "-----BEGIN PUBLIC KEY-----",
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLs",
+ "DjatUqRN/rHmH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2V",
+ "CAltWyuLbfXWce9jd8CSHLI8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh",
+ "4fINDOjP+yJJvZohNwIDAQAB",
+ "-----END PUBLIC KEY-----"
+ ],
+ "tests": [
+ {
+ "message": "cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0",
+ "salt": "dee959c7e06411361420ff80185ed57f3e6776af",
+ "signature": "9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c"
+ },
+ {
+ "message": "851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e",
+ "salt": "ef2869fa40c346cb183dab3d7bffc98fd56df42d",
+ "signature": "3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c729984f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193be5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98c22c4d4d47724fdb5669e843"
+ },
+ {
+ "message": "a4b159941761c40c6a82f2b80d1b94f5aa2654fd17e12d588864679b54cd04ef8bd03012be8dc37f4b83af7963faff0dfa225477437c48017ff2be8191cf3955fc07356eab3f322f7f620e21d254e5db4324279fe067e0910e2e81ca2cab31c745e67a54058eb50d993cdb9ed0b4d029c06d21a94ca661c3ce27fae1d6cb20f4564d66ce4767583d0e5f060215b59017be85ea848939127bd8c9c4d47b51056c031cf336f17c9980f3b8f5b9b6878e8b797aa43b882684333e17893fe9caa6aa299f7ed1a18ee2c54864b7b2b99b72618fb02574d139ef50f019c9eef416971338e7d470",
+ "salt": "710b9c4747d800d4de87f12afdce6df18107cc77",
+ "signature": "666026fba71bd3e7cf13157cc2c51a8e4aa684af9778f91849f34335d141c00154c4197621f9624a675b5abc22ee7d5baaffaae1c9baca2cc373b3f33e78e6143c395a91aa7faca664eb733afd14d8827259d99a7550faca501ef2b04e33c23aa51f4b9e8282efdb728cc0ab09405a91607c6369961bc8270d2d4f39fce612b1"
+ },
+ {
+ "message": "bc656747fa9eafb3f0",
+ "salt": "056f00985de14d8ef5cea9e82f8c27bef720335e",
+ "signature": "4609793b23e9d09362dc21bb47da0b4f3a7622649a47d464019b9aeafe53359c178c91cd58ba6bcb78be0346a7bc637f4b873d4bab38ee661f199634c547a1ad8442e03da015b136e543f7ab07c0c13e4225b8de8cce25d4f6eb8400f81f7e1833b7ee6e334d370964ca79fdb872b4d75223b5eeb08101591fb532d155a6de87"
+ },
+ {
+ "message": "b45581547e5427770c768e8b82b75564e0ea4e9c32594d6bff706544de0a8776c7a80b4576550eee1b2acabc7e8b7d3ef7bb5b03e462c11047eadd00629ae575480ac1470fe046f13a2bf5af17921dc4b0aa8b02bee6334911651d7f8525d10f32b51d33be520d3ddf5a709955a3dfe78283b9e0ab54046d150c177f037fdccc5be4ea5f68b5e5a38c9d7edcccc4975f455a6909b4",
+ "salt": "80e70ff86a08de3ec60972b39b4fbfdcea67ae8e",
+ "signature": "1d2aad221ca4d31ddf13509239019398e3d14b32dc34dc5af4aeaea3c095af73479cf0a45e5629635a53a018377615b16cb9b13b3e09d671eb71e387b8545c5960da5a64776e768e82b2c93583bf104c3fdb23512b7b4e89f633dd0063a530db4524b01c3f384c09310e315a79dcd3d684022a7f31c865a664e316978b759fad"
+ },
+ {
+ "message": "10aae9a0ab0b595d0841207b700d48d75faedde3b775cd6b4cc88ae06e4694ec74ba18f8520d4f5ea69cbbe7cc2beba43efdc10215ac4eb32dc302a1f53dc6c4352267e7936cfebf7c8d67035784a3909fa859c7b7b59b8e39c5c2349f1886b705a30267d402f7486ab4f58cad5d69adb17ab8cd0ce1caf5025af4ae24b1fb8794c6070cc09a51e2f9911311e3877d0044c71c57a993395008806b723ac38373d395481818528c1e7053739282053529510e935cd0fa77b8fa53cc2d474bd4fb3cc5c672d6ffdc90a00f9848712c4bcfe46c60573659b11e6457e861f0f604b6138d144f8ce4e2da73",
+ "salt": "a8ab69dd801f0074c2a1fc60649836c616d99681",
+ "signature": "2a34f6125e1f6b0bf971e84fbd41c632be8f2c2ace7de8b6926e31ff93e9af987fbc06e51e9be14f5198f91f3f953bd67da60a9df59764c3dc0fe08e1cbef0b75f868d10ad3fba749fef59fb6dac46a0d6e504369331586f58e4628f39aa278982543bc0eeb537dc61958019b394fb273f215858a0a01ac4d650b955c67f4c58"
+ }
+ ]
+ },
+ "example10": {
+ "publicKey": [
+ "-----BEGIN PUBLIC KEY-----",
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd2GesTLAvkLlFfUjBSn",
+ "cO+ZHFbDnA7GX9Ea+ok3zqV7m+esc7RcABdhW4LWIuMYdTtgJ8D9FXvhL4CQ/uKn",
+ "rc0O73WfiLpJl8ekLVjJqhLLma4AH+UhwTu1QxRFqNWuT15MfpSKwifTYEBx8g5X",
+ "fpBfvrFd+vBtHeWuYlPWOmohILMaXaXavJVQYA4g8n03OeJieSX+o8xQnyHf8E5u",
+ "6kVJxUDWgJ/5MH7t6R//WHM9g4WiN9bTcFoz45GQCZIHDfet8TV89+NwDONmfeg/",
+ "F7jfF3jbOB3OCctK0FilEQAac4GY7ifPVaE7dUU5kGWC7IsXS9WNXR89dnxhNyGu",
+ "BQIDAQAB",
+ "-----END PUBLIC KEY-----"
+ ],
+ "tests": [
+ {
+ "message": "883177e5126b9be2d9a9680327d5370c6f26861f5820c43da67a3ad609",
+ "salt": "04e215ee6ff934b9da70d7730c8734abfcecde89",
+ "signature": "82c2b160093b8aa3c0f7522b19f87354066c77847abf2a9fce542d0e84e920c5afb49ffdfdace16560ee94a1369601148ebad7a0e151cf16331791a5727d05f21e74e7eb811440206935d744765a15e79f015cb66c532c87a6a05961c8bfad741a9a6657022894393e7223739796c02a77455d0f555b0ec01ddf259b6207fd0fd57614cef1a5573baaff4ec00069951659b85f24300a25160ca8522dc6e6727e57d019d7e63629b8fe5e89e25cc15beb3a647577559299280b9b28f79b0409000be25bbd96408ba3b43cc486184dd1c8e62553fa1af4040f60663de7f5e49c04388e257f1ce89c95dab48a315d9b66b1b7628233876ff2385230d070d07e1666"
+ },
+ {
+ "message": "dd670a01465868adc93f26131957a50c52fb777cdbaa30892c9e12361164ec13979d43048118e4445db87bee58dd987b3425d02071d8dbae80708b039dbb64dbd1de5657d9fed0c118a54143742e0ff3c87f74e45857647af3f79eb0a14c9d75ea9a1a04b7cf478a897a708fd988f48e801edb0b7039df8c23bb3c56f4e821ac",
+ "salt": "8b2bdd4b40faf545c778ddf9bc1a49cb57f9b71b",
+ "signature": "14ae35d9dd06ba92f7f3b897978aed7cd4bf5ff0b585a40bd46ce1b42cd2703053bb9044d64e813d8f96db2dd7007d10118f6f8f8496097ad75e1ff692341b2892ad55a633a1c55e7f0a0ad59a0e203a5b8278aec54dd8622e2831d87174f8caff43ee6c46445345d84a59659bfb92ecd4c818668695f34706f66828a89959637f2bf3e3251c24bdba4d4b7649da0022218b119c84e79a6527ec5b8a5f861c159952e23ec05e1e717346faefe8b1686825bd2b262fb2531066c0de09acde2e4231690728b5d85e115a2f6b92b79c25abc9bd9399ff8bcf825a52ea1f56ea76dd26f43baafa18bfa92a504cbd35699e26d1dcc5a2887385f3c63232f06f3244c3"
+ },
+ {
+ "message": "48b2b6a57a63c84cea859d65c668284b08d96bdcaabe252db0e4a96cb1bac6019341db6fbefb8d106b0e90eda6bcc6c6262f37e7ea9c7e5d226bd7df85ec5e71efff2f54c5db577ff729ff91b842491de2741d0c631607df586b905b23b91af13da12304bf83eca8a73e871ff9db",
+ "salt": "4e96fc1b398f92b44671010c0dc3efd6e20c2d73",
+ "signature": "6e3e4d7b6b15d2fb46013b8900aa5bbb3939cf2c095717987042026ee62c74c54cffd5d7d57efbbf950a0f5c574fa09d3fc1c9f513b05b4ff50dd8df7edfa20102854c35e592180119a70ce5b085182aa02d9ea2aa90d1df03f2daae885ba2f5d05afdac97476f06b93b5bc94a1a80aa9116c4d615f333b098892b25fface266f5db5a5a3bcc10a824ed55aad35b727834fb8c07da28fcf416a5d9b2224f1f8b442b36f91e456fdea2d7cfe3367268de0307a4c74e924159ed33393d5e0655531c77327b89821bdedf880161c78cd4196b5419f7acc3f13e5ebf161b6e7c6724716ca33b85c2e25640192ac2859651d50bde7eb976e51cec828b98b6563b86bb"
+ },
+ {
+ "message": "0b8777c7f839baf0a64bbbdbc5ce79755c57a205b845c174e2d2e90546a089c4e6ec8adffa23a7ea97bae6b65d782b82db5d2b5a56d22a29a05e7c4433e2b82a621abba90add05ce393fc48a840542451a",
+ "salt": "c7cd698d84b65128d8835e3a8b1eb0e01cb541ec",
+ "signature": "34047ff96c4dc0dc90b2d4ff59a1a361a4754b255d2ee0af7d8bf87c9bc9e7ddeede33934c63ca1c0e3d262cb145ef932a1f2c0a997aa6a34f8eaee7477d82ccf09095a6b8acad38d4eec9fb7eab7ad02da1d11d8e54c1825e55bf58c2a23234b902be124f9e9038a8f68fa45dab72f66e0945bf1d8bacc9044c6f07098c9fcec58a3aab100c805178155f030a124c450e5acbda47d0e4f10b80a23f803e774d023b0015c20b9f9bbe7c91296338d5ecb471cafb032007b67a60be5f69504a9f01abb3cb467b260e2bce860be8d95bf92c0c8e1496ed1e528593a4abb6df462dde8a0968dffe4683116857a232f5ebf6c85be238745ad0f38f767a5fdbf486fb"
+ },
+ {
+ "message": "f1036e008e71e964dadc9219ed30e17f06b4b68a955c16b312b1eddf028b74976bed6b3f6a63d4e77859243c9cccdc98016523abb02483b35591c33aad81213bb7c7bb1a470aabc10d44256c4d4559d916",
+ "salt": "efa8bff96212b2f4a3f371a10d574152655f5dfb",
+ "signature": "7e0935ea18f4d6c1d17ce82eb2b3836c55b384589ce19dfe743363ac9948d1f346b7bfddfe92efd78adb21faefc89ade42b10f374003fe122e67429a1cb8cbd1f8d9014564c44d120116f4990f1a6e38774c194bd1b8213286b077b0499d2e7b3f434ab12289c556684deed78131934bb3dd6537236f7c6f3dcb09d476be07721e37e1ceed9b2f7b406887bd53157305e1c8b4f84d733bc1e186fe06cc59b6edb8f4bd7ffefdf4f7ba9cfb9d570689b5a1a4109a746a690893db3799255a0cb9215d2d1cd490590e952e8c8786aa0011265252470c041dfbc3eec7c3cbf71c24869d115c0cb4a956f56d530b80ab589acfefc690751ddf36e8d383f83cedd2cc"
+ },
+ {
+ "message": "25f10895a87716c137450bb9519dfaa1f207faa942ea88abf71e9c17980085b555aebab76264ae2a3ab93c2d12981191ddac6fb5949eb36aee3c5da940f00752c916d94608fa7d97ba6a2915b688f20323d4e9d96801d89a72ab5892dc2117c07434fcf972e058cf8c41ca4b4ff554f7d5068ad3155fced0f3125bc04f9193378a8f5c4c3b8cb4dd6d1cc69d30ecca6eaa51e36a05730e9e342e855baf099defb8afd7",
+ "salt": "ad8b1523703646224b660b550885917ca2d1df28",
+ "signature": "6d3b5b87f67ea657af21f75441977d2180f91b2c5f692de82955696a686730d9b9778d970758ccb26071c2209ffbd6125be2e96ea81b67cb9b9308239fda17f7b2b64ecda096b6b935640a5a1cb42a9155b1c9ef7a633a02c59f0d6ee59b852c43b35029e73c940ff0410e8f114eed46bbd0fae165e42be2528a401c3b28fd818ef3232dca9f4d2a0f5166ec59c42396d6c11dbc1215a56fa17169db9575343ef34f9de32a49cdc3174922f229c23e18e45df9353119ec4319cedce7a17c64088c1f6f52be29634100b3919d38f3d1ed94e6891e66a73b8fb849f5874df59459e298c7bbce2eee782a195aa66fe2d0732b25e595f57d3e061b1fc3e4063bf98f"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js
index a9a2cac78828ab..fa410de4a62b4f 100644
--- a/test/parallel/test-crypto-sign-verify.js
+++ b/test/parallel/test-crypto-sign-verify.js
@@ -4,12 +4,20 @@ if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
+const exec = require('child_process').exec;
const fs = require('fs');
+const path = require('path');
+
+if (!common.hasCrypto) {
+ common.skip('missing crypto');
+ return;
+}
const crypto = require('crypto');
// Test certificates
const certPem = fs.readFileSync(`${common.fixturesDir}/test_cert.pem`, 'ascii');
const keyPem = fs.readFileSync(`${common.fixturesDir}/test_key.pem`, 'ascii');
+const modSize = 1024;
// Test signing and verifying
{
@@ -69,9 +77,203 @@ const keyPem = fs.readFileSync(`${common.fixturesDir}/test_key.pem`, 'ascii');
assert.strictEqual(verified, true, 'sign and verify (stream)');
}
+// Special tests for RSA_PKCS1_PSS_PADDING
+{
+ function testPSS(algo, hLen) {
+ // Maximum permissible salt length
+ const max = modSize / 8 - hLen - 2;
+
+ function getEffectiveSaltLength(saltLength) {
+ switch (saltLength) {
+ case crypto.constants.RSA_PSS_SALTLEN_DIGEST:
+ return hLen;
+ case crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN:
+ return max;
+ default:
+ return saltLength;
+ }
+ }
+
+ const signSaltLengths = [
+ crypto.constants.RSA_PSS_SALTLEN_DIGEST,
+ getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST),
+ crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN,
+ getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN),
+ 0, 16, 32, 64, 128
+ ];
+
+ const verifySaltLengths = [
+ crypto.constants.RSA_PSS_SALTLEN_DIGEST,
+ getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST),
+ getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN),
+ 0, 16, 32, 64, 128
+ ];
+
+ signSaltLengths.forEach((signSaltLength) => {
+ if (signSaltLength > max) {
+ // If the salt length is too big, an Error should be thrown
+ assert.throws(() => {
+ crypto.createSign(algo)
+ .update('Test123')
+ .sign({
+ key: keyPem,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: signSaltLength
+ });
+ }, /^Error:.*data too large for key size$/);
+ } else {
+ // Otherwise, a valid signature should be generated
+ const s4 = crypto.createSign(algo)
+ .update('Test123')
+ .sign({
+ key: keyPem,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: signSaltLength
+ });
+
+ let verified;
+ verifySaltLengths.forEach((verifySaltLength) => {
+ // Verification should succeed if and only if the salt length is
+ // correct
+ verified = crypto.createVerify(algo)
+ .update('Test123')
+ .verify({
+ key: certPem,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: verifySaltLength
+ }, s4);
+ const saltLengthCorrect = getEffectiveSaltLength(signSaltLength) ==
+ getEffectiveSaltLength(verifySaltLength);
+ assert.strictEqual(verified, saltLengthCorrect, 'verify (PSS)');
+ });
+
+ // Verification using RSA_PSS_SALTLEN_AUTO should always work
+ verified = crypto.createVerify(algo)
+ .update('Test123')
+ .verify({
+ key: certPem,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO
+ }, s4);
+ assert.strictEqual(verified, true, 'verify (PSS with SALTLEN_AUTO)');
+
+ // Verifying an incorrect message should never work
+ verified = crypto.createVerify(algo)
+ .update('Test1234')
+ .verify({
+ key: certPem,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO
+ }, s4);
+ assert.strictEqual(verified, false, 'verify (PSS, incorrect)');
+ }
+ });
+ }
+
+ testPSS('RSA-SHA1', 20);
+ testPSS('RSA-SHA256', 32);
+}
+
+// Test vectors for RSA_PKCS1_PSS_PADDING provided by the RSA Laboratories:
+// https://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm
+{
+ // We only test verification as we cannot specify explicit salts when signing
+ function testVerify(cert, vector) {
+ const verified = crypto.createVerify('RSA-SHA1')
+ .update(Buffer.from(vector.message, 'hex'))
+ .verify({
+ key: cert,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: vector.salt.length / 2
+ }, vector.signature, 'hex');
+ assert.strictEqual(verified, true, 'verify (PSS)');
+ }
+
+ const vectorfile = path.join(common.fixturesDir, 'pss-vectors.json');
+ const examples = JSON.parse(fs.readFileSync(vectorfile, {
+ encoding: 'utf8'
+ }));
+
+ for (const key in examples) {
+ const example = examples[key];
+ const publicKey = example.publicKey.join('\n');
+ example.tests.forEach((test) => testVerify(publicKey, test));
+ }
+}
+
+// Test exceptions for invalid `padding` and `saltLength` values
+{
+ [null, undefined, NaN, 'boom', {}, [], true, false]
+ .forEach((invalidValue) => {
+ assert.throws(() => {
+ crypto.createSign('RSA-SHA256')
+ .update('Test123')
+ .sign({
+ key: keyPem,
+ padding: invalidValue
+ });
+ }, /^TypeError: padding must be an integer$/);
+
+ assert.throws(() => {
+ crypto.createSign('RSA-SHA256')
+ .update('Test123')
+ .sign({
+ key: keyPem,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
+ saltLength: invalidValue
+ });
+ }, /^TypeError: saltLength must be an integer$/);
+ });
+
+ assert.throws(() => {
+ crypto.createSign('RSA-SHA1')
+ .update('Test123')
+ .sign({
+ key: keyPem,
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
+ });
+ }, /^Error:.*illegal or unsupported padding mode$/);
+}
+
// Test throws exception when key options is null
{
assert.throws(() => {
crypto.createSign('RSA-SHA1').update('Test123').sign(null, 'base64');
}, /^Error: No key provided to sign$/);
}
+
+// RSA-PSS Sign test by verifying with 'openssl dgst -verify'
+{
+ if (!common.opensslCli) {
+ common.skip('node compiled without OpenSSL CLI.');
+ return;
+ }
+
+ const pubfile = path.join(common.fixturesDir, 'keys/rsa_public_2048.pem');
+ const privfile = path.join(common.fixturesDir, 'keys/rsa_private_2048.pem');
+ const privkey = fs.readFileSync(privfile);
+
+ const msg = 'Test123';
+ const s5 = crypto.createSign('RSA-SHA256')
+ .update(msg)
+ .sign({
+ key: privkey,
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING
+ });
+
+ common.refreshTmpDir();
+
+ const sigfile = path.join(common.tmpDir, 's5.sig');
+ fs.writeFileSync(sigfile, s5);
+ const msgfile = path.join(common.tmpDir, 's5.msg');
+ fs.writeFileSync(msgfile, msg);
+
+ const cmd = '"' + common.opensslCli + '" dgst -sha256 -verify "' + pubfile +
+ '" -signature "' + sigfile +
+ '" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "' +
+ msgfile + '"';
+
+ exec(cmd, common.mustCall((err, stdout, stderr) => {
+ assert(stdout.includes('Verified OK'));
+ }));
+}
diff --git a/test/parallel/test-path-basename.js b/test/parallel/test-path-basename.js
new file mode 100644
index 00000000000000..6b64e40b7eca7a
--- /dev/null
+++ b/test/parallel/test-path-basename.js
@@ -0,0 +1,70 @@
+'use strict';
+require('../common');
+const assert = require('assert');
+const path = require('path');
+
+assert.strictEqual(path.basename(__filename), 'test-path-basename.js');
+assert.strictEqual(path.basename(__filename, '.js'), 'test-path-basename');
+assert.strictEqual(path.basename('.js', '.js'), '');
+assert.strictEqual(path.basename(''), '');
+assert.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext');
+assert.strictEqual(path.basename('/basename.ext'), 'basename.ext');
+assert.strictEqual(path.basename('basename.ext'), 'basename.ext');
+assert.strictEqual(path.basename('basename.ext/'), 'basename.ext');
+assert.strictEqual(path.basename('basename.ext//'), 'basename.ext');
+assert.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb');
+assert.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb');
+assert.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb');
+assert.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb');
+assert.strictEqual(path.basename('aaa/bbb', 'bb'), 'b');
+assert.strictEqual(path.basename('aaa/bbb', 'b'), 'bb');
+assert.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb');
+assert.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb');
+assert.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb');
+assert.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb');
+assert.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b');
+assert.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb');
+assert.strictEqual(path.basename('/aaa/bbb'), 'bbb');
+assert.strictEqual(path.basename('/aaa/'), 'aaa');
+assert.strictEqual(path.basename('/aaa/b'), 'b');
+assert.strictEqual(path.basename('/a/b'), 'b');
+assert.strictEqual(path.basename('//a'), 'a');
+
+// On Windows a backslash acts as a path separator.
+assert.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext');
+assert.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext');
+assert.strictEqual(path.win32.basename('basename.ext'), 'basename.ext');
+assert.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext');
+assert.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext');
+assert.strictEqual(path.win32.basename('foo'), 'foo');
+assert.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb');
+assert.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb');
+assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb');
+assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb');
+assert.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b');
+assert.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb');
+assert.strictEqual(path.win32.basename('C:'), '');
+assert.strictEqual(path.win32.basename('C:.'), '.');
+assert.strictEqual(path.win32.basename('C:\\'), '');
+assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext');
+assert.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext');
+assert.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext');
+assert.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext');
+assert.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext');
+assert.strictEqual(path.win32.basename('C:foo'), 'foo');
+assert.strictEqual(path.win32.basename('file:stream'), 'file:stream');
+
+// On unix a backslash is just treated as any other character.
+assert.strictEqual(path.posix.basename('\\dir\\basename.ext'),
+ '\\dir\\basename.ext');
+assert.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext');
+assert.strictEqual(path.posix.basename('basename.ext'), 'basename.ext');
+assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\');
+assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\');
+assert.strictEqual(path.posix.basename('foo'), 'foo');
+
+// POSIX filenames may include control characters
+// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
+const controlCharFilename = `Icon${String.fromCharCode(13)}`;
+assert.strictEqual(path.posix.basename(`/a/b/${controlCharFilename}`),
+ controlCharFilename);
diff --git a/test/parallel/test-path-dirname.js b/test/parallel/test-path-dirname.js
new file mode 100644
index 00000000000000..6ecb5c93b4db74
--- /dev/null
+++ b/test/parallel/test-path-dirname.js
@@ -0,0 +1,56 @@
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const path = require('path');
+
+assert.strictEqual(path.dirname(__filename).substr(-13),
+ common.isWindows ? 'test\\parallel' : 'test/parallel');
+
+assert.strictEqual(path.posix.dirname('/a/b/'), '/a');
+assert.strictEqual(path.posix.dirname('/a/b'), '/a');
+assert.strictEqual(path.posix.dirname('/a'), '/');
+assert.strictEqual(path.posix.dirname(''), '.');
+assert.strictEqual(path.posix.dirname('/'), '/');
+assert.strictEqual(path.posix.dirname('////'), '/');
+assert.strictEqual(path.posix.dirname('//a'), '//');
+assert.strictEqual(path.posix.dirname('foo'), '.');
+
+assert.strictEqual(path.win32.dirname('c:\\'), 'c:\\');
+assert.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\');
+assert.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\');
+assert.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo');
+assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo');
+assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar');
+assert.strictEqual(path.win32.dirname('\\'), '\\');
+assert.strictEqual(path.win32.dirname('\\foo'), '\\');
+assert.strictEqual(path.win32.dirname('\\foo\\'), '\\');
+assert.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo');
+assert.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo');
+assert.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar');
+assert.strictEqual(path.win32.dirname('c:'), 'c:');
+assert.strictEqual(path.win32.dirname('c:foo'), 'c:');
+assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:');
+assert.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo');
+assert.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo');
+assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar');
+assert.strictEqual(path.win32.dirname('file:stream'), '.');
+assert.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir');
+assert.strictEqual(path.win32.dirname('\\\\unc\\share'),
+ '\\\\unc\\share');
+assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'),
+ '\\\\unc\\share\\');
+assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'),
+ '\\\\unc\\share\\');
+assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'),
+ '\\\\unc\\share\\foo');
+assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'),
+ '\\\\unc\\share\\foo');
+assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'),
+ '\\\\unc\\share\\foo\\bar');
+assert.strictEqual(path.win32.dirname('/a/b/'), '/a');
+assert.strictEqual(path.win32.dirname('/a/b'), '/a');
+assert.strictEqual(path.win32.dirname('/a'), '/');
+assert.strictEqual(path.win32.dirname(''), '.');
+assert.strictEqual(path.win32.dirname('/'), '/');
+assert.strictEqual(path.win32.dirname('////'), '/');
+assert.strictEqual(path.win32.dirname('foo'), '.');
diff --git a/test/parallel/test-path-extname.js b/test/parallel/test-path-extname.js
new file mode 100644
index 00000000000000..47b327d370d78e
--- /dev/null
+++ b/test/parallel/test-path-extname.js
@@ -0,0 +1,99 @@
+'use strict';
+require('../common');
+const assert = require('assert');
+const path = require('path');
+
+const failures = [];
+const slashRE = /\//g;
+
+[
+ [__filename, '.js'],
+ ['', ''],
+ ['/path/to/file', ''],
+ ['/path/to/file.ext', '.ext'],
+ ['/path.to/file.ext', '.ext'],
+ ['/path.to/file', ''],
+ ['/path.to/.file', ''],
+ ['/path.to/.file.ext', '.ext'],
+ ['/path/to/f.ext', '.ext'],
+ ['/path/to/..ext', '.ext'],
+ ['/path/to/..', ''],
+ ['file', ''],
+ ['file.ext', '.ext'],
+ ['.file', ''],
+ ['.file.ext', '.ext'],
+ ['/file', ''],
+ ['/file.ext', '.ext'],
+ ['/.file', ''],
+ ['/.file.ext', '.ext'],
+ ['.path/file.ext', '.ext'],
+ ['file.ext.ext', '.ext'],
+ ['file.', '.'],
+ ['.', ''],
+ ['./', ''],
+ ['.file.ext', '.ext'],
+ ['.file', ''],
+ ['.file.', '.'],
+ ['.file..', '.'],
+ ['..', ''],
+ ['../', ''],
+ ['..file.ext', '.ext'],
+ ['..file', '.file'],
+ ['..file.', '.'],
+ ['..file..', '.'],
+ ['...', '.'],
+ ['...ext', '.ext'],
+ ['....', '.'],
+ ['file.ext/', '.ext'],
+ ['file.ext//', '.ext'],
+ ['file/', ''],
+ ['file//', ''],
+ ['file./', '.'],
+ ['file.//', '.'],
+].forEach((test) => {
+ const expected = test[1];
+ [path.posix.extname, path.win32.extname].forEach((extname) => {
+ let input = test[0];
+ let os;
+ if (extname === path.win32.extname) {
+ input = input.replace(slashRE, '\\');
+ os = 'win32';
+ } else {
+ os = 'posix';
+ }
+ const actual = extname(input);
+ const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${
+ JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
+ if (actual !== expected)
+ failures.push(`\n${message}`);
+ });
+ {
+ const input = `C:${test[0].replace(slashRE, '\\')}`;
+ const actual = path.win32.extname(input);
+ const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${
+ JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
+ if (actual !== expected)
+ failures.push(`\n${message}`);
+ }
+});
+assert.strictEqual(failures.length, 0, failures.join(''));
+
+// On Windows, backslash is a path separator.
+assert.strictEqual(path.win32.extname('.\\'), '');
+assert.strictEqual(path.win32.extname('..\\'), '');
+assert.strictEqual(path.win32.extname('file.ext\\'), '.ext');
+assert.strictEqual(path.win32.extname('file.ext\\\\'), '.ext');
+assert.strictEqual(path.win32.extname('file\\'), '');
+assert.strictEqual(path.win32.extname('file\\\\'), '');
+assert.strictEqual(path.win32.extname('file.\\'), '.');
+assert.strictEqual(path.win32.extname('file.\\\\'), '.');
+
+// On *nix, backslash is a valid name component like any other character.
+assert.strictEqual(path.posix.extname('.\\'), '');
+assert.strictEqual(path.posix.extname('..\\'), '.\\');
+assert.strictEqual(path.posix.extname('file.ext\\'), '.ext\\');
+assert.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\');
+assert.strictEqual(path.posix.extname('file\\'), '');
+assert.strictEqual(path.posix.extname('file\\\\'), '');
+assert.strictEqual(path.posix.extname('file.\\'), '.\\');
+assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\');
diff --git a/test/parallel/test-path-isabsolute.js b/test/parallel/test-path-isabsolute.js
new file mode 100644
index 00000000000000..66b4f1ee51103a
--- /dev/null
+++ b/test/parallel/test-path-isabsolute.js
@@ -0,0 +1,28 @@
+'use strict';
+require('../common');
+const assert = require('assert');
+const path = require('path');
+
+assert.strictEqual(path.win32.isAbsolute('/'), true);
+assert.strictEqual(path.win32.isAbsolute('//'), true);
+assert.strictEqual(path.win32.isAbsolute('//server'), true);
+assert.strictEqual(path.win32.isAbsolute('//server/file'), true);
+assert.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true);
+assert.strictEqual(path.win32.isAbsolute('\\\\server'), true);
+assert.strictEqual(path.win32.isAbsolute('\\\\'), true);
+assert.strictEqual(path.win32.isAbsolute('c'), false);
+assert.strictEqual(path.win32.isAbsolute('c:'), false);
+assert.strictEqual(path.win32.isAbsolute('c:\\'), true);
+assert.strictEqual(path.win32.isAbsolute('c:/'), true);
+assert.strictEqual(path.win32.isAbsolute('c://'), true);
+assert.strictEqual(path.win32.isAbsolute('C:/Users/'), true);
+assert.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true);
+assert.strictEqual(path.win32.isAbsolute('C:cwd/another'), false);
+assert.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false);
+assert.strictEqual(path.win32.isAbsolute('directory/directory'), false);
+assert.strictEqual(path.win32.isAbsolute('directory\\directory'), false);
+
+assert.strictEqual(path.posix.isAbsolute('/home/foo'), true);
+assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true);
+assert.strictEqual(path.posix.isAbsolute('bar/'), false);
+assert.strictEqual(path.posix.isAbsolute('./baz'), false);
diff --git a/test/parallel/test-path-join.js b/test/parallel/test-path-join.js
new file mode 100644
index 00000000000000..691ba98f9bc095
--- /dev/null
+++ b/test/parallel/test-path-join.js
@@ -0,0 +1,142 @@
+'use strict';
+require('../common');
+const assert = require('assert');
+const path = require('path');
+
+const failures = [];
+const backslashRE = /\\/g;
+
+const joinTests = [
+ [ [path.posix.join, path.win32.join],
+ // arguments result
+ [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'],
+ [[], '.'],
+ [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'],
+ [['/foo', '../../../bar'], '/bar'],
+ [['foo', '../../../bar'], '../../bar'],
+ [['foo/', '../../../bar'], '../../bar'],
+ [['foo/x', '../../../bar'], '../bar'],
+ [['foo/x', './bar'], 'foo/x/bar'],
+ [['foo/x/', './bar'], 'foo/x/bar'],
+ [['foo/x/', '.', 'bar'], 'foo/x/bar'],
+ [['./'], './'],
+ [['.', './'], './'],
+ [['.', '.', '.'], '.'],
+ [['.', './', '.'], '.'],
+ [['.', '/./', '.'], '.'],
+ [['.', '/////./', '.'], '.'],
+ [['.'], '.'],
+ [['', '.'], '.'],
+ [['', 'foo'], 'foo'],
+ [['foo', '/bar'], 'foo/bar'],
+ [['', '/foo'], '/foo'],
+ [['', '', '/foo'], '/foo'],
+ [['', '', 'foo'], 'foo'],
+ [['foo', ''], 'foo'],
+ [['foo/', ''], 'foo/'],
+ [['foo', '', '/bar'], 'foo/bar'],
+ [['./', '..', '/foo'], '../foo'],
+ [['./', '..', '..', '/foo'], '../../foo'],
+ [['.', '..', '..', '/foo'], '../../foo'],
+ [['', '..', '..', '/foo'], '../../foo'],
+ [['/'], '/'],
+ [['/', '.'], '/'],
+ [['/', '..'], '/'],
+ [['/', '..', '..'], '/'],
+ [[''], '.'],
+ [['', ''], '.'],
+ [[' /foo'], ' /foo'],
+ [[' ', 'foo'], ' /foo'],
+ [[' ', '.'], ' '],
+ [[' ', '/'], ' /'],
+ [[' ', ''], ' '],
+ [['/', 'foo'], '/foo'],
+ [['/', '/foo'], '/foo'],
+ [['/', '//foo'], '/foo'],
+ [['/', '', '/foo'], '/foo'],
+ [['', '/', 'foo'], '/foo'],
+ [['', '/', '/foo'], '/foo']
+ ]
+ ]
+];
+
+// Windows-specific join tests
+joinTests.push([
+ path.win32.join,
+ joinTests[0][1].slice(0).concat(
+ [// arguments result
+ // UNC path expected
+ [['//foo/bar'], '\\\\foo\\bar\\'],
+ [['\\/foo/bar'], '\\\\foo\\bar\\'],
+ [['\\\\foo/bar'], '\\\\foo\\bar\\'],
+ // UNC path expected - server and share separate
+ [['//foo', 'bar'], '\\\\foo\\bar\\'],
+ [['//foo/', 'bar'], '\\\\foo\\bar\\'],
+ [['//foo', '/bar'], '\\\\foo\\bar\\'],
+ // UNC path expected - questionable
+ [['//foo', '', 'bar'], '\\\\foo\\bar\\'],
+ [['//foo/', '', 'bar'], '\\\\foo\\bar\\'],
+ [['//foo/', '', '/bar'], '\\\\foo\\bar\\'],
+ // UNC path expected - even more questionable
+ [['', '//foo', 'bar'], '\\\\foo\\bar\\'],
+ [['', '//foo/', 'bar'], '\\\\foo\\bar\\'],
+ [['', '//foo/', '/bar'], '\\\\foo\\bar\\'],
+ // No UNC path expected (no double slash in first component)
+ [['\\', 'foo/bar'], '\\foo\\bar'],
+ [['\\', '/foo/bar'], '\\foo\\bar'],
+ [['', '/', '/foo/bar'], '\\foo\\bar'],
+ // No UNC path expected (no non-slashes in first component -
+ // questionable)
+ [['//', 'foo/bar'], '\\foo\\bar'],
+ [['//', '/foo/bar'], '\\foo\\bar'],
+ [['\\\\', '/', '/foo/bar'], '\\foo\\bar'],
+ [['//'], '/'],
+ // No UNC path expected (share name missing - questionable).
+ [['//foo'], '\\foo'],
+ [['//foo/'], '\\foo\\'],
+ [['//foo', '/'], '\\foo\\'],
+ [['//foo', '', '/'], '\\foo\\'],
+ // No UNC path expected (too many leading slashes - questionable)
+ [['///foo/bar'], '\\foo\\bar'],
+ [['////foo', 'bar'], '\\foo\\bar'],
+ [['\\\\\\/foo/bar'], '\\foo\\bar'],
+ // Drive-relative vs drive-absolute paths. This merely describes the
+ // status quo, rather than being obviously right
+ [['c:'], 'c:.'],
+ [['c:.'], 'c:.'],
+ [['c:', ''], 'c:.'],
+ [['', 'c:'], 'c:.'],
+ [['c:.', '/'], 'c:.\\'],
+ [['c:.', 'file'], 'c:file'],
+ [['c:', '/'], 'c:\\'],
+ [['c:', 'file'], 'c:\\file']
+ ]
+ )
+]);
+joinTests.forEach((test) => {
+ if (!Array.isArray(test[0]))
+ test[0] = [test[0]];
+ test[0].forEach((join) => {
+ test[1].forEach((test) => {
+ const actual = join.apply(null, test[0]);
+ const expected = test[1];
+ // For non-Windows specific tests with the Windows join(), we need to try
+ // replacing the slashes since the non-Windows specific tests' `expected`
+ // use forward slashes
+ let actualAlt;
+ let os;
+ if (join === path.win32.join) {
+ actualAlt = actual.replace(backslashRE, '/');
+ os = 'win32';
+ } else {
+ os = 'posix';
+ }
+ const message =
+ `path.${os}.join(${test[0].map(JSON.stringify).join(',')})\n expect=${
+ JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
+ if (actual !== expected && actualAlt !== expected)
+ failures.push(`\n${message}`);
+ });
+ });
+});
+assert.strictEqual(failures.length, 0, failures.join(''));
diff --git a/test/parallel/test-path-makelong.js b/test/parallel/test-path-makelong.js
index 769f81bdb81735..4e33d359e6cf11 100644
--- a/test/parallel/test-path-makelong.js
+++ b/test/parallel/test-path-makelong.js
@@ -23,3 +23,37 @@ assert.strictEqual(path._makeLong(100), 100);
assert.strictEqual(path._makeLong(path), path);
assert.strictEqual(path._makeLong(false), false);
assert.strictEqual(path._makeLong(true), true);
+
+const emptyObj = {};
+assert.strictEqual(path.posix._makeLong('/foo/bar'), '/foo/bar');
+assert.strictEqual(path.posix._makeLong('foo/bar'), 'foo/bar');
+assert.strictEqual(path.posix._makeLong(null), null);
+assert.strictEqual(path.posix._makeLong(true), true);
+assert.strictEqual(path.posix._makeLong(1), 1);
+assert.strictEqual(path.posix._makeLong(), undefined);
+assert.strictEqual(path.posix._makeLong(emptyObj), emptyObj);
+if (common.isWindows) {
+ // These tests cause resolve() to insert the cwd, so we cannot test them from
+ // non-Windows platforms (easily)
+ assert.strictEqual(path.win32._makeLong('foo\\bar').toLowerCase(),
+ `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`);
+ assert.strictEqual(path.win32._makeLong('foo/bar').toLowerCase(),
+ `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`);
+ const currentDeviceLetter = path.parse(process.cwd()).root.substring(0, 2);
+ assert.strictEqual(path.win32._makeLong(currentDeviceLetter).toLowerCase(),
+ `\\\\?\\${process.cwd().toLowerCase()}`);
+ assert.strictEqual(path.win32._makeLong('C').toLowerCase(),
+ `\\\\?\\${process.cwd().toLowerCase()}\\c`);
+}
+assert.strictEqual(path.win32._makeLong('C:\\foo'), '\\\\?\\C:\\foo');
+assert.strictEqual(path.win32._makeLong('C:/foo'), '\\\\?\\C:\\foo');
+assert.strictEqual(path.win32._makeLong('\\\\foo\\bar'),
+ '\\\\?\\UNC\\foo\\bar\\');
+assert.strictEqual(path.win32._makeLong('//foo//bar'),
+ '\\\\?\\UNC\\foo\\bar\\');
+assert.strictEqual(path.win32._makeLong('\\\\?\\foo'), '\\\\?\\foo');
+assert.strictEqual(path.win32._makeLong(null), null);
+assert.strictEqual(path.win32._makeLong(true), true);
+assert.strictEqual(path.win32._makeLong(1), 1);
+assert.strictEqual(path.win32._makeLong(), undefined);
+assert.strictEqual(path.win32._makeLong(emptyObj), emptyObj);
diff --git a/test/parallel/test-path-normalize.js b/test/parallel/test-path-normalize.js
new file mode 100644
index 00000000000000..db91432b3e0955
--- /dev/null
+++ b/test/parallel/test-path-normalize.js
@@ -0,0 +1,39 @@
+'use strict';
+require('../common');
+const assert = require('assert');
+const path = require('path');
+
+assert.strictEqual(path.win32.normalize('./fixtures///b/../b/c.js'),
+ 'fixtures\\b\\c.js');
+assert.strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar');
+assert.strictEqual(path.win32.normalize('a//b//../b'), 'a\\b');
+assert.strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c');
+assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b');
+assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'),
+ '\\\\server\\share\\dir\\file.ext');
+assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z');
+assert.strictEqual(path.win32.normalize('C:'), 'C:.');
+assert.strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc');
+assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'),
+ 'C:..\\..\\def');
+assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\');
+assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream');
+assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\');
+assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar');
+assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz');
+assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\');
+assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..');
+
+assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'),
+ 'fixtures/b/c.js');
+assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar');
+assert.strictEqual(path.posix.normalize('a//b//../b'), 'a/b');
+assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c');
+assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b');
+assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z');
+assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar');
+assert.strictEqual(path.posix.normalize('bar/foo../../'), 'bar/');
+assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar');
+assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz');
+assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../');
+assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..');
diff --git a/test/parallel/test-path-relative.js b/test/parallel/test-path-relative.js
new file mode 100644
index 00000000000000..bd2c3f75a52dd2
--- /dev/null
+++ b/test/parallel/test-path-relative.js
@@ -0,0 +1,67 @@
+'use strict';
+require('../common');
+const assert = require('assert');
+const path = require('path');
+
+const failures = [];
+
+const relativeTests = [
+ [ path.win32.relative,
+ // arguments result
+ [['c:/blah\\blah', 'd:/games', 'd:\\games'],
+ ['c:/aaaa/bbbb', 'c:/aaaa', '..'],
+ ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'],
+ ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''],
+ ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'],
+ ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'],
+ ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'],
+ ['c:/aaaa/bbbb', 'd:\\', 'd:\\'],
+ ['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''],
+ ['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'],
+ ['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'],
+ ['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'],
+ ['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'],
+ ['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'],
+ ['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'],
+ ['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'],
+ ['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'],
+ ['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'],
+ ['C:\\baz-quux', 'C:\\baz', '..\\baz'],
+ ['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'],
+ ['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'],
+ ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'],
+ ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'],
+ ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz']
+ ]
+ ],
+ [ path.posix.relative,
+ // arguments result
+ [['/var/lib', '/var', '..'],
+ ['/var/lib', '/bin', '../../bin'],
+ ['/var/lib', '/var/lib', ''],
+ ['/var/lib', '/var/apache', '../apache'],
+ ['/var/', '/var/lib', 'lib'],
+ ['/', '/var/lib', 'var/lib'],
+ ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'],
+ ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'],
+ ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'],
+ ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'],
+ ['/baz-quux', '/baz', '../baz'],
+ ['/baz', '/baz-quux', '../baz-quux']
+ ]
+ ]
+];
+relativeTests.forEach((test) => {
+ const relative = test[0];
+ test[1].forEach((test) => {
+ const actual = relative(test[0], test[1]);
+ const expected = test[2];
+ const os = relative === path.win32.relative ? 'win32' : 'posix';
+ const message = `path.${os}.relative(${
+ test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${
+ JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
+ if (actual !== expected)
+ failures.push(`\n${message}`);
+ });
+});
+assert.strictEqual(failures.length, 0, failures.join(''));
diff --git a/test/parallel/test-path-resolve.js b/test/parallel/test-path-resolve.js
new file mode 100644
index 00000000000000..91a2807648fa40
--- /dev/null
+++ b/test/parallel/test-path-resolve.js
@@ -0,0 +1,70 @@
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const child = require('child_process');
+const path = require('path');
+
+const failures = [];
+const slashRE = /\//g;
+const backslashRE = /\\/g;
+
+const resolveTests = [
+ [ path.win32.resolve,
+ // arguments result
+ [[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'],
+ [['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'],
+ [['c:/ignore', 'c:/some/file'], 'c:\\some\\file'],
+ [['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'],
+ [['.'], process.cwd()],
+ [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'],
+ [['c:/', '//'], 'c:\\'],
+ [['c:/', '//dir'], 'c:\\dir'],
+ [['c:/', '//server/share'], '\\\\server\\share\\'],
+ [['c:/', '//server//share'], '\\\\server\\share\\'],
+ [['c:/', '///some//dir'], 'c:\\some\\dir'],
+ [['C:\\foo\\tmp.3\\', '..\\tmp.3\\cycles\\root.js'],
+ 'C:\\foo\\tmp.3\\cycles\\root.js']
+ ]
+ ],
+ [ path.posix.resolve,
+ // arguments result
+ [[['/var/lib', '../', 'file/'], '/var/file'],
+ [['/var/lib', '/../', 'file/'], '/file'],
+ [['a/b/c/', '../../..'], process.cwd()],
+ [['.'], process.cwd()],
+ [['/some/dir', '.', '/absolute/'], '/absolute'],
+ [['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js']
+ ]
+ ]
+];
+resolveTests.forEach((test) => {
+ const resolve = test[0];
+ test[1].forEach((test) => {
+ const actual = resolve.apply(null, test[0]);
+ let actualAlt;
+ const os = resolve === path.win32.resolve ? 'win32' : 'posix';
+ if (resolve === path.win32.resolve && !common.isWindows)
+ actualAlt = actual.replace(backslashRE, '/');
+ else if (resolve !== path.win32.resolve && common.isWindows)
+ actualAlt = actual.replace(slashRE, '\\');
+
+ const expected = test[1];
+ const message =
+ `path.${os}.resolve(${test[0].map(JSON.stringify).join(',')})\n expect=${
+ JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
+ if (actual !== expected && actualAlt !== expected)
+ failures.push(`\n${message}`);
+ });
+});
+assert.strictEqual(failures.length, 0, failures.join(''));
+
+if (common.isWindows) {
+ // Test resolving the current Windows drive letter from a spawned process.
+ // See https://github.com/nodejs/node/issues/7215
+ const currentDriveLetter = path.parse(process.cwd()).root.substring(0, 2);
+ const resolveFixture = path.join(common.fixturesDir, 'path-resolve.js');
+ const spawnResult = child.spawnSync(
+ process.argv[0], [resolveFixture, currentDriveLetter]);
+ const resolvedPath = spawnResult.stdout.toString().trim();
+ assert.strictEqual(resolvedPath.toLowerCase(), process.cwd().toLowerCase());
+}
diff --git a/test/parallel/test-path.js b/test/parallel/test-path.js
index 531b5d69e7b483..18eedbeb076681 100644
--- a/test/parallel/test-path.js
+++ b/test/parallel/test-path.js
@@ -1,368 +1,8 @@
'use strict';
const common = require('../common');
const assert = require('assert');
-const child = require('child_process');
const path = require('path');
-const f = __filename;
-const failures = [];
-
-const slashRE = /\//g;
-const backslashRE = /\\/g;
-
-// path.basename tests
-assert.strictEqual(path.basename(f), 'test-path.js');
-assert.strictEqual(path.basename(f, '.js'), 'test-path');
-assert.strictEqual(path.basename('.js', '.js'), '');
-assert.strictEqual(path.basename(''), '');
-assert.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext');
-assert.strictEqual(path.basename('/basename.ext'), 'basename.ext');
-assert.strictEqual(path.basename('basename.ext'), 'basename.ext');
-assert.strictEqual(path.basename('basename.ext/'), 'basename.ext');
-assert.strictEqual(path.basename('basename.ext//'), 'basename.ext');
-assert.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb');
-assert.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb');
-assert.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb');
-assert.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb');
-assert.strictEqual(path.basename('aaa/bbb', 'bb'), 'b');
-assert.strictEqual(path.basename('aaa/bbb', 'b'), 'bb');
-assert.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb');
-assert.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb');
-assert.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb');
-assert.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb');
-assert.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b');
-assert.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb');
-assert.strictEqual(path.basename('/aaa/bbb'), 'bbb');
-assert.strictEqual(path.basename('/aaa/'), 'aaa');
-assert.strictEqual(path.basename('/aaa/b'), 'b');
-assert.strictEqual(path.basename('/a/b'), 'b');
-assert.strictEqual(path.basename('//a'), 'a');
-
-// On Windows a backslash acts as a path separator.
-assert.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext');
-assert.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext');
-assert.strictEqual(path.win32.basename('basename.ext'), 'basename.ext');
-assert.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext');
-assert.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext');
-assert.strictEqual(path.win32.basename('foo'), 'foo');
-assert.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb');
-assert.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb');
-assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb');
-assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb');
-assert.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b');
-assert.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb');
-assert.strictEqual(path.win32.basename('C:'), '');
-assert.strictEqual(path.win32.basename('C:.'), '.');
-assert.strictEqual(path.win32.basename('C:\\'), '');
-assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext');
-assert.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext');
-assert.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext');
-assert.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext');
-assert.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext');
-assert.strictEqual(path.win32.basename('C:foo'), 'foo');
-assert.strictEqual(path.win32.basename('file:stream'), 'file:stream');
-
-// On unix a backslash is just treated as any other character.
-assert.strictEqual(path.posix.basename('\\dir\\basename.ext'),
- '\\dir\\basename.ext');
-assert.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext');
-assert.strictEqual(path.posix.basename('basename.ext'), 'basename.ext');
-assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\');
-assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\');
-assert.strictEqual(path.posix.basename('foo'), 'foo');
-
-// POSIX filenames may include control characters
-// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
-const controlCharFilename = `Icon${String.fromCharCode(13)}`;
-assert.strictEqual(path.posix.basename(`/a/b/${controlCharFilename}`),
- controlCharFilename);
-
-
-// path.dirname tests
-assert.strictEqual(path.dirname(f).substr(-13),
- common.isWindows ? 'test\\parallel' : 'test/parallel');
-
-assert.strictEqual(path.posix.dirname('/a/b/'), '/a');
-assert.strictEqual(path.posix.dirname('/a/b'), '/a');
-assert.strictEqual(path.posix.dirname('/a'), '/');
-assert.strictEqual(path.posix.dirname(''), '.');
-assert.strictEqual(path.posix.dirname('/'), '/');
-assert.strictEqual(path.posix.dirname('////'), '/');
-assert.strictEqual(path.posix.dirname('//a'), '//');
-assert.strictEqual(path.posix.dirname('foo'), '.');
-
-assert.strictEqual(path.win32.dirname('c:\\'), 'c:\\');
-assert.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\');
-assert.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\');
-assert.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo');
-assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo');
-assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar');
-assert.strictEqual(path.win32.dirname('\\'), '\\');
-assert.strictEqual(path.win32.dirname('\\foo'), '\\');
-assert.strictEqual(path.win32.dirname('\\foo\\'), '\\');
-assert.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo');
-assert.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo');
-assert.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar');
-assert.strictEqual(path.win32.dirname('c:'), 'c:');
-assert.strictEqual(path.win32.dirname('c:foo'), 'c:');
-assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:');
-assert.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo');
-assert.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo');
-assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar');
-assert.strictEqual(path.win32.dirname('file:stream'), '.');
-assert.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir');
-assert.strictEqual(path.win32.dirname('\\\\unc\\share'),
- '\\\\unc\\share');
-assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'),
- '\\\\unc\\share\\');
-assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'),
- '\\\\unc\\share\\');
-assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'),
- '\\\\unc\\share\\foo');
-assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'),
- '\\\\unc\\share\\foo');
-assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'),
- '\\\\unc\\share\\foo\\bar');
-assert.strictEqual(path.win32.dirname('/a/b/'), '/a');
-assert.strictEqual(path.win32.dirname('/a/b'), '/a');
-assert.strictEqual(path.win32.dirname('/a'), '/');
-assert.strictEqual(path.win32.dirname(''), '.');
-assert.strictEqual(path.win32.dirname('/'), '/');
-assert.strictEqual(path.win32.dirname('////'), '/');
-assert.strictEqual(path.win32.dirname('foo'), '.');
-
-
-// path.extname tests
-[
- [f, '.js'],
- ['', ''],
- ['/path/to/file', ''],
- ['/path/to/file.ext', '.ext'],
- ['/path.to/file.ext', '.ext'],
- ['/path.to/file', ''],
- ['/path.to/.file', ''],
- ['/path.to/.file.ext', '.ext'],
- ['/path/to/f.ext', '.ext'],
- ['/path/to/..ext', '.ext'],
- ['/path/to/..', ''],
- ['file', ''],
- ['file.ext', '.ext'],
- ['.file', ''],
- ['.file.ext', '.ext'],
- ['/file', ''],
- ['/file.ext', '.ext'],
- ['/.file', ''],
- ['/.file.ext', '.ext'],
- ['.path/file.ext', '.ext'],
- ['file.ext.ext', '.ext'],
- ['file.', '.'],
- ['.', ''],
- ['./', ''],
- ['.file.ext', '.ext'],
- ['.file', ''],
- ['.file.', '.'],
- ['.file..', '.'],
- ['..', ''],
- ['../', ''],
- ['..file.ext', '.ext'],
- ['..file', '.file'],
- ['..file.', '.'],
- ['..file..', '.'],
- ['...', '.'],
- ['...ext', '.ext'],
- ['....', '.'],
- ['file.ext/', '.ext'],
- ['file.ext//', '.ext'],
- ['file/', ''],
- ['file//', ''],
- ['file./', '.'],
- ['file.//', '.'],
-].forEach((test) => {
- const expected = test[1];
- [path.posix.extname, path.win32.extname].forEach((extname) => {
- let input = test[0];
- let os;
- if (extname === path.win32.extname) {
- input = input.replace(slashRE, '\\');
- os = 'win32';
- } else {
- os = 'posix';
- }
- const actual = extname(input);
- const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${
- JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
- if (actual !== expected)
- failures.push(`\n${message}`);
- });
- {
- const input = `C:${test[0].replace(slashRE, '\\')}`;
- const actual = path.win32.extname(input);
- const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${
- JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
- if (actual !== expected)
- failures.push(`\n${message}`);
- }
-});
-assert.strictEqual(failures.length, 0, failures.join(''));
-
-// On Windows, backslash is a path separator.
-assert.strictEqual(path.win32.extname('.\\'), '');
-assert.strictEqual(path.win32.extname('..\\'), '');
-assert.strictEqual(path.win32.extname('file.ext\\'), '.ext');
-assert.strictEqual(path.win32.extname('file.ext\\\\'), '.ext');
-assert.strictEqual(path.win32.extname('file\\'), '');
-assert.strictEqual(path.win32.extname('file\\\\'), '');
-assert.strictEqual(path.win32.extname('file.\\'), '.');
-assert.strictEqual(path.win32.extname('file.\\\\'), '.');
-
-// On *nix, backslash is a valid name component like any other character.
-assert.strictEqual(path.posix.extname('.\\'), '');
-assert.strictEqual(path.posix.extname('..\\'), '.\\');
-assert.strictEqual(path.posix.extname('file.ext\\'), '.ext\\');
-assert.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\');
-assert.strictEqual(path.posix.extname('file\\'), '');
-assert.strictEqual(path.posix.extname('file\\\\'), '');
-assert.strictEqual(path.posix.extname('file.\\'), '.\\');
-assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\');
-
-
-// path.join tests
-const joinTests = [
- [ [path.posix.join, path.win32.join],
- // arguments result
- [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'],
- [[], '.'],
- [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'],
- [['/foo', '../../../bar'], '/bar'],
- [['foo', '../../../bar'], '../../bar'],
- [['foo/', '../../../bar'], '../../bar'],
- [['foo/x', '../../../bar'], '../bar'],
- [['foo/x', './bar'], 'foo/x/bar'],
- [['foo/x/', './bar'], 'foo/x/bar'],
- [['foo/x/', '.', 'bar'], 'foo/x/bar'],
- [['./'], './'],
- [['.', './'], './'],
- [['.', '.', '.'], '.'],
- [['.', './', '.'], '.'],
- [['.', '/./', '.'], '.'],
- [['.', '/////./', '.'], '.'],
- [['.'], '.'],
- [['', '.'], '.'],
- [['', 'foo'], 'foo'],
- [['foo', '/bar'], 'foo/bar'],
- [['', '/foo'], '/foo'],
- [['', '', '/foo'], '/foo'],
- [['', '', 'foo'], 'foo'],
- [['foo', ''], 'foo'],
- [['foo/', ''], 'foo/'],
- [['foo', '', '/bar'], 'foo/bar'],
- [['./', '..', '/foo'], '../foo'],
- [['./', '..', '..', '/foo'], '../../foo'],
- [['.', '..', '..', '/foo'], '../../foo'],
- [['', '..', '..', '/foo'], '../../foo'],
- [['/'], '/'],
- [['/', '.'], '/'],
- [['/', '..'], '/'],
- [['/', '..', '..'], '/'],
- [[''], '.'],
- [['', ''], '.'],
- [[' /foo'], ' /foo'],
- [[' ', 'foo'], ' /foo'],
- [[' ', '.'], ' '],
- [[' ', '/'], ' /'],
- [[' ', ''], ' '],
- [['/', 'foo'], '/foo'],
- [['/', '/foo'], '/foo'],
- [['/', '//foo'], '/foo'],
- [['/', '', '/foo'], '/foo'],
- [['', '/', 'foo'], '/foo'],
- [['', '/', '/foo'], '/foo']
- ]
- ]
-];
-
-// Windows-specific join tests
-joinTests.push([
- path.win32.join,
- joinTests[0][1].slice(0).concat(
- [// arguments result
- // UNC path expected
- [['//foo/bar'], '\\\\foo\\bar\\'],
- [['\\/foo/bar'], '\\\\foo\\bar\\'],
- [['\\\\foo/bar'], '\\\\foo\\bar\\'],
- // UNC path expected - server and share separate
- [['//foo', 'bar'], '\\\\foo\\bar\\'],
- [['//foo/', 'bar'], '\\\\foo\\bar\\'],
- [['//foo', '/bar'], '\\\\foo\\bar\\'],
- // UNC path expected - questionable
- [['//foo', '', 'bar'], '\\\\foo\\bar\\'],
- [['//foo/', '', 'bar'], '\\\\foo\\bar\\'],
- [['//foo/', '', '/bar'], '\\\\foo\\bar\\'],
- // UNC path expected - even more questionable
- [['', '//foo', 'bar'], '\\\\foo\\bar\\'],
- [['', '//foo/', 'bar'], '\\\\foo\\bar\\'],
- [['', '//foo/', '/bar'], '\\\\foo\\bar\\'],
- // No UNC path expected (no double slash in first component)
- [['\\', 'foo/bar'], '\\foo\\bar'],
- [['\\', '/foo/bar'], '\\foo\\bar'],
- [['', '/', '/foo/bar'], '\\foo\\bar'],
- // No UNC path expected (no non-slashes in first component -
- // questionable)
- [['//', 'foo/bar'], '\\foo\\bar'],
- [['//', '/foo/bar'], '\\foo\\bar'],
- [['\\\\', '/', '/foo/bar'], '\\foo\\bar'],
- [['//'], '/'],
- // No UNC path expected (share name missing - questionable).
- [['//foo'], '\\foo'],
- [['//foo/'], '\\foo\\'],
- [['//foo', '/'], '\\foo\\'],
- [['//foo', '', '/'], '\\foo\\'],
- // No UNC path expected (too many leading slashes - questionable)
- [['///foo/bar'], '\\foo\\bar'],
- [['////foo', 'bar'], '\\foo\\bar'],
- [['\\\\\\/foo/bar'], '\\foo\\bar'],
- // Drive-relative vs drive-absolute paths. This merely describes the
- // status quo, rather than being obviously right
- [['c:'], 'c:.'],
- [['c:.'], 'c:.'],
- [['c:', ''], 'c:.'],
- [['', 'c:'], 'c:.'],
- [['c:.', '/'], 'c:.\\'],
- [['c:.', 'file'], 'c:file'],
- [['c:', '/'], 'c:\\'],
- [['c:', 'file'], 'c:\\file']
- ]
- )
-]);
-joinTests.forEach((test) => {
- if (!Array.isArray(test[0]))
- test[0] = [test[0]];
- test[0].forEach((join) => {
- test[1].forEach((test) => {
- const actual = join.apply(null, test[0]);
- const expected = test[1];
- // For non-Windows specific tests with the Windows join(), we need to try
- // replacing the slashes since the non-Windows specific tests' `expected`
- // use forward slashes
- let actualAlt;
- let os;
- if (join === path.win32.join) {
- actualAlt = actual.replace(backslashRE, '/');
- os = 'win32';
- } else {
- os = 'posix';
- }
- const message =
- `path.${os}.join(${test[0].map(JSON.stringify).join(',')})\n expect=${
- JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
- if (actual !== expected && actualAlt !== expected)
- failures.push(`\n${message}`);
- });
- });
-});
-assert.strictEqual(failures.length, 0, failures.join(''));
-
-
// Test thrown TypeErrors
const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN];
@@ -394,186 +34,6 @@ typeErrorTests.forEach((test) => {
});
});
-
-// path.normalize tests
-assert.strictEqual(path.win32.normalize('./fixtures///b/../b/c.js'),
- 'fixtures\\b\\c.js');
-assert.strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar');
-assert.strictEqual(path.win32.normalize('a//b//../b'), 'a\\b');
-assert.strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c');
-assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b');
-assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'),
- '\\\\server\\share\\dir\\file.ext');
-assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z');
-assert.strictEqual(path.win32.normalize('C:'), 'C:.');
-assert.strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc');
-assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'),
- 'C:..\\..\\def');
-assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\');
-assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream');
-
-assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'),
- 'fixtures/b/c.js');
-assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar');
-assert.strictEqual(path.posix.normalize('a//b//../b'), 'a/b');
-assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c');
-assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b');
-assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z');
-assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar');
-
-
-// path.resolve tests
-const resolveTests = [
- [ path.win32.resolve,
- // arguments result
- [[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'],
- [['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'],
- [['c:/ignore', 'c:/some/file'], 'c:\\some\\file'],
- [['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'],
- [['.'], process.cwd()],
- [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'],
- [['c:/', '//'], 'c:\\'],
- [['c:/', '//dir'], 'c:\\dir'],
- [['c:/', '//server/share'], '\\\\server\\share\\'],
- [['c:/', '//server//share'], '\\\\server\\share\\'],
- [['c:/', '///some//dir'], 'c:\\some\\dir'],
- [['C:\\foo\\tmp.3\\', '..\\tmp.3\\cycles\\root.js'],
- 'C:\\foo\\tmp.3\\cycles\\root.js']
- ]
- ],
- [ path.posix.resolve,
- // arguments result
- [[['/var/lib', '../', 'file/'], '/var/file'],
- [['/var/lib', '/../', 'file/'], '/file'],
- [['a/b/c/', '../../..'], process.cwd()],
- [['.'], process.cwd()],
- [['/some/dir', '.', '/absolute/'], '/absolute'],
- [['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js']
- ]
- ]
-];
-resolveTests.forEach((test) => {
- const resolve = test[0];
- test[1].forEach((test) => {
- const actual = resolve.apply(null, test[0]);
- let actualAlt;
- const os = resolve === path.win32.resolve ? 'win32' : 'posix';
- if (resolve === path.win32.resolve && !common.isWindows)
- actualAlt = actual.replace(backslashRE, '/');
- else if (resolve !== path.win32.resolve && common.isWindows)
- actualAlt = actual.replace(slashRE, '\\');
-
- const expected = test[1];
- const message =
- `path.${os}.resolve(${test[0].map(JSON.stringify).join(',')})\n expect=${
- JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
- if (actual !== expected && actualAlt !== expected)
- failures.push(`\n${message}`);
- });
-});
-assert.strictEqual(failures.length, 0, failures.join(''));
-
-if (common.isWindows) {
- // Test resolving the current Windows drive letter from a spawned process.
- // See https://github.com/nodejs/node/issues/7215
- const currentDriveLetter = path.parse(process.cwd()).root.substring(0, 2);
- const resolveFixture = path.join(common.fixturesDir, 'path-resolve.js');
- const spawnResult = child.spawnSync(
- process.argv[0], [resolveFixture, currentDriveLetter]);
- const resolvedPath = spawnResult.stdout.toString().trim();
- assert.strictEqual(resolvedPath.toLowerCase(), process.cwd().toLowerCase());
-}
-
-
-// path.isAbsolute tests
-assert.strictEqual(path.win32.isAbsolute('/'), true);
-assert.strictEqual(path.win32.isAbsolute('//'), true);
-assert.strictEqual(path.win32.isAbsolute('//server'), true);
-assert.strictEqual(path.win32.isAbsolute('//server/file'), true);
-assert.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true);
-assert.strictEqual(path.win32.isAbsolute('\\\\server'), true);
-assert.strictEqual(path.win32.isAbsolute('\\\\'), true);
-assert.strictEqual(path.win32.isAbsolute('c'), false);
-assert.strictEqual(path.win32.isAbsolute('c:'), false);
-assert.strictEqual(path.win32.isAbsolute('c:\\'), true);
-assert.strictEqual(path.win32.isAbsolute('c:/'), true);
-assert.strictEqual(path.win32.isAbsolute('c://'), true);
-assert.strictEqual(path.win32.isAbsolute('C:/Users/'), true);
-assert.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true);
-assert.strictEqual(path.win32.isAbsolute('C:cwd/another'), false);
-assert.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false);
-assert.strictEqual(path.win32.isAbsolute('directory/directory'), false);
-assert.strictEqual(path.win32.isAbsolute('directory\\directory'), false);
-
-assert.strictEqual(path.posix.isAbsolute('/home/foo'), true);
-assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true);
-assert.strictEqual(path.posix.isAbsolute('bar/'), false);
-assert.strictEqual(path.posix.isAbsolute('./baz'), false);
-
-
-// path.relative tests
-const relativeTests = [
- [ path.win32.relative,
- // arguments result
- [['c:/blah\\blah', 'd:/games', 'd:\\games'],
- ['c:/aaaa/bbbb', 'c:/aaaa', '..'],
- ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'],
- ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''],
- ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'],
- ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'],
- ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'],
- ['c:/aaaa/bbbb', 'd:\\', 'd:\\'],
- ['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''],
- ['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'],
- ['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'],
- ['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'],
- ['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'],
- ['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'],
- ['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'],
- ['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'],
- ['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'],
- ['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'],
- ['C:\\baz-quux', 'C:\\baz', '..\\baz'],
- ['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'],
- ['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'],
- ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'],
- ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'],
- ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz']
- ]
- ],
- [ path.posix.relative,
- // arguments result
- [['/var/lib', '/var', '..'],
- ['/var/lib', '/bin', '../../bin'],
- ['/var/lib', '/var/lib', ''],
- ['/var/lib', '/var/apache', '../apache'],
- ['/var/', '/var/lib', 'lib'],
- ['/', '/var/lib', 'var/lib'],
- ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'],
- ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'],
- ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'],
- ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'],
- ['/baz-quux', '/baz', '../baz'],
- ['/baz', '/baz-quux', '../baz-quux']
- ]
- ]
-];
-relativeTests.forEach((test) => {
- const relative = test[0];
- test[1].forEach((test) => {
- const actual = relative(test[0], test[1]);
- const expected = test[2];
- const os = relative === path.win32.relative ? 'win32' : 'posix';
- const message = `path.${os}.relative(${
- test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${
- JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
- if (actual !== expected)
- failures.push(`\n${message}`);
- });
-});
-assert.strictEqual(failures.length, 0, failures.join(''));
-
-
// path.sep tests
// windows
assert.strictEqual(path.win32.sep, '\\');
@@ -586,43 +46,6 @@ assert.strictEqual(path.win32.delimiter, ';');
// posix
assert.strictEqual(path.posix.delimiter, ':');
-
-// path._makeLong tests
-const emptyObj = {};
-assert.strictEqual(path.posix._makeLong('/foo/bar'), '/foo/bar');
-assert.strictEqual(path.posix._makeLong('foo/bar'), 'foo/bar');
-assert.strictEqual(path.posix._makeLong(null), null);
-assert.strictEqual(path.posix._makeLong(true), true);
-assert.strictEqual(path.posix._makeLong(1), 1);
-assert.strictEqual(path.posix._makeLong(), undefined);
-assert.strictEqual(path.posix._makeLong(emptyObj), emptyObj);
-if (common.isWindows) {
- // These tests cause resolve() to insert the cwd, so we cannot test them from
- // non-Windows platforms (easily)
- assert.strictEqual(path.win32._makeLong('foo\\bar').toLowerCase(),
- `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`);
- assert.strictEqual(path.win32._makeLong('foo/bar').toLowerCase(),
- `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`);
- const currentDeviceLetter = path.parse(process.cwd()).root.substring(0, 2);
- assert.strictEqual(path.win32._makeLong(currentDeviceLetter).toLowerCase(),
- `\\\\?\\${process.cwd().toLowerCase()}`);
- assert.strictEqual(path.win32._makeLong('C').toLowerCase(),
- `\\\\?\\${process.cwd().toLowerCase()}\\c`);
-}
-assert.strictEqual(path.win32._makeLong('C:\\foo'), '\\\\?\\C:\\foo');
-assert.strictEqual(path.win32._makeLong('C:/foo'), '\\\\?\\C:\\foo');
-assert.strictEqual(path.win32._makeLong('\\\\foo\\bar'),
- '\\\\?\\UNC\\foo\\bar\\');
-assert.strictEqual(path.win32._makeLong('//foo//bar'),
- '\\\\?\\UNC\\foo\\bar\\');
-assert.strictEqual(path.win32._makeLong('\\\\?\\foo'), '\\\\?\\foo');
-assert.strictEqual(path.win32._makeLong(null), null);
-assert.strictEqual(path.win32._makeLong(true), true);
-assert.strictEqual(path.win32._makeLong(1), 1);
-assert.strictEqual(path.win32._makeLong(), undefined);
-assert.strictEqual(path.win32._makeLong(emptyObj), emptyObj);
-
-
if (common.isWindows)
assert.deepStrictEqual(path, path.win32, 'should be win32 path module');
else