Timeline: repair oldest/newest metrics if we fetch nothing
This commit is contained in:
parent
56ae4a41eb
commit
6832b8acca
47 changed files with 579 additions and 173 deletions
130
ts/test-node/updater/common_test.ts
Normal file
130
ts/test-node/updater/common_test.ts
Normal file
|
@ -0,0 +1,130 @@
|
|||
// Copyright 2019-2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
import {
|
||||
createTempDir,
|
||||
getUpdateFileName,
|
||||
getVersion,
|
||||
isUpdateFileNameValid,
|
||||
validatePath,
|
||||
} from '../../updater/common';
|
||||
|
||||
describe('updater/signatures', () => {
|
||||
const windows = `version: 1.23.2
|
||||
files:
|
||||
- url: signal-desktop-win-1.23.2.exe
|
||||
sha512: hhK+cVAb+QOK/Ln0RBcq8Rb1iPcUC0KZeT4NwLB25PMGoPmakY27XE1bXq4QlkASJN1EkYTbKf3oUJtcllziyQ==
|
||||
size: 92020776
|
||||
path: signal-desktop-win-1.23.2.exe
|
||||
sha512: hhK+cVAb+QOK/Ln0RBcq8Rb1iPcUC0KZeT4NwLB25PMGoPmakY27XE1bXq4QlkASJN1EkYTbKf3oUJtcllziyQ==
|
||||
releaseDate: '2019-03-29T16:58:08.210Z'
|
||||
`;
|
||||
const mac = `version: 1.23.2
|
||||
files:
|
||||
- url: signal-desktop-mac-1.23.2.zip
|
||||
sha512: f4pPo3WulTVi9zBWGsJPNIlvPOTCxPibPPDmRFDoXMmFm6lqJpXZQ9DSWMJumfc4BRp4y/NTQLGYI6b4WuJwhg==
|
||||
size: 105179791
|
||||
blockMapSize: 111109
|
||||
path: signal-desktop-mac-1.23.2.zip
|
||||
sha512: f4pPo3WulTVi9zBWGsJPNIlvPOTCxPibPPDmRFDoXMmFm6lqJpXZQ9DSWMJumfc4BRp4y/NTQLGYI6b4WuJwhg==
|
||||
releaseDate: '2019-03-29T16:57:16.997Z'
|
||||
`;
|
||||
const windowsBeta = `version: 1.23.2-beta.1
|
||||
files:
|
||||
- url: signal-desktop-beta-win-1.23.2-beta.1.exe
|
||||
sha512: ZHM1F3y/Y6ulP5NhbFuh7t2ZCpY4lD9BeBhPV+g2B/0p/66kp0MJDeVxTgjR49OakwpMAafA1d6y2QBail4hSQ==
|
||||
size: 92028656
|
||||
path: signal-desktop-beta-win-1.23.2-beta.1.exe
|
||||
sha512: ZHM1F3y/Y6ulP5NhbFuh7t2ZCpY4lD9BeBhPV+g2B/0p/66kp0MJDeVxTgjR49OakwpMAafA1d6y2QBail4hSQ==
|
||||
releaseDate: '2019-03-29T01:56:00.544Z'
|
||||
`;
|
||||
const macBeta = `version: 1.23.2-beta.1
|
||||
files:
|
||||
- url: signal-desktop-beta-mac-1.23.2-beta.1.zip
|
||||
sha512: h/01N0DD5Jw2Q6M1n4uLGLTCrMFxcn8QOPtLR3HpABsf3w9b2jFtKb56/2cbuJXP8ol8TkTDWKnRV6mnqnLBDw==
|
||||
size: 105182398
|
||||
blockMapSize: 110894
|
||||
path: signal-desktop-beta-mac-1.23.2-beta.1.zip
|
||||
sha512: h/01N0DD5Jw2Q6M1n4uLGLTCrMFxcn8QOPtLR3HpABsf3w9b2jFtKb56/2cbuJXP8ol8TkTDWKnRV6mnqnLBDw==
|
||||
releaseDate: '2019-03-29T01:53:23.881Z'
|
||||
`;
|
||||
|
||||
describe('#getVersion', () => {
|
||||
it('successfully gets version', () => {
|
||||
const expected = '1.23.2';
|
||||
assert.strictEqual(getVersion(windows), expected);
|
||||
assert.strictEqual(getVersion(mac), expected);
|
||||
|
||||
const expectedBeta = '1.23.2-beta.1';
|
||||
assert.strictEqual(getVersion(windowsBeta), expectedBeta);
|
||||
assert.strictEqual(getVersion(macBeta), expectedBeta);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getUpdateFileName', () => {
|
||||
it('successfully gets version', () => {
|
||||
assert.strictEqual(
|
||||
getUpdateFileName(windows),
|
||||
'signal-desktop-win-1.23.2.exe'
|
||||
);
|
||||
assert.strictEqual(
|
||||
getUpdateFileName(mac),
|
||||
'signal-desktop-mac-1.23.2.zip'
|
||||
);
|
||||
assert.strictEqual(
|
||||
getUpdateFileName(windowsBeta),
|
||||
'signal-desktop-beta-win-1.23.2-beta.1.exe'
|
||||
);
|
||||
assert.strictEqual(
|
||||
getUpdateFileName(macBeta),
|
||||
'signal-desktop-beta-mac-1.23.2-beta.1.zip'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isUpdateFileNameValid', () => {
|
||||
it('returns true for normal filenames', () => {
|
||||
assert.strictEqual(
|
||||
isUpdateFileNameValid('signal-desktop-win-1.23.2.exe'),
|
||||
true
|
||||
);
|
||||
assert.strictEqual(
|
||||
isUpdateFileNameValid('signal-desktop-mac-1.23.2-beta.1.zip'),
|
||||
true
|
||||
);
|
||||
});
|
||||
it('returns false for problematic names', () => {
|
||||
assert.strictEqual(
|
||||
isUpdateFileNameValid('../signal-desktop-win-1.23.2.exe'),
|
||||
false
|
||||
);
|
||||
assert.strictEqual(
|
||||
isUpdateFileNameValid('%signal-desktop-mac-1.23.2-beta.1.zip'),
|
||||
false
|
||||
);
|
||||
assert.strictEqual(
|
||||
isUpdateFileNameValid('@signal-desktop-mac-1.23.2-beta.1.zip'),
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#validatePath', () => {
|
||||
it('succeeds for simple children', async () => {
|
||||
const base = await createTempDir();
|
||||
validatePath(base, `${base}/child`);
|
||||
validatePath(base, `${base}/child/grandchild`);
|
||||
});
|
||||
it('returns false for problematic names', async () => {
|
||||
const base = await createTempDir();
|
||||
assert.throws(() => {
|
||||
validatePath(base, `${base}/../child`);
|
||||
});
|
||||
assert.throws(() => {
|
||||
validatePath(base, '/root');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
17
ts/test-node/updater/curve_test.ts
Normal file
17
ts/test-node/updater/curve_test.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2019-2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { keyPair, sign, verify } from '../../updater/curve';
|
||||
|
||||
describe('updater/curve', () => {
|
||||
it('roundtrips', () => {
|
||||
const message = Buffer.from('message');
|
||||
const { publicKey, privateKey } = keyPair();
|
||||
const signature = sign(privateKey, message);
|
||||
const verified = verify(publicKey, message, signature);
|
||||
|
||||
assert.strictEqual(verified, true);
|
||||
});
|
||||
});
|
209
ts/test-node/updater/signature_test.ts
Normal file
209
ts/test-node/updater/signature_test.ts
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2019-2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { copy } from 'fs-extra';
|
||||
|
||||
import {
|
||||
_getFileHash,
|
||||
getSignaturePath,
|
||||
loadHexFromPath,
|
||||
verifySignature,
|
||||
writeHexToPath,
|
||||
writeSignature,
|
||||
} from '../../updater/signature';
|
||||
import { createTempDir, deleteTempDir } from '../../updater/common';
|
||||
import { keyPair } from '../../updater/curve';
|
||||
|
||||
describe('updater/signatures', () => {
|
||||
it('_getFileHash returns correct hash', async () => {
|
||||
const filePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
|
||||
const expected =
|
||||
'7bc77f27d92d00b4a1d57c480ca86dacc43d57bc318339c92119d1fbf6b557a5';
|
||||
|
||||
const hash = await _getFileHash(filePath);
|
||||
|
||||
assert.strictEqual(expected, Buffer.from(hash).toString('hex'));
|
||||
});
|
||||
|
||||
it('roundtrips binary file writes', async () => {
|
||||
let tempDir;
|
||||
|
||||
try {
|
||||
tempDir = await createTempDir();
|
||||
|
||||
const path = join(tempDir, 'something.bin');
|
||||
const { publicKey } = keyPair();
|
||||
|
||||
await writeHexToPath(path, publicKey);
|
||||
|
||||
const fromDisk = await loadHexFromPath(path);
|
||||
|
||||
assert.strictEqual(
|
||||
Buffer.from(fromDisk).compare(Buffer.from(publicKey)),
|
||||
0
|
||||
);
|
||||
} finally {
|
||||
if (tempDir) {
|
||||
await deleteTempDir(tempDir);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('roundtrips signature', async () => {
|
||||
let tempDir;
|
||||
|
||||
try {
|
||||
tempDir = await createTempDir();
|
||||
|
||||
const version = 'v1.23.2';
|
||||
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
|
||||
const updatePath = join(tempDir, 'ghost-kitty.mp4');
|
||||
await copy(sourcePath, updatePath);
|
||||
|
||||
const privateKeyPath = join(tempDir, 'private.key');
|
||||
const { publicKey, privateKey } = keyPair();
|
||||
await writeHexToPath(privateKeyPath, privateKey);
|
||||
|
||||
await writeSignature(updatePath, version, privateKeyPath);
|
||||
|
||||
const signaturePath = getSignaturePath(updatePath);
|
||||
assert.strictEqual(existsSync(signaturePath), true);
|
||||
|
||||
const verified = await verifySignature(updatePath, version, publicKey);
|
||||
assert.strictEqual(verified, true);
|
||||
} finally {
|
||||
if (tempDir) {
|
||||
await deleteTempDir(tempDir);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('fails signature verification if version changes', async () => {
|
||||
let tempDir;
|
||||
|
||||
try {
|
||||
tempDir = await createTempDir();
|
||||
|
||||
const version = 'v1.23.2';
|
||||
const brokenVersion = 'v1.23.3';
|
||||
|
||||
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
|
||||
const updatePath = join(tempDir, 'ghost-kitty.mp4');
|
||||
await copy(sourcePath, updatePath);
|
||||
|
||||
const privateKeyPath = join(tempDir, 'private.key');
|
||||
const { publicKey, privateKey } = keyPair();
|
||||
await writeHexToPath(privateKeyPath, privateKey);
|
||||
|
||||
await writeSignature(updatePath, version, privateKeyPath);
|
||||
|
||||
const verified = await verifySignature(
|
||||
updatePath,
|
||||
brokenVersion,
|
||||
publicKey
|
||||
);
|
||||
assert.strictEqual(verified, false);
|
||||
} finally {
|
||||
if (tempDir) {
|
||||
await deleteTempDir(tempDir);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('fails signature verification if signature tampered with', async () => {
|
||||
let tempDir;
|
||||
|
||||
try {
|
||||
tempDir = await createTempDir();
|
||||
|
||||
const version = 'v1.23.2';
|
||||
|
||||
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
|
||||
const updatePath = join(tempDir, 'ghost-kitty.mp4');
|
||||
await copy(sourcePath, updatePath);
|
||||
|
||||
const privateKeyPath = join(tempDir, 'private.key');
|
||||
const { publicKey, privateKey } = keyPair();
|
||||
await writeHexToPath(privateKeyPath, privateKey);
|
||||
|
||||
await writeSignature(updatePath, version, privateKeyPath);
|
||||
|
||||
const signaturePath = getSignaturePath(updatePath);
|
||||
const signature = Buffer.from(await loadHexFromPath(signaturePath));
|
||||
signature[4] += 3;
|
||||
await writeHexToPath(signaturePath, signature);
|
||||
|
||||
const verified = await verifySignature(updatePath, version, publicKey);
|
||||
assert.strictEqual(verified, false);
|
||||
} finally {
|
||||
if (tempDir) {
|
||||
await deleteTempDir(tempDir);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('fails signature verification if binary file tampered with', async () => {
|
||||
let tempDir;
|
||||
|
||||
try {
|
||||
tempDir = await createTempDir();
|
||||
|
||||
const version = 'v1.23.2';
|
||||
|
||||
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
|
||||
const updatePath = join(tempDir, 'ghost-kitty.mp4');
|
||||
await copy(sourcePath, updatePath);
|
||||
|
||||
const privateKeyPath = join(tempDir, 'private.key');
|
||||
const { publicKey, privateKey } = keyPair();
|
||||
await writeHexToPath(privateKeyPath, privateKey);
|
||||
|
||||
await writeSignature(updatePath, version, privateKeyPath);
|
||||
|
||||
const brokenSourcePath = join(
|
||||
__dirname,
|
||||
'../../../fixtures/pixabay-Soap-Bubble-7141.mp4'
|
||||
);
|
||||
await copy(brokenSourcePath, updatePath);
|
||||
|
||||
const verified = await verifySignature(updatePath, version, publicKey);
|
||||
assert.strictEqual(verified, false);
|
||||
} finally {
|
||||
if (tempDir) {
|
||||
await deleteTempDir(tempDir);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('fails signature verification if signed by different key', async () => {
|
||||
let tempDir;
|
||||
|
||||
try {
|
||||
tempDir = await createTempDir();
|
||||
|
||||
const version = 'v1.23.2';
|
||||
|
||||
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
|
||||
const updatePath = join(tempDir, 'ghost-kitty.mp4');
|
||||
await copy(sourcePath, updatePath);
|
||||
|
||||
const privateKeyPath = join(tempDir, 'private.key');
|
||||
const { publicKey } = keyPair();
|
||||
const { privateKey } = keyPair();
|
||||
await writeHexToPath(privateKeyPath, privateKey);
|
||||
|
||||
await writeSignature(updatePath, version, privateKeyPath);
|
||||
|
||||
const verified = await verifySignature(updatePath, version, publicKey);
|
||||
assert.strictEqual(verified, false);
|
||||
} finally {
|
||||
if (tempDir) {
|
||||
await deleteTempDir(tempDir);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue