Skip to content

Commit 58ebbe5

Browse files
authored
Merge pull request #320 from configurator/combined-hashes-tests
Prevent hash collisions
2 parents 885cbab + be2ae05 commit 58ebbe5

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

src/Documents/Queries/HashCalculator.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ import * as md5 from "md5";
22
import { TypeUtil } from "../../Utility/TypeUtil";
33
import { TypesAwareObjectMapper } from "../../Mapping/ObjectMapper";
44

5+
const typeSignatures = {
6+
bigint: Buffer.from([1]),
7+
boolean: Buffer.from([2]),
8+
function: Buffer.from([3]),
9+
number: Buffer.from([4]),
10+
object: Buffer.from([5]),
11+
string: Buffer.from([6]),
12+
symbol: Buffer.from([7]),
13+
undefined: Buffer.from([8]),
14+
};
15+
516
export class HashCalculator {
617

718
private _buffers: Buffer[] = [];
@@ -17,9 +28,12 @@ export class HashCalculator {
1728
this._buffers.push(Buffer.from("null"));
1829
return;
1930
}
31+
32+
// Push a byte that identifies the type, to differentiate strings, numbers, and bools
33+
this._buffers.push(typeSignatures[typeof o] || typeSignatures.undefined);
2034

2135
if (typeof o === "number") {
22-
this._buffers.push(Buffer.from([o]));
36+
this._buffers.push(Buffer.from(String(o)));
2337
} else if (typeof o === "string") {
2438
this._buffers.push(Buffer.from(o));
2539
} else if (typeof o === "boolean") {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as assert from "assert";
2+
3+
import { HashCalculator } from "../../../src/Documents/Queries/HashCalculator";
4+
import { TypesAwareObjectMapper } from "../../../src/Mapping/ObjectMapper";
5+
6+
const mockObjectMapper = {
7+
toObjectLiteral: obj => obj.toString()
8+
} as TypesAwareObjectMapper;
9+
10+
const hash = data => {
11+
const calculator = new HashCalculator();
12+
calculator.write(data, mockObjectMapper);
13+
return calculator.getHash();
14+
}
15+
16+
describe('Hash calculator tests', () => {
17+
it('Calculates the same hash for the same object', async () => {
18+
const obj = {
19+
boolean: true,
20+
function: () => {
21+
console.log('No-op')
22+
},
23+
number: 4,
24+
object: {
25+
property: 'value'
26+
},
27+
string: 'hello',
28+
symbol: Symbol('world'),
29+
undefined: undefined,
30+
};
31+
32+
assert.equal(hash(obj), hash(obj));
33+
assert.equal(hash(obj), hash({ ...obj }));
34+
});
35+
36+
it('Calculates different hashes for different types', async () => {
37+
assert.notEqual(hash(1), hash(true))
38+
assert.notEqual(hash('1'), hash(true))
39+
assert.notEqual(hash(1), hash('1'))
40+
});
41+
42+
it('Calculates different hashes for different numbers', async () => {
43+
assert.notEqual(hash(1), hash(257));
44+
assert.notEqual(hash(86400), hash(0));
45+
});
46+
});

0 commit comments

Comments
 (0)