From b1aa869bfbde0990fd1c665968e11519ebe048af Mon Sep 17 00:00:00 2001 From: Stefan-Gabriel Muscalu Date: Tue, 26 Jul 2016 17:25:47 +0300 Subject: [PATCH 1/2] Update: reworked `storeFingerprint` to await on `appendFile` and lock the server ID while I/O event, thanks to @swist --- src/v1/internal/ch-node.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/v1/internal/ch-node.js b/src/v1/internal/ch-node.js index d2c75de68..b1252dcaf 100644 --- a/src/v1/internal/ch-node.js +++ b/src/v1/internal/ch-node.js @@ -57,11 +57,11 @@ function loadFingerprint( serverId, knownHostsPath, cb ) { } const _lockFingerprintFromAppending = {}; -function storeFingerprint( serverId, knownHostsPath, fingerprint ) { +function storeFingerprint( serverId, knownHostsPath, fingerprint, cb ) { // we check if the serverId has been appended if(!!_lockFingerprintFromAppending[serverId]){ // if it has, we ignore it - return; + return cb(null); } // we make the line as appended @@ -70,15 +70,11 @@ function storeFingerprint( serverId, knownHostsPath, fingerprint ) { // we append to file fs.appendFile(knownHostsPath, serverId + " " + fingerprint + EOL, "utf8", (err) => { + delete _lockFingerprintFromAppending[serverId]; if (err) { console.log(err); } - }); - - // since the error occurs in the span of one tick - // after one tick we clean up to not interfere with anything else - setImmediate(() => { - delete _lockFingerprintFromAppending[serverId]; + return cb(err); }); } @@ -145,8 +141,12 @@ const TrustStrategy = { if( knownFingerprint === serverFingerprint ) { onSuccess(); } else if( knownFingerprint == null ) { - storeFingerprint( serverId, knownHostsPath, serverFingerprint ); - onSuccess(); + storeFingerprint( serverId, knownHostsPath, serverFingerprint, (err) => { + if (err) { + return onFailure(err); + } + return onSuccess(); + }); } else { onFailure(newError("Database encryption certificate has changed, and no longer " + "matches the certificate stored for " + serverId + " in `" + knownHostsPath + From 732fc3386ac141e2f3762c4447ecff89208797df Mon Sep 17 00:00:00 2001 From: Stefan-Gabriel Muscalu Date: Thu, 4 Aug 2016 10:16:51 +0300 Subject: [PATCH 2/2] Fix: readded missing `should not duplicate fingerprint entries` test --- test/internal/tls.test.js | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test/internal/tls.test.js b/test/internal/tls.test.js index c333b7074..2d6c1ea8b 100644 --- a/test/internal/tls.test.js +++ b/test/internal/tls.test.js @@ -180,6 +180,60 @@ describe('trust-on-first-use', function() { done(); }); }); + + it('should not duplicate fingerprint entries', function(done) { + // Assuming we only run this test on NodeJS with TOFU support + if( !hasFeature("trust_on_first_use") ) { + done(); + return; + } + + // Given + var knownHostsPath = "build/known_hosts"; + if( fs.existsSync(knownHostsPath) ) { + fs.unlinkSync(knownHostsPath); + } + fs.writeFileSync(knownHostsPath, ''); + + driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), { + encrypted: true, + trust: "TRUST_ON_FIRST_USE", + knownHosts: knownHostsPath + }); + + // When + driver.session(); + driver.session(); + + // Then + setTimeout(function() { + var lines = {}; + fs.readFileSync(knownHostsPath, 'utf8') + .split('\n') + .filter(function(line) { + return !! (line.trim()); + }) + .forEach(function(line) { + if (!lines[line]) { + lines[line] = 0; + } + lines[line]++; + }); + + var duplicatedLines = Object + .keys(lines) + .map(function(line) { + return lines[line]; + }) + .filter(function(count) { + return count > 1; + }) + .length; + + expect( duplicatedLines ).toBe( 0 ); + done(); + }, 1000); + }); it('should should give helpful error if database cert does not match stored certificate', function(done) { // Assuming we only run this test on NodeJS with TOFU support