Skip to content

Commit 20c702a

Browse files
author
Zhen Li
authored
Merge pull request #113 from legraphista/fix/known_hosts-fingerprint-duplication-error/1.0
Fix: regarding PR #110 edge cases
2 parents 4d19e69 + 732fc33 commit 20c702a

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

src/v1/internal/ch-node.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,27 @@ function loadFingerprint( serverId, knownHostsPath, cb ) {
7575
});
7676
}
7777

78-
function storeFingerprint(serverId, knownHostsPath, fingerprint) {
78+
const _lockFingerprintFromAppending = {};
79+
function storeFingerprint( serverId, knownHostsPath, fingerprint, cb ) {
80+
// we check if the serverId has been appended
81+
if(!!_lockFingerprintFromAppending[serverId]){
82+
// if it has, we ignore it
83+
return cb(null);
84+
}
85+
7986
// If file doesn't exist, create full path to it
8087
try {
8188
fs.accessSync(knownHostsPath);
8289
} catch (_) {
8390
mkFullPath(path.dirname(knownHostsPath));
8491
}
92+
8593
fs.appendFile(knownHostsPath, serverId + " " + fingerprint + EOL, "utf8", (err) => {
94+
delete _lockFingerprintFromAppending[serverId];
8695
if (err) {
8796
console.log(err);
8897
}
98+
return cb(err);
8999
});
90100
}
91101

@@ -152,8 +162,12 @@ const TrustStrategy = {
152162
if( knownFingerprint === serverFingerprint ) {
153163
onSuccess();
154164
} else if( knownFingerprint == null ) {
155-
storeFingerprint( serverId, knownHostsPath, serverFingerprint );
156-
onSuccess();
165+
storeFingerprint( serverId, knownHostsPath, serverFingerprint, (err) => {
166+
if (err) {
167+
return onFailure(err);
168+
}
169+
return onSuccess();
170+
});
157171
} else {
158172
onFailure(newError("Database encryption certificate has changed, and no longer " +
159173
"matches the certificate stored for " + serverId + " in `" + knownHostsPath +

test/internal/tls.test.js

+54
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,60 @@ describe('trust-on-first-use', function() {
180180
done();
181181
});
182182
});
183+
184+
it('should not duplicate fingerprint entries', function(done) {
185+
// Assuming we only run this test on NodeJS with TOFU support
186+
if( !hasFeature("trust_on_first_use") ) {
187+
done();
188+
return;
189+
}
190+
191+
// Given
192+
var knownHostsPath = "build/known_hosts";
193+
if( fs.existsSync(knownHostsPath) ) {
194+
fs.unlinkSync(knownHostsPath);
195+
}
196+
fs.writeFileSync(knownHostsPath, '');
197+
198+
driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), {
199+
encrypted: true,
200+
trust: "TRUST_ON_FIRST_USE",
201+
knownHosts: knownHostsPath
202+
});
203+
204+
// When
205+
driver.session();
206+
driver.session();
207+
208+
// Then
209+
setTimeout(function() {
210+
var lines = {};
211+
fs.readFileSync(knownHostsPath, 'utf8')
212+
.split('\n')
213+
.filter(function(line) {
214+
return !! (line.trim());
215+
})
216+
.forEach(function(line) {
217+
if (!lines[line]) {
218+
lines[line] = 0;
219+
}
220+
lines[line]++;
221+
});
222+
223+
var duplicatedLines = Object
224+
.keys(lines)
225+
.map(function(line) {
226+
return lines[line];
227+
})
228+
.filter(function(count) {
229+
return count > 1;
230+
})
231+
.length;
232+
233+
expect( duplicatedLines ).toBe( 0 );
234+
done();
235+
}, 1000);
236+
});
183237

184238
it('should should give helpful error if database cert does not match stored certificate', function(done) {
185239
// Assuming we only run this test on NodeJS with TOFU support

0 commit comments

Comments
 (0)