-
Notifications
You must be signed in to change notification settings - Fork 53
MLE-30259: [Node Client] Replace rejectUnauthorized:false With Proper CA Certificates in Test Configs #1100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| /* | ||
| * Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. | ||
| * | ||
| * Regression guard: verify that no SSL-capable connection config disables TLS certificate | ||
| * verification via rejectUnauthorized: false, and that each uses the CA extracted from | ||
| * MarkLogic by the Gradle generateAndInstallSslCertificate task. | ||
| * | ||
| * Two tiers of checks: | ||
| * 1. rejectUnauthorized: false guard -- always runs, no MarkLogic required. | ||
| * 2. CA Buffer presence check -- runs only after mlDeploy has been executed | ||
| * (self-signed-ca.pem exists). Tests are skipped with a clear message otherwise. | ||
| */ | ||
|
|
||
| "use strict"; | ||
|
|
||
| var fs = require("fs"); | ||
| var path = require("path"); | ||
| var should = require("should"); | ||
| var marklogic = require("../"); | ||
| var testlib = require("../etc/test-lib"); | ||
|
|
||
| // Path where generateAndInstallSslCertificate writes the CA PEM after mlDeploy | ||
| var CA_PATH = path.join(__dirname, "../test-app/src/main/ml-config/self-signed-ca.pem"); | ||
| var caDeployed = fs.existsSync(CA_PATH); | ||
|
|
||
| // All three test-config files that previously contained rejectUnauthorized: false | ||
| var configs = [ | ||
| { label: "test-config.js", module: require("../etc/test-config.js") }, | ||
| { label: "test-config-qa-ssl.js", module: require("../etc/test-config-qa-ssl.js") }, | ||
| { label: "test-config-qa.js", module: require("../etc/test-config-qa.js") } | ||
| ]; | ||
| var mainTestConfig = configs[0].module; | ||
|
|
||
| // SSL-enabled connection objects expected in each config | ||
| var sslConnectionKeys = { | ||
| "test-config.js": ["restSslConnection", "restConnectionForTls"], | ||
| "test-config-qa-ssl.js": ["restSslConnection"], | ||
| "test-config-qa.js": ["restSslConnection"] | ||
| }; | ||
|
|
||
| // Non-SSL connections that previously carried the stray property | ||
| var nonSslConnectionKeys = { | ||
| "test-config.js": ["testConnection", "tdeConnection"] | ||
| }; | ||
|
|
||
| function looksLikeTlsVerifyError(message) { | ||
| if (!message) { | ||
| return false; | ||
| } | ||
| return /self signed|unable to verify|certificate verify failed|DEPTH_ZERO_SELF_SIGNED_CERT|SELF_SIGNED_CERT_IN_CHAIN|UNABLE_TO_VERIFY_LEAF_SIGNATURE|SSL routines/i.test(message); | ||
| } | ||
|
|
||
| describe("SSL connection config security guard", function () { | ||
|
|
||
| configs.forEach(function(cfgEntry) { | ||
| var label = cfgEntry.label; | ||
| var cfg = cfgEntry.module; | ||
|
|
||
| describe(label, function () { | ||
|
|
||
| var sslKeys = sslConnectionKeys[label] || []; | ||
| sslKeys.forEach(function (key) { | ||
|
|
||
| // Tier 1: always enforce -- no MarkLogic needed | ||
| it(key + " must not set rejectUnauthorized: false", function () { | ||
| var conn = cfg[key]; | ||
| should.exist(conn, key + " should exist in " + label); | ||
| should(conn.rejectUnauthorized).not.equal(false, | ||
| key + ".rejectUnauthorized must not be false -- use a ca bundle instead"); | ||
| }); | ||
|
|
||
| // Tier 2: CA presence -- only meaningful after mlDeploy has run | ||
| it(key + " must provide a ca certificate Buffer (requires mlDeploy)", function () { | ||
| if (!caDeployed) { | ||
| return this.skip( | ||
| "self-signed-ca.pem not found -- run gradle mlDeploy first to " + | ||
| "extract the CA certificate from MarkLogic"); | ||
| } | ||
| var conn = cfg[key]; | ||
| should.exist(conn, key + " should exist in " + label); | ||
| should.exist(conn.ca, | ||
| key + ".ca must be set -- self-signed-ca.pem exists but ca is undefined"); | ||
| Buffer.isBuffer(conn.ca).should.be.true( | ||
| key + ".ca should be a Buffer loaded from the PEM file"); | ||
| conn.ca.length.should.be.greaterThan(0, key + ".ca buffer must not be empty"); | ||
| conn.ca.toString("utf8").should.containEql("-----BEGIN CERTIFICATE-----"); | ||
| }); | ||
| }); | ||
|
|
||
| // Non-SSL connections must not carry the stray property | ||
| var nonSslKeys = nonSslConnectionKeys[label] || []; | ||
| nonSslKeys.forEach(function (key) { | ||
| it(key + " (non-SSL) must not set rejectUnauthorized: false", function () { | ||
| var conn = cfg[key]; | ||
| should.exist(conn, key + " should exist in " + label); | ||
| should(conn.rejectUnauthorized).not.equal(false, | ||
| key + ".rejectUnauthorized: false has no effect on non-SSL connections -- remove it"); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| // CA file sanity check -- skipped pre-deploy, validates post-deploy | ||
| describe("self-signed-ca.pem (generated by generateAndInstallSslCertificate task)", function () { | ||
|
|
||
| it("CA file exists at the well-known path after mlDeploy", function () { | ||
| if (!caDeployed) { | ||
| return this.skip("Run gradle mlDeploy to generate self-signed-ca.pem"); | ||
| } | ||
| fs.existsSync(CA_PATH).should.be.true("CA file not found at " + CA_PATH); | ||
| }); | ||
|
|
||
| it("CA file contains a valid PEM certificate", function () { | ||
| if (!caDeployed) { | ||
| return this.skip("Run gradle mlDeploy to generate self-signed-ca.pem"); | ||
| } | ||
| var content = fs.readFileSync(CA_PATH, "utf8"); | ||
| content.should.containEql("-----BEGIN CERTIFICATE-----"); | ||
| content.should.containEql("-----END CERTIFICATE-----"); | ||
| }); | ||
| }); | ||
|
|
||
| // Live-server integration check: verification must fail when CA is not provided. | ||
| describe("SSL certificate enforcement (negative)", function () { | ||
| this.timeout(15000); | ||
|
|
||
| var serverConfiguration = {}; | ||
|
|
||
| before(function (done) { | ||
| testlib.findServerConfiguration(serverConfiguration); | ||
| setTimeout(function () { | ||
| done(); | ||
| }, 3000); | ||
| }); | ||
|
|
||
| it("fails TLS handshake when CA is intentionally omitted", function (done) { | ||
| if (serverConfiguration.serverVersion < 12) { | ||
| return this.skip(); | ||
| } | ||
| if (!caDeployed || !mainTestConfig.restConnectionForTls || !Buffer.isBuffer(mainTestConfig.restConnectionForTls.ca)) { | ||
| return this.skip( | ||
| "CA not deployed/loaded -- run gradle mlDeploy first so restConnectionForTls includes a CA before running this integration check"); | ||
| } | ||
|
|
||
| // Clone the known-good TLS connection and intentionally remove CA trust. | ||
| var insecureConn = Object.assign({}, mainTestConfig.restConnectionForTls); | ||
| delete insecureConn.ca; | ||
| var dbNoCa = marklogic.createDatabaseClient(insecureConn); | ||
|
|
||
| dbNoCa.documents.read({ uris: "/does-not-matter-for-tls-check.json" }) | ||
| .result(function () { | ||
| done(new Error("Expected TLS handshake failure when CA is omitted, but request succeeded")); | ||
| }) | ||
| .catch(function (error) { | ||
| should.exist(error); | ||
| should.exist(error.message); | ||
| looksLikeTlsVerifyError(error.message).should.be.true( | ||
| "Expected TLS/certificate verification failure, got: " + error.message | ||
| ); | ||
| done(); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.