Skip to content

Commit 3cff7f8

Browse files
Aditi-1400aduh95
authored andcommitted
src: suggest --use-system-ca when a certificate error occurs
PR-URL: #57362 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent 1f7b08a commit 3cff7f8

7 files changed

Lines changed: 34 additions & 17 deletions

‎doc/api/tls.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,12 @@ description are taken from deps/openssl/openssl/crypto/x509/x509_txt.c
547547
*`'CERT_REJECTED'`: Certificate rejected.
548548
*`'HOSTNAME_MISMATCH'`: Hostname mismatch.
549549

550+
When certificate errors like `UNABLE_TO_VERIFY_LEAF_SIGNATURE`,
551+
`DEPTH_ZERO_SELF_SIGNED_CERT`, or `UNABLE_TO_GET_ISSUER_CERT` occur, Node.js
552+
appends a hint suggesting that if the root CA is installed locally,
553+
try running with the `--use-system-ca` flag to direct developers towards a
554+
secure solution, to prevent unsafe workarounds.
555+
550556
## Class: `tls.CryptoStream`
551557

552558
<!-- YAML

‎src/crypto/crypto_common.cc‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,20 @@ SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length) {
5353
}
5454

5555
MaybeLocal<Value> GetValidationErrorReason(Environment* env, int err) {
56-
auto reason = X509Pointer::ErrorReason(err).value_or("");
56+
auto reason = std::string(X509Pointer::ErrorReason(err).value_or(""));
5757
if (reason == "") returnUndefined(env->isolate());
58+
59+
// Suggest --use-system-ca if the error indicates a certificate issue
60+
bool suggest_system_ca =
61+
(err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) ||
62+
(err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ||
63+
((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) &&
64+
!per_process::cli_options->use_system_ca);
65+
66+
if (suggest_system_ca) {
67+
reason.append("; if the root CA is installed locally, "
68+
"try running Node.js with --use-system-ca");
69+
}
5870
returnOneByteString(env->isolate(), reason);
5971
}
6072

‎test/parallel/test-https-agent-create-connection.js‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const options = {
1717

1818
constexpectedHeader=/^HTTP\/1\.1200OK/;
1919
constexpectedBody=/helloworld\n/;
20-
constexpectCertError=/^Error:unabletoverifythefirstcertificate$/;
20+
constexpectCertError=/^UNABLE_TO_VERIFY_LEAF_SIGNATURE$/;
2121

2222
constcheckRequest=(socket,server)=>{
2323
letresult='';
@@ -112,7 +112,7 @@ function createServer() {
112112
constoptions=null;
113113
constsocket=agent.createConnection(port,host,options);
114114
socket.on('error',common.mustCall((e)=>{
115-
assert.match(e.toString(),expectCertError);
115+
assert.match(e.code,expectCertError);
116116
server.close();
117117
}));
118118
}));
@@ -127,7 +127,7 @@ function createServer() {
127127
constoptions=undefined;
128128
constsocket=agent.createConnection(port,host,options);
129129
socket.on('error',common.mustCall((e)=>{
130-
assert.match(e.toString(),expectCertError);
130+
assert.match(e.code,expectCertError);
131131
server.close();
132132
}));
133133
}));

‎test/parallel/test-tls-addca.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ connect({
3434
server: serverOptions,
3535
},common.mustCall((err,pair,cleanup)=>{
3636
assert(err);
37-
assert.strictEqual(err.message,'unable to verify the first certificate');
37+
assert.strictEqual(err.code,'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
3838
cleanup();
3939

4040
// This time it should connect because contextWithCert includes the needed CA

‎test/parallel/test-tls-friendly-error-message.js‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,5 @@ tls.createServer({ key, cert }).on('connection', common.mustCall(function() {
4040
constoptions={port: this.address().port,rejectUnauthorized: true};
4141
tls.connect(options).on('error',common.mustCall(function(err){
4242
assert.strictEqual(err.code,'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
43-
assert.strictEqual(err.message,'unable to verify the first certificate');
4443
}));
4544
}));

‎test/parallel/test-tls-set-secure-context.js‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const assert = require('assert');
1313
constevents=require('events');
1414
consthttps=require('https');
1515
consttimers=require('timers/promises');
16-
const{ hasOpenSSL3 }=require('../common/crypto');
1716
constfixtures=require('../common/fixtures');
1817
constcredentialOptions=[
1918
{
@@ -57,17 +56,18 @@ server.listen(0, common.mustCall(() => {
5756

5857
server.setSecureContext(credentialOptions[1]);
5958
firstResponse.write('request-');
60-
consterrorMessageRegex=hasOpenSSL3 ?
61-
/^Error:self-signedcertificate$/ :
62-
/^Error:selfsignedcertificate$/;
63-
awaitassert.rejects(makeRequest(port,3),errorMessageRegex);
59+
awaitassert.rejects(makeRequest(port,3),{
60+
code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
61+
});
6462

6563
server.setSecureContext(credentialOptions[0]);
6664
assert.strictEqual(awaitmakeRequest(port,4),'success');
6765

6866
server.setSecureContext(credentialOptions[1]);
6967
firstResponse.end('fun!');
70-
awaitassert.rejects(makeRequest(port,5),errorMessageRegex);
68+
awaitassert.rejects(makeRequest(port,5),{
69+
code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
70+
});
7171

7272
assert.strictEqual(awaitfirstRequest,'multi-request-success-fun!');
7373
server.close();

‎test/parallel/test-tls-socket-default-options.js‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ const {
1010
}=require(fixtures.path('tls-connect'));
1111

1212
test(undefined,(err)=>{
13-
assert.strictEqual(err.message,'unable to verify the first certificate');
13+
assert.strictEqual(err.code,'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
1414
});
1515

1616
test({},(err)=>{
17-
assert.strictEqual(err.message,'unable to verify the first certificate');
17+
assert.strictEqual(err.code,'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
1818
});
1919

2020
test(
@@ -30,8 +30,8 @@ test(
3030
test(
3131
{secureContext: tls.createSecureContext(),ca: keys.agent1.ca},
3232
(err)=>{
33-
assert.strictEqual(err.message,
34-
'unable to verify the first certificate');
33+
assert.strictEqual(err.code,
34+
'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
3535
});
3636

3737
functiontest(client,callback){
@@ -42,7 +42,7 @@ function test(client, callback) {
4242
cert: keys.agent1.cert,
4343
},
4444
},function(err,pair,cleanup){
45-
assert.strictEqual(err.message,'unable to verify the first certificate');
45+
assert.strictEqual(err.code,'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
4646
letrecv='';
4747
pair.server.server.once('secureConnection',common.mustCall((conn)=>{
4848
conn.on('data',(data)=>recv+=data);

0 commit comments

Comments
 (0)