sliftutils 1.7.128 → 1.7.129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/addmachine.js +9 -0
- package/bin/machineclient.js +9 -0
- package/bin/machineserver.js +10 -0
- package/index.d.ts +15 -0
- package/misc/dist/ownIPs.ts.cache +43 -0
- package/misc/dist/strings.ts.cache +2 -2
- package/misc/https/dist/certs.ts.cache +2 -2
- package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
- package/misc/ownIPs.d.ts +11 -0
- package/misc/ownIPs.ts +34 -0
- package/package.json +9 -3
- package/security/authorizedKeys/authorizedKeys.ts +34 -0
- package/security/authorizedKeys/daemon/authLog.ts +12 -5
- package/security/authorizedKeys/daemon/changes.ts +16 -1
- package/security/authorizedKeys/daemon/daemon.ts +43 -34
- package/security/authorizedKeys/daemon/dist/changes.ts.cache +40 -0
- package/security/authorizedKeys/daemon/dist/git.ts.cache +121 -0
- package/security/authorizedKeys/daemon/dist/notify.ts.cache +39 -0
- package/security/authorizedKeys/daemon/dist/paths.ts.cache +29 -0
- package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +114 -0
- package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
- package/security/authorizedKeys/daemon/dist/sessions.ts.cache +147 -0
- package/security/authorizedKeys/daemon/dist/state.ts.cache +74 -0
- package/security/authorizedKeys/daemon/dist/trust.ts.cache +295 -0
- package/security/authorizedKeys/daemon/notify.ts +8 -2
- package/security/authorizedKeys/daemon/repoFiles.ts +118 -0
- package/security/authorizedKeys/daemon/revocation.ts +0 -0
- package/security/authorizedKeys/daemon/rootKeys.ts +77 -10
- package/security/authorizedKeys/daemon/state.ts +25 -7
- package/security/authorizedKeys/daemon/trust.ts +37 -16
- package/security/authorizedKeys/daemon/userKeys.ts +5 -4
- package/security/authorizedKeys/dist/authorizedKeys.ts.cache +37 -3
- package/security/authorizedKeys/dist/revokeSource.ts.cache +17 -4
- package/security/authorizedKeys/dist/sources.ts.cache +52 -8
- package/security/authorizedKeys/dist/unrevoke.ts.cache +113 -17
- package/security/authorizedKeys/secureSSH.ts +39 -21
- package/security/authorizedKeys/unrevoke.ts +159 -8
- package/security/helpers/dist/remoteSSH.ts.cache +19 -9
- package/security/machines/addMachine.ts +83 -0
- package/security/machines/dist/addMachine.ts.cache +88 -0
- package/security/machines/dist/identity.ts.cache +176 -0
- package/security/machines/dist/machines.ts.cache +322 -0
- package/security/machines/dist/trustClient.ts.cache +48 -0
- package/security/machines/dist/trustState.ts.cache +57 -0
- package/security/machines/identity.ts +199 -0
- package/security/machines/machines.ts +355 -0
- package/security/machines/trustClient.ts +51 -0
- package/security/machines/trustServer.ts +85 -0
- package/security/machines/trustState.ts +53 -0
- package/security/notifications/discord.ts +14 -5
- package/security/notifications/dist/discord.ts.cache +17 -8
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
+
//exports.saveState = exports.loadState = exports.sourceState = exports.getState = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const paths_1 = require("./paths");
|
|
10
|
+
let state = {
|
|
11
|
+
sources: {},
|
|
12
|
+
userKeyHashes: {},
|
|
13
|
+
revocations: {},
|
|
14
|
+
unrevokeFirstSeen: {},
|
|
15
|
+
pendingRevocations: [],
|
|
16
|
+
appliedKeys: [],
|
|
17
|
+
authLogOffset: 0,
|
|
18
|
+
authLogSignature: "",
|
|
19
|
+
};
|
|
20
|
+
function getState() {
|
|
21
|
+
return state;
|
|
22
|
+
}
|
|
23
|
+
exports.getState = getState;
|
|
24
|
+
/** Per source progress, created on first use so a newly added source starts clean. */
|
|
25
|
+
function sourceState(repoURL) {
|
|
26
|
+
let existing = state.sources[repoURL];
|
|
27
|
+
if (existing) {
|
|
28
|
+
return existing;
|
|
29
|
+
}
|
|
30
|
+
let created = {
|
|
31
|
+
lastSha: "",
|
|
32
|
+
branch: "",
|
|
33
|
+
accepted: false,
|
|
34
|
+
acceptedSigner: "",
|
|
35
|
+
acceptedKeys: [],
|
|
36
|
+
acceptedManifestHash: "",
|
|
37
|
+
acceptedSignatureHash: "",
|
|
38
|
+
pendingSigner: "",
|
|
39
|
+
pendingSince: 0,
|
|
40
|
+
reportedProblem: "",
|
|
41
|
+
};
|
|
42
|
+
state.sources[repoURL] = created;
|
|
43
|
+
return created;
|
|
44
|
+
}
|
|
45
|
+
exports.sourceState = sourceState;
|
|
46
|
+
async function loadState() {
|
|
47
|
+
let contents;
|
|
48
|
+
try {
|
|
49
|
+
contents = await promises_1.default.readFile(paths_1.STATE_PATH, "utf8");
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
let loaded = JSON.parse(contents);
|
|
56
|
+
// Revocations are held in memory only, so whatever an older version left on disk is not
|
|
57
|
+
// read back in. Starting the daemon is what forgets them.
|
|
58
|
+
delete loaded.revocations;
|
|
59
|
+
state = Object.assign(state, loaded);
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
// Corrupt state only costs us one duplicate notification, so it is not worth failing over.
|
|
63
|
+
console.log(`Ignoring unreadable state file ${paths_1.STATE_PATH}. ${e}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.loadState = loadState;
|
|
67
|
+
async function saveState() {
|
|
68
|
+
let { revocations, ...persisted } = state;
|
|
69
|
+
await promises_1.default.mkdir(path_1.default.dirname(paths_1.STATE_PATH), { recursive: true });
|
|
70
|
+
await promises_1.default.writeFile(paths_1.STATE_PATH, JSON.stringify(persisted, undefined, 4), { mode: 0o600 });
|
|
71
|
+
}
|
|
72
|
+
exports.saveState = saveState;
|
|
73
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSwyREFBNkI7QUFDN0IsZ0RBQXdCO0FBQ3hCLG1DQUFxQztBQTZFckMsSUFBSSxLQUFLLEdBQWdCO0lBQ3JCLE9BQU8sRUFBRSxFQUFFO0lBQ1gsYUFBYSxFQUFFLEVBQUU7SUFDakIsV0FBVyxFQUFFLEVBQUU7SUFDZixpQkFBaUIsRUFBRSxFQUFFO0lBQ3JCLGtCQUFrQixFQUFFLEVBQUU7SUFDdEIsV0FBVyxFQUFFLEVBQUU7SUFDZixhQUFhLEVBQUUsQ0FBQztJQUNoQixnQkFBZ0IsRUFBRSxFQUFFO0NBQ3ZCLENBQUM7QUFFRixTQUFnQixRQUFRO0lBQ3BCLE9BQU8sS0FBSyxDQUFDO0FBQ2pCLENBQUM7QUFGRCw0QkFFQztBQUVELHNGQUFzRjtBQUN0RixTQUFnQixXQUFXLENBQUMsT0FBZTtJQUN2QyxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3RDLElBQUksUUFBUSxFQUFFLENBQUM7UUFDWCxPQUFPLFFBQVEsQ0FBQztJQUNwQixDQUFDO0lBQ0QsSUFBSSxPQUFPLEdBQWdCO1FBQ3ZCLE9BQU8sRUFBRSxFQUFFO1FBQ1gsTUFBTSxFQUFFLEVBQUU7UUFDVixRQUFRLEVBQUUsS0FBSztRQUNmLGNBQWMsRUFBRSxFQUFFO1FBQ2xCLFlBQVksRUFBRSxFQUFFO1FBQ2hCLG9CQUFvQixFQUFFLEVBQUU7UUFDeEIscUJBQXFCLEVBQUUsRUFBRTtRQUN6QixhQUFhLEVBQUUsRUFBRTtRQUNqQixZQUFZLEVBQUUsQ0FBQztRQUNmLGVBQWUsRUFBRSxFQUFFO0tBQ3RCLENBQUM7SUFDRixLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLE9BQU8sQ0FBQztJQUNqQyxPQUFPLE9BQU8sQ0FBQztBQUNuQixDQUFDO0FBbkJELGtDQW1CQztBQUVNLEtBQUssVUFBVSxTQUFTO0lBQzNCLElBQUksUUFBUSxDQUFDO0lBQ2IsSUFBSSxDQUFDO1FBQ0QsUUFBUSxHQUFHLE1BQU0sa0JBQUUsQ0FBQyxRQUFRLENBQUMsa0JBQVUsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNyRCxDQUFDO0lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNULE9BQU87SUFDWCxDQUFDO0lBQ0QsSUFBSSxDQUFDO1FBQ0QsSUFBSSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNsQyx3RkFBd0Y7UUFDeEYsMERBQTBEO1FBQzFELE9BQU8sTUFBTSxDQUFDLFdBQVcsQ0FBQztRQUMxQixLQUFLLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDVCwyRkFBMkY7UUFDM0YsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQ0FBa0Msa0JBQVUsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7QUFDTCxDQUFDO0FBakJELDhCQWlCQztBQUVNLEtBQUssVUFBVSxTQUFTO0lBQzNCLElBQUksRUFBRSxXQUFXLEVBQUUsR0FBRyxTQUFTLEVBQUUsR0FBRyxLQUFLLENBQUM7SUFDMUMsTUFBTSxrQkFBRSxDQUFDLEtBQUssQ0FBQyxjQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFVLENBQUMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzlELE1BQU0sa0JBQUUsQ0FBQyxTQUFTLENBQUMsa0JBQVUsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztBQUM3RixDQUFDO0FBSkQsOEJBSUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZnMgZnJvbSBcImZzL3Byb21pc2VzXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuaW1wb3J0IHsgU1RBVEVfUEFUSCB9IGZyb20gXCIuL3BhdGhzXCI7XG5cbi8qKiBXaGF0IHdlIGxhc3QgZGVjaWRlZCB0byB0cnVzdCBmb3Igb25lIHNvdXJjZSwga2VwdCBvbiBkaXNrIHNvIG5vYm9keSBjYW4gdGVsbCB1cyBhIGRpZmZlcmVudFxuICAgIHN0b3J5IGFib3V0IHdoYXQgd2Ugc2F3IGxhc3QgdGltZS4gKi9cbmV4cG9ydCB0eXBlIFNvdXJjZVN0YXRlID0ge1xuICAgIGxhc3RTaGE6IHN0cmluZztcbiAgICBicmFuY2g6IHN0cmluZztcbiAgICBhY2NlcHRlZDogYm9vbGVhbjtcbiAgICBhY2NlcHRlZFNpZ25lcjogc3RyaW5nO1xuICAgIGFjY2VwdGVkS2V5czogc3RyaW5nW107XG4gICAgYWNjZXB0ZWRNYW5pZmVzdEhhc2g6IHN0cmluZztcbiAgICBhY2NlcHRlZFNpZ25hdHVyZUhhc2g6IHN0cmluZztcbiAgICBwZW5kaW5nU2lnbmVyOiBzdHJpbmc7XG4gICAgcGVuZGluZ1NpbmNlOiBudW1iZXI7XG4gICAgcmVwb3J0ZWRQcm9ibGVtOiBzdHJpbmc7XG59O1xuXG4vKiogT25lIHJldm9jYXRpb24gZXZlbnQ6IHRoaXMga2V5LCB1c2VkIGZyb20gdGhpcyBhZGRyZXNzLCBzbyBpdCBpcyBubyBsb25nZXIgYWNjZXB0ZWQuIEtlcHQgZXZlblxuICAgIHdoZW4gdGhlIHJldm9rZSByZXBvIG5vIGxvbmdlciBsaXN0cyBpdDogdGhlIGRlcGxveSBrZXkgdGhhdCB3cml0ZXMgcmV2b2NhdGlvbnMgbGl2ZXMgb24gZXZlcnlcbiAgICBzZXJ2ZXIsIHNvIGFuIGF0dGFja2VyIHdobyB0b29rIG9uZSBjb3VsZCBvdGhlcndpc2UgZGVsZXRlIHRoZSByZXZvY2F0aW9uIHRoYXQgbG9ja2VkIHRoZW0gb3V0LlxuXG4gICAgVGhlIGlkIGlzIHVuaXF1ZSB0byB0aGUgZXZlbnQsIG5ldmVyIGRlcml2ZWQgZnJvbSB0aGUga2V5LiBBIGtleSB0aGF0IGhhcyBiZWVuIGZvcmdpdmVuIGZvciBvbmVcbiAgICBhZGRyZXNzIGlzIHN0aWxsIGFuIG9yZGluYXJ5IGtleSwgYW5kIHVzaW5nIGl0IGZyb20gc29tZSBvdGhlciBhZGRyZXNzIHJldm9rZXMgaXQgYWdhaW4sIHdpdGggYVxuICAgIG5ldyBpZCB0aGF0IG5vIGV4aXN0aW5nIHVucmV2b2tlIG5hbWVzLiAqL1xuZXhwb3J0IHR5cGUgUmV2b2NhdGlvblN0YXRlID0ge1xuICAgIHJldm9jYXRpb25JZDogc3RyaW5nO1xuICAgIGZpbmdlcnByaW50OiBzdHJpbmc7XG4gICAgLy8gVGhlIGFkZHJlc3MgaXQgd2FzIHVzZWQgZnJvbS4gSGFsZiBvZiB3aGF0IGEgcmV2b2NhdGlvbiBzYXlzLCBhbmQgdGhlIGhhbGYgYW4gdW5yZXZva2UgaGFzXG4gICAgLy8gdG8gbmFtZSB0byB1bmRvIGl0LlxuICAgIGlwOiBzdHJpbmc7XG4gICAgLy8gV2h5IHRoaXMga2V5IHdhcyByZXZva2VkLCBjYXJyaWVkIGZyb20gdGhlIHJldm9jYXRpb24gZmlsZSBzbyBhIG1hY2hpbmUgdGhhdCByZWFkcyBvbmVcbiAgICAvLyB3cml0dGVuIGVsc2V3aGVyZSBjYW4gc2F5IHdoYXQgaGFwcGVuZWQgcmF0aGVyIHRoYW4gb25seSB0aGF0IHNvbWV0aGluZyBkaWQuXG4gICAgcmV2b2tlZEF0OiBzdHJpbmc7XG4gICAgcmV2b2tlZEJ5OiBzdHJpbmc7XG4gICAgcmVhc29uOiBzdHJpbmc7XG4gICAgLy8gV2hpY2ggdW5yZXZva2UgYWxsb3dlZCB0aGlzIGtleSBmcm9tIHRoaXMgYWRkcmVzcywgb25jZSBvbmUgaGFzIGJlZW4gc2Vlbi4gV2hlbiB0aGUgd2FpdCBpdFxuICAgIC8vIHN0YXJ0ZWQgcnVucyBvdXQgaXMgaW4gdW5yZXZva2VGaXJzdFNlZW4sIHdoaWNoIG91dGxpdmVzIHRoZSBkYWVtb24uXG4gICAgdW5yZXZva2VJZDogc3RyaW5nO1xuICAgIHVucmV2b2tlZDogYm9vbGVhbjtcbiAgICAvLyBXaGV0aGVyIHdlIGhhdmUgc2FpZCB0aGF0IHRoaXMga2V5IGFjdHVhbGx5IGxlZnQgcm9vdCdzIGF1dGhvcml6ZWRfa2V5cy4gVGhlIHJlbW92YWwgaXMgdGhlXG4gICAgLy8gdGhpbmcgd29ydGggcmVwb3J0aW5nLCBhbmQgaXQgaXMgd29ydGggcmVwb3J0aW5nIGV4YWN0bHkgb25jZS5cbiAgICByZXBvcnRlZFJlbW92ZWQ6IGJvb2xlYW47XG59O1xuXG4vKiogQSByZWZ1c2FsIHdlIHNhdyBidXQgY291bGQgbm90IHdyaXRlIGRvd24geWV0LiBIZWxkIHVudGlsIGl0IGlzIHJlY29yZGVkLCBiZWNhdXNlIHRoZSBsb2cgaXNcbiAgICByZWFkIG9uY2UgYW5kIG1vdmVzIG9uOiBsb3Npbmcgb25lIG9mIHRoZXNlIHRvIGEgcmVwbyB0aGF0IGhhcHBlbmVkIHRvIGJlIHVucmVhY2hhYmxlIHdvdWxkXG4gICAgbGVhdmUgYSBrZXkgdGhhdCB3YXMgbWlzdXNlZCBhY2NlcHRlZCBmb3JldmVyLiAqL1xuZXhwb3J0IHR5cGUgUGVuZGluZ1Jldm9jYXRpb24gPSB7XG4gICAgZmluZ2VycHJpbnQ6IHN0cmluZztcbiAgICBrZXlMaW5lOiBzdHJpbmc7XG4gICAgc291cmNlVVJMOiBzdHJpbmc7XG4gICAgYXR0ZW1wdDogeyBpcDogc3RyaW5nOyB1c2VyOiBzdHJpbmc7IHBvcnQ6IHN0cmluZzsgcmVxdWlyZWQ6IHN0cmluZzsgbGluZTogc3RyaW5nIH07XG59O1xuXG5leHBvcnQgdHlwZSBEYWVtb25TdGF0ZSA9IHtcbiAgICBzb3VyY2VzOiB7IFtyZXBvVVJMOiBzdHJpbmddOiBTb3VyY2VTdGF0ZSB9O1xuICAgIHVzZXJLZXlIYXNoZXM6IHsgW3VzZXJOYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcbiAgICAvLyBIZWxkIGluIG1lbW9yeSBvbmx5LCBuZXZlciB3cml0dGVuIHRvIGRpc2suIERlbGV0aW5nIGEgcmV2b2NhdGlvbiBmcm9tIHRoZSByZXZva2UgcmVwbyBkb2VzXG4gICAgLy8gbm90IGJyaW5nIGEga2V5IGJhY2ssIGJlY2F1c2UgdGhpcyBtYWNoaW5lIHN0aWxsIHJlbWVtYmVycyBpdCAtIHRoZSBrZXkgdGhhdCB3cml0ZXNcbiAgICAvLyByZXZvY2F0aW9ucyBpcyBvbiBldmVyeSBzZXJ2ZXIsIHNvIHdob2V2ZXIgc3RvbGUgb25lIGNvdWxkIG90aGVyd2lzZSBlcmFzZSB0aGUgcmVjb3JkIHRoYXRcbiAgICAvLyBzaHV0IHRoZW0gb3V0LiBSZXN0YXJ0aW5nIHRoZSBkYWVtb24gZG9lcyBjbGVhciBpdCwgd2hpY2ggaXMgdGhlIHdheSBiYWNrIGZyb20gYSByZXZvY2F0aW9uXG4gICAgLy8gdGhhdCBzaG91bGQgbm90IGhhdmUgaGFwcGVuZWQuXG4gICAgcmV2b2NhdGlvbnM6IHsgW3Jldm9jYXRpb25JZDogc3RyaW5nXTogUmV2b2NhdGlvblN0YXRlIH07XG4gICAgLy8gV2hlbiB0aGlzIG1hY2hpbmUgZmlyc3Qgc2F3IGVhY2ggdW5yZXZva2UsIGJ5IHVucmV2b2tlIGlkLiBPbiBkaXNrLCB1bmxpa2UgdGhlIHJldm9jYXRpb25zXG4gICAgLy8gdGhlbXNlbHZlcywgYmVjYXVzZSBpdCBpcyB3aGF0IHRoZSBob3VyIGxvbmcgd2FpdCBpcyBjb3VudGVkIGZyb206IG1lYXN1cmluZyBmcm9tIGEgdGltZSB0aGVcbiAgICAvLyB1bnJldm9rZSBmaWxlIHN0YXRlcyB3b3VsZCBsZXQgd2hvZXZlciB3cm90ZSBpdCBiYWNrZGF0ZSB0aGUgZmlsZSBhbmQgc2tpcCB0aGUgd2FpdCwgYW5kXG4gICAgLy8gbWVhc3VyaW5nIGZyb20gcHJvY2VzcyBzdGFydCB3b3VsZCByZXN0YXJ0IHRoZSB3YWl0IG9uIGV2ZXJ5IHJlc3RhcnQuXG4gICAgdW5yZXZva2VGaXJzdFNlZW46IHsgW3VucmV2b2tlSWQ6IHN0cmluZ106IG51bWJlciB9O1xuICAgIHBlbmRpbmdSZXZvY2F0aW9uczogUGVuZGluZ1Jldm9jYXRpb25bXTtcbiAgICAvLyBUaGUga2V5cyB3ZSBsYXN0IHdyb3RlIG91dC4gV2hhdCB0ZWxscyBhIGNoYW5nZSB3ZSBtYWRlIGFwYXJ0IGZyb20gb25lIHNvbWVib2R5IGVsc2UgbWFkZTpcbiAgICAvLyBpZiB0aGlzIHN0aWxsIG1hdGNoZXMgd2hhdCB3ZSB3YW50LCBhbmQgdGhlIGZpbGUgZG9lcyBub3QsIHRoZSBmaWxlIHdhcyBlZGl0ZWQgYmVoaW5kIHVzLlxuICAgIGFwcGxpZWRLZXlzOiBzdHJpbmdbXTtcbiAgICAvLyBXaGVyZSB3ZSBoYWQgcmVhZCB1cCB0byBpbiB0aGUgYXV0aCBsb2csIHNvIGEgcmVzdGFydCBkb2VzIG5vdCByZS1yZXBvcnQgb2xkIGF0dGVtcHRzLlxuICAgIGF1dGhMb2dPZmZzZXQ6IG51bWJlcjtcbiAgICBhdXRoTG9nU2lnbmF0dXJlOiBzdHJpbmc7XG59O1xuXG5sZXQgc3RhdGU6IERhZW1vblN0YXRlID0ge1xuICAgIHNvdXJjZXM6IHt9LFxuICAgIHVzZXJLZXlIYXNoZXM6IHt9LFxuICAgIHJldm9jYXRpb25zOiB7fSxcbiAgICB1bnJldm9rZUZpcnN0U2Vlbjoge30sXG4gICAgcGVuZGluZ1Jldm9jYXRpb25zOiBbXSxcbiAgICBhcHBsaWVkS2V5czogW10sXG4gICAgYXV0aExvZ09mZnNldDogMCxcbiAgICBhdXRoTG9nU2lnbmF0dXJlOiBcIlwiLFxufTtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldFN0YXRlKCkge1xuICAgIHJldHVybiBzdGF0ZTtcbn1cblxuLyoqIFBlciBzb3VyY2UgcHJvZ3Jlc3MsIGNyZWF0ZWQgb24gZmlyc3QgdXNlIHNvIGEgbmV3bHkgYWRkZWQgc291cmNlIHN0YXJ0cyBjbGVhbi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzb3VyY2VTdGF0ZShyZXBvVVJMOiBzdHJpbmcpIHtcbiAgICBsZXQgZXhpc3RpbmcgPSBzdGF0ZS5zb3VyY2VzW3JlcG9VUkxdO1xuICAgIGlmIChleGlzdGluZykge1xuICAgICAgICByZXR1cm4gZXhpc3Rpbmc7XG4gICAgfVxuICAgIGxldCBjcmVhdGVkOiBTb3VyY2VTdGF0ZSA9IHtcbiAgICAgICAgbGFzdFNoYTogXCJcIixcbiAgICAgICAgYnJhbmNoOiBcIlwiLFxuICAgICAgICBhY2NlcHRlZDogZmFsc2UsXG4gICAgICAgIGFjY2VwdGVkU2lnbmVyOiBcIlwiLFxuICAgICAgICBhY2NlcHRlZEtleXM6IFtdLFxuICAgICAgICBhY2NlcHRlZE1hbmlmZXN0SGFzaDogXCJcIixcbiAgICAgICAgYWNjZXB0ZWRTaWduYXR1cmVIYXNoOiBcIlwiLFxuICAgICAgICBwZW5kaW5nU2lnbmVyOiBcIlwiLFxuICAgICAgICBwZW5kaW5nU2luY2U6IDAsXG4gICAgICAgIHJlcG9ydGVkUHJvYmxlbTogXCJcIixcbiAgICB9O1xuICAgIHN0YXRlLnNvdXJjZXNbcmVwb1VSTF0gPSBjcmVhdGVkO1xuICAgIHJldHVybiBjcmVhdGVkO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZFN0YXRlKCkge1xuICAgIGxldCBjb250ZW50cztcbiAgICB0cnkge1xuICAgICAgICBjb250ZW50cyA9IGF3YWl0IGZzLnJlYWRGaWxlKFNUQVRFX1BBVEgsIFwidXRmOFwiKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgbGV0IGxvYWRlZCA9IEpTT04ucGFyc2UoY29udGVudHMpO1xuICAgICAgICAvLyBSZXZvY2F0aW9ucyBhcmUgaGVsZCBpbiBtZW1vcnkgb25seSwgc28gd2hhdGV2ZXIgYW4gb2xkZXIgdmVyc2lvbiBsZWZ0IG9uIGRpc2sgaXMgbm90XG4gICAgICAgIC8vIHJlYWQgYmFjayBpbi4gU3RhcnRpbmcgdGhlIGRhZW1vbiBpcyB3aGF0IGZvcmdldHMgdGhlbS5cbiAgICAgICAgZGVsZXRlIGxvYWRlZC5yZXZvY2F0aW9ucztcbiAgICAgICAgc3RhdGUgPSBPYmplY3QuYXNzaWduKHN0YXRlLCBsb2FkZWQpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8gQ29ycnVwdCBzdGF0ZSBvbmx5IGNvc3RzIHVzIG9uZSBkdXBsaWNhdGUgbm90aWZpY2F0aW9uLCBzbyBpdCBpcyBub3Qgd29ydGggZmFpbGluZyBvdmVyLlxuICAgICAgICBjb25zb2xlLmxvZyhgSWdub3JpbmcgdW5yZWFkYWJsZSBzdGF0ZSBmaWxlICR7U1RBVEVfUEFUSH0uICR7ZX1gKTtcbiAgICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzYXZlU3RhdGUoKSB7XG4gICAgbGV0IHsgcmV2b2NhdGlvbnMsIC4uLnBlcnNpc3RlZCB9ID0gc3RhdGU7XG4gICAgYXdhaXQgZnMubWtkaXIocGF0aC5kaXJuYW1lKFNUQVRFX1BBVEgpLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBhd2FpdCBmcy53cml0ZUZpbGUoU1RBVEVfUEFUSCwgSlNPTi5zdHJpbmdpZnkocGVyc2lzdGVkLCB1bmRlZmluZWQsIDQpLCB7IG1vZGU6IDBvNjAwIH0pO1xufVxuIl19
|
|
74
|
+
/* _JS_SOURCE_HASH = "3030766f08724eecf78cccf54e061c2d6de08041d697e10af564a5ada8171569"; */
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
+
//exports.resolveSourceKeys = exports.describeSigner = exports.verifyCheckout = exports.readCheckoutKeys = exports.publicKeyFromSignature = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const spawn_1 = require("../../helpers/spawn");
|
|
11
|
+
const authorizedKeys_1 = require("../authorizedKeys");
|
|
12
|
+
const sources_1 = require("../sources");
|
|
13
|
+
const manifest_1 = require("../../signedFiles/manifest");
|
|
14
|
+
const paths_1 = require("./paths");
|
|
15
|
+
const notify_1 = require("./notify");
|
|
16
|
+
const state_1 = require("./state");
|
|
17
|
+
// A source that has never been signed reads as this, so losing a signature counts as a change of
|
|
18
|
+
// signer rather than as something to wave through.
|
|
19
|
+
const UNSIGNED = "";
|
|
20
|
+
// What fixes both of the signature problems we report, so the message can say so rather than
|
|
21
|
+
// leaving someone to work it out.
|
|
22
|
+
const SIGN_COMMAND = "yarn signfiles git";
|
|
23
|
+
async function pathExists(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
await promises_1.default.access(filePath);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function readSSHString(buffer, offset) {
|
|
33
|
+
let length = buffer.readUInt32BE(offset);
|
|
34
|
+
return buffer.subarray(offset + 4, offset + 4 + length);
|
|
35
|
+
}
|
|
36
|
+
/** The signature carries the public key that made it, so the key itself can be reported rather
|
|
37
|
+
than only a fingerprint. Returns it in the usual "type base64" form. */
|
|
38
|
+
function publicKeyFromSignature(signatureText) {
|
|
39
|
+
let body = signatureText.split("\n").filter(line => line && !line.startsWith("-----")).join("");
|
|
40
|
+
let blob = Buffer.from(body, "base64");
|
|
41
|
+
if (blob.subarray(0, 6).toString() !== "SSHSIG") {
|
|
42
|
+
throw new Error(`Expected an SSHSIG signature, started with ${blob.subarray(0, 6).toString()}`);
|
|
43
|
+
}
|
|
44
|
+
// The magic, then a uint32 version, then the public key.
|
|
45
|
+
let publicKey = readSSHString(blob, 6 + 4);
|
|
46
|
+
let keyType = readSSHString(publicKey, 0).toString();
|
|
47
|
+
let fingerprint = "SHA256:" + crypto_1.default.createHash("sha256").update(publicKey).digest("base64").replace(/=+$/, "");
|
|
48
|
+
return { publicKey: `${keyType} ${publicKey.toString("base64")}`, fingerprint };
|
|
49
|
+
}
|
|
50
|
+
exports.publicKeyFromSignature = publicKeyFromSignature;
|
|
51
|
+
/** Reads one checkout's keys. Prefers a top level authorized_keys file and otherwise concatenates
|
|
52
|
+
every .pub at the top level. */
|
|
53
|
+
async function readCheckoutKeys(repoPath) {
|
|
54
|
+
let combinedPath = path_1.default.join(repoPath, "authorized_keys");
|
|
55
|
+
if (await pathExists(combinedPath)) {
|
|
56
|
+
return (0, authorizedKeys_1.normalizeKeys)(await promises_1.default.readFile(combinedPath, "utf8"));
|
|
57
|
+
}
|
|
58
|
+
let pubFiles = (await promises_1.default.readdir(repoPath)).filter(name => name.endsWith(".pub")).sort();
|
|
59
|
+
if (!pubFiles.length) {
|
|
60
|
+
throw new Error(`Expected authorized_keys or at least one .pub file in ${repoPath}, found neither`);
|
|
61
|
+
}
|
|
62
|
+
let keys = [];
|
|
63
|
+
for (let name of pubFiles) {
|
|
64
|
+
keys.push(...(0, authorizedKeys_1.normalizeKeys)(await promises_1.default.readFile(path_1.default.join(repoPath, name), "utf8")));
|
|
65
|
+
}
|
|
66
|
+
return keys;
|
|
67
|
+
}
|
|
68
|
+
exports.readCheckoutKeys = readCheckoutKeys;
|
|
69
|
+
/** Every file in a checkout, other than git's own directory and the signature files, which cannot
|
|
70
|
+
describe themselves. */
|
|
71
|
+
async function listCheckoutFiles(repoPath, prefix) {
|
|
72
|
+
let files = [];
|
|
73
|
+
for (let entry of await promises_1.default.readdir(path_1.default.join(repoPath, prefix || ""), { withFileTypes: true })) {
|
|
74
|
+
let relativePath = prefix && `${prefix}/${entry.name}` || entry.name;
|
|
75
|
+
if (entry.name === ".git") {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!prefix && (entry.name === manifest_1.MANIFEST_NAME || entry.name === manifest_1.SIGNATURE_NAME)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (entry.isDirectory()) {
|
|
82
|
+
files.push(...await listCheckoutFiles(repoPath, relativePath));
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
files.push(relativePath);
|
|
86
|
+
}
|
|
87
|
+
return files.sort();
|
|
88
|
+
}
|
|
89
|
+
/** The signature only covers the manifest, so the manifest has to be checked against what is
|
|
90
|
+
actually on disk. Both directions matter: a missing file changes what the keys mean, and an
|
|
91
|
+
extra unlisted file could add keys nobody signed for. */
|
|
92
|
+
async function verifyManifestMatchesFiles(repoPath) {
|
|
93
|
+
let manifest = JSON.parse(await promises_1.default.readFile(path_1.default.join(repoPath, manifest_1.MANIFEST_NAME), "utf8"));
|
|
94
|
+
let listed = new Map((manifest.files || []).map((file) => [file.path, file]));
|
|
95
|
+
let actual = await listCheckoutFiles(repoPath);
|
|
96
|
+
let missing = [...listed.keys()].filter(filePath => !actual.includes(filePath));
|
|
97
|
+
if (missing.length) {
|
|
98
|
+
throw new Error(`Expected the signed files to be present, ${missing.length} missing, first ${missing[0]}`);
|
|
99
|
+
}
|
|
100
|
+
let extra = actual.filter(filePath => !listed.has(filePath));
|
|
101
|
+
if (extra.length) {
|
|
102
|
+
throw new Error(`Expected only signed files to be present, ${extra.length} extra, first ${extra[0]}`);
|
|
103
|
+
}
|
|
104
|
+
for (let filePath of actual) {
|
|
105
|
+
let expected = listed.get(filePath);
|
|
106
|
+
if (!expected) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
let contents = (0, manifest_1.normalizeContent)(await promises_1.default.readFile(path_1.default.join(repoPath, filePath)));
|
|
110
|
+
if (contents.length !== expected.size) {
|
|
111
|
+
throw new Error(`Expected ${filePath} to be ${expected.size} bytes, was ${contents.length}`);
|
|
112
|
+
}
|
|
113
|
+
let hash = crypto_1.default.createHash("sha256").update(contents).digest("hex");
|
|
114
|
+
if (hash !== expected.sha256) {
|
|
115
|
+
throw new Error(`Expected ${filePath} to hash to ${expected.sha256}, was ${hash}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** The manifest and signature as they stand, whether or not they are any good. The hashes are
|
|
120
|
+
what tell a signature that was never updated apart from one that is broken. */
|
|
121
|
+
async function readSignatureFiles(repoPath) {
|
|
122
|
+
let manifestPath = path_1.default.join(repoPath, manifest_1.MANIFEST_NAME);
|
|
123
|
+
let signaturePath = path_1.default.join(repoPath, manifest_1.SIGNATURE_NAME);
|
|
124
|
+
let manifest = await pathExists(manifestPath) && await promises_1.default.readFile(manifestPath) || undefined;
|
|
125
|
+
let signature = await pathExists(signaturePath) && await promises_1.default.readFile(signaturePath) || undefined;
|
|
126
|
+
return {
|
|
127
|
+
manifest,
|
|
128
|
+
signature,
|
|
129
|
+
manifestHash: manifest && crypto_1.default.createHash("sha256").update(manifest).digest("hex") || "",
|
|
130
|
+
signatureHash: signature && crypto_1.default.createHash("sha256").update(signature).digest("hex") || "",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Who signed this checkout. Returns UNSIGNED when there is no signature at all, and throws when
|
|
134
|
+
there is one that does not hold up - an unverifiable signature is never treated as an identity,
|
|
135
|
+
so it can never become something we accept. */
|
|
136
|
+
async function verifyCheckoutSigner(config) {
|
|
137
|
+
let { repoPath, files } = config;
|
|
138
|
+
if (!files.manifest && !files.signature) {
|
|
139
|
+
return UNSIGNED;
|
|
140
|
+
}
|
|
141
|
+
if (!files.manifest || !files.signature) {
|
|
142
|
+
throw new Error(`Expected both ${manifest_1.MANIFEST_NAME} and ${manifest_1.SIGNATURE_NAME}, only one is present`);
|
|
143
|
+
}
|
|
144
|
+
let result = await (0, spawn_1.spawnPromise)({
|
|
145
|
+
command: "ssh-keygen",
|
|
146
|
+
args: ["-Y", "check-novalidate", "-n", manifest_1.SIGN_NAMESPACE, "-s", path_1.default.join(repoPath, manifest_1.SIGNATURE_NAME)],
|
|
147
|
+
// The signature covers the normalised bytes, so a checkout that arrived with CRLF still
|
|
148
|
+
// verifies rather than looking like tampering.
|
|
149
|
+
input: (0, manifest_1.normalizeContent)(files.manifest).toString("utf8"),
|
|
150
|
+
});
|
|
151
|
+
if (result.status !== 0) {
|
|
152
|
+
throw new Error(`the signature over ${manifest_1.MANIFEST_NAME} does not verify: `
|
|
153
|
+
+ `${(result.stdout + result.stderr).trim().slice(0, paths_1.MAX_ERROR_BODY_LENGTH)}`);
|
|
154
|
+
}
|
|
155
|
+
let reported = `${result.stdout} ${result.stderr}`.match(/(SHA256:[A-Za-z0-9+/=]+)/);
|
|
156
|
+
if (!reported) {
|
|
157
|
+
throw new Error(`ssh-keygen reported no signer fingerprint, said ${result.stdout.slice(0, paths_1.MAX_ERROR_BODY_LENGTH)}`);
|
|
158
|
+
}
|
|
159
|
+
let { publicKey, fingerprint } = publicKeyFromSignature(files.signature.toString("utf8"));
|
|
160
|
+
// The key we read out of the signature has to be the one ssh-keygen just checked against,
|
|
161
|
+
// otherwise we would be reporting an identity that did not sign anything.
|
|
162
|
+
if (fingerprint !== reported[1]) {
|
|
163
|
+
throw new Error(`the signing key reads as ${fingerprint} but ssh-keygen verified ${reported[1]}`);
|
|
164
|
+
}
|
|
165
|
+
await verifyManifestMatchesFiles(repoPath);
|
|
166
|
+
return publicKey;
|
|
167
|
+
}
|
|
168
|
+
/** Proves a checkout on disk was signed, and that the files in it are the ones that were signed.
|
|
169
|
+
Returns the signer, and throws if anything about that does not hold.
|
|
170
|
+
|
|
171
|
+
Anything reading a repo for something other than ssh keys goes through this first: the whole
|
|
172
|
+
checkout is covered by one manifest, so a machine list is exactly as trustworthy as the keys
|
|
173
|
+
beside it, and neither is worth reading unsigned. */
|
|
174
|
+
async function verifyCheckout(repoPath) {
|
|
175
|
+
return await verifyCheckoutSigner({ repoPath, files: await readSignatureFiles(repoPath) });
|
|
176
|
+
}
|
|
177
|
+
exports.verifyCheckout = verifyCheckout;
|
|
178
|
+
function describeSigner(signer) {
|
|
179
|
+
return signer === UNSIGNED && "<no public key>" || signer;
|
|
180
|
+
}
|
|
181
|
+
exports.describeSigner = describeSigner;
|
|
182
|
+
/** Reports a problem with a source's signature, but only when it is not the same problem we
|
|
183
|
+
already reported, so a fault that persists does not repeat every check. */
|
|
184
|
+
async function reportProblem(config) {
|
|
185
|
+
let { repoURL, problem, headline, body } = config;
|
|
186
|
+
let sourceStateValue = (0, state_1.sourceState)(repoURL);
|
|
187
|
+
if (sourceStateValue.reportedProblem === problem) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
sourceStateValue.reportedProblem = problem;
|
|
191
|
+
await (0, state_1.saveState)();
|
|
192
|
+
await (0, notify_1.notify)(headline, body);
|
|
193
|
+
}
|
|
194
|
+
/** The keys a source is allowed to contribute right now. A source signed by someone we have not
|
|
195
|
+
accepted keeps contributing the keys we last accepted, until the delay has passed. */
|
|
196
|
+
async function resolveSourceKeys(repoURL) {
|
|
197
|
+
let sourceStateValue = (0, state_1.sourceState)(repoURL);
|
|
198
|
+
let repoPath = (0, sources_1.sourceRepoPath)(repoURL);
|
|
199
|
+
let files = await readSignatureFiles(repoPath);
|
|
200
|
+
let signer;
|
|
201
|
+
try {
|
|
202
|
+
signer = await verifyCheckoutSigner({ repoPath, files });
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
// Nothing here is trustworthy, so nothing here is used. Which of the two problems it is
|
|
206
|
+
// depends on whether the signature is simply the one we already accepted.
|
|
207
|
+
let unchanged = files.manifestHash === sourceStateValue.acceptedManifestHash
|
|
208
|
+
&& files.signatureHash === sourceStateValue.acceptedSignatureHash;
|
|
209
|
+
if (unchanged) {
|
|
210
|
+
await reportProblem({
|
|
211
|
+
repoURL,
|
|
212
|
+
problem: "stale",
|
|
213
|
+
headline: `KEY REPO CHANGED WITHOUT BEING SIGNED`,
|
|
214
|
+
body: `\`${repoURL}\` changed, but nobody signed the change, so it is being ignored`
|
|
215
|
+
+ ` and this machine is still using the keys signed by`
|
|
216
|
+
+ ` \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
|
|
217
|
+
+ `\n\nTo make the change take effect, run this in that repo:`
|
|
218
|
+
+ `\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
await reportProblem({
|
|
223
|
+
repoURL,
|
|
224
|
+
problem: "corrupt",
|
|
225
|
+
headline: `KEY REPO SIGNATURE IS BROKEN`,
|
|
226
|
+
body: `The signature on \`${repoURL}\` does not check out, so everything in it is`
|
|
227
|
+
+ ` being ignored and this machine is still using the keys signed by`
|
|
228
|
+
+ ` \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
|
|
229
|
+
+ `\n\n${e}`
|
|
230
|
+
+ `\n\nTo replace the signature, run this in that repo:`
|
|
231
|
+
+ `\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return sourceStateValue.acceptedKeys;
|
|
235
|
+
}
|
|
236
|
+
if (sourceStateValue.reportedProblem) {
|
|
237
|
+
sourceStateValue.reportedProblem = "";
|
|
238
|
+
await (0, state_1.saveState)();
|
|
239
|
+
}
|
|
240
|
+
let checkoutKeys = await readCheckoutKeys(repoPath);
|
|
241
|
+
let accept = async () => {
|
|
242
|
+
sourceStateValue.accepted = true;
|
|
243
|
+
sourceStateValue.acceptedSigner = signer;
|
|
244
|
+
sourceStateValue.acceptedKeys = checkoutKeys;
|
|
245
|
+
sourceStateValue.acceptedManifestHash = files.manifestHash;
|
|
246
|
+
sourceStateValue.acceptedSignatureHash = files.signatureHash;
|
|
247
|
+
sourceStateValue.pendingSigner = UNSIGNED;
|
|
248
|
+
sourceStateValue.pendingSince = 0;
|
|
249
|
+
await (0, state_1.saveState)();
|
|
250
|
+
return checkoutKeys;
|
|
251
|
+
};
|
|
252
|
+
// Nothing has ever been accepted from this source, so this is what we start trusting.
|
|
253
|
+
if (!sourceStateValue.accepted) {
|
|
254
|
+
console.log(`Trusting ${repoURL} as signed by ${describeSigner(signer)}`);
|
|
255
|
+
return await accept();
|
|
256
|
+
}
|
|
257
|
+
if (signer === sourceStateValue.acceptedSigner) {
|
|
258
|
+
// Back to the signer we already trust, so anything we were waiting on is moot.
|
|
259
|
+
if (sourceStateValue.pendingSince) {
|
|
260
|
+
console.log(`${repoURL} is signed by its accepted key again, dropping the pending change`);
|
|
261
|
+
}
|
|
262
|
+
return await accept();
|
|
263
|
+
}
|
|
264
|
+
// Going from nothing to something signed is only ever an improvement, so it does not wait.
|
|
265
|
+
if (sourceStateValue.acceptedSigner === UNSIGNED) {
|
|
266
|
+
await (0, notify_1.notify)(`KEY REPO IS NOW SIGNED`, `\`${repoURL}\` was not signed at all before, so this is an improvement and its keys`
|
|
267
|
+
+ ` are being applied right away.`
|
|
268
|
+
+ `\n\nsigned by: \`${signer}\``);
|
|
269
|
+
return await accept();
|
|
270
|
+
}
|
|
271
|
+
// A signer we have not accepted. Anything new restarts the wait, so publishing twice in a row
|
|
272
|
+
// gains an attacker nothing. pendingSince is what marks a wait as running, because an unsigned
|
|
273
|
+
// checkout is itself a signer value and cannot double as "nothing pending".
|
|
274
|
+
if (!sourceStateValue.pendingSince || signer !== sourceStateValue.pendingSigner) {
|
|
275
|
+
sourceStateValue.pendingSigner = signer;
|
|
276
|
+
sourceStateValue.pendingSince = Date.now();
|
|
277
|
+
await (0, state_1.saveState)();
|
|
278
|
+
await (0, notify_1.notify)(`KEY REPO SIGNED BY A DIFFERENT KEY`, `Somebody else is signing \`${repoURL}\` now. Its keys are NOT being applied, and this`
|
|
279
|
+
+ ` machine is still using the ones it already had. If nothing changes, the new signer`
|
|
280
|
+
+ ` is accepted in 24 hours.`
|
|
281
|
+
+ `\n\nIf that was not you, fix the repo before the 24 hours are up.`
|
|
282
|
+
+ `\n\nsigned by now: \`${describeSigner(signer)}\``
|
|
283
|
+
+ `\nsigned by before: \`${describeSigner(sourceStateValue.acceptedSigner)}\``);
|
|
284
|
+
return sourceStateValue.acceptedKeys;
|
|
285
|
+
}
|
|
286
|
+
if (Date.now() - sourceStateValue.pendingSince < paths_1.SIGNER_CHANGE_DELAY) {
|
|
287
|
+
return sourceStateValue.acceptedKeys;
|
|
288
|
+
}
|
|
289
|
+
// Same new signer, 24 hours later, and nobody stopped it.
|
|
290
|
+
console.log(`Accepting ${describeSigner(signer)} for ${repoURL} after the ${paths_1.SIGNER_CHANGE_DELAY}ms wait`);
|
|
291
|
+
return await accept();
|
|
292
|
+
}
|
|
293
|
+
exports.resolveSourceKeys = resolveSourceKeys;
|
|
294
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJ1c3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0cnVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUN4QiwrQ0FBbUQ7QUFDbkQsc0RBQWtEO0FBQ2xELHdDQUE0QztBQUM1Qyx5REFBNkc7QUFDN0csbUNBQXFFO0FBQ3JFLHFDQUFrQztBQUNsQyxtQ0FBaUQ7QUFFakQsaUdBQWlHO0FBQ2pHLG1EQUFtRDtBQUNuRCxNQUFNLFFBQVEsR0FBRyxFQUFFLENBQUM7QUFDcEIsNkZBQTZGO0FBQzdGLGtDQUFrQztBQUNsQyxNQUFNLFlBQVksR0FBRyxvQkFBb0IsQ0FBQztBQUUxQyxLQUFLLFVBQVUsVUFBVSxDQUFDLFFBQWdCO0lBQ3RDLElBQUksQ0FBQztRQUNELE1BQU0sa0JBQUUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUM7SUFDaEIsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDVCxPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsYUFBYSxDQUFDLE1BQWMsRUFBRSxNQUFjO0lBQ2pELElBQUksTUFBTSxHQUFHLE1BQU0sQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDekMsT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsTUFBTSxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQztBQUM1RCxDQUFDO0FBRUQ7MkVBQzJFO0FBQzNFLFNBQWdCLHNCQUFzQixDQUFDLGFBQXFCO0lBQ3hELElBQUksSUFBSSxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FB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QUMzQyxNQUFNLElBQUEsaUJBQVMsR0FBRSxDQUFDO1FBQ2xCLE1BQU0sSUFBQSxlQUFNLEVBQUMsb0NBQW9DLEVBQzdDLDhCQUE4QixPQUFPLGtEQUFrRDtjQUNyRixxRkFBcUY7Y0FDckYsMkJBQTJCO2NBQzNCLG1FQUFtRTtjQUNuRSx3QkFBd0IsY0FBYyxDQUFDLE1BQU0sQ0FBQyxJQUFJO2NBQ2xELHlCQUF5QixjQUFjLENBQUMsZ0JBQWdCLENBQUMsY0FBYyxDQUFDLElBQUksQ0FDakYsQ0FBQztRQUNGLE9BQU8sZ0JBQWdCLENBQUMsWUFBWSxDQUFDO0lBQ3pDLENBQUM7SUFFRCxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxnQkFBZ0IsQ0FBQyxZQUFZLEdBQUcsMkJBQW1CLEVBQUUsQ0FBQztRQUNuRSxPQUFPLGdCQUFnQixDQUFDLFlBQVksQ0FBQztJQUN6QyxDQUFDO0lBRUQsMERBQTBEO0lBQzFELE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxjQUFjLENBQUMsTUFBTSxDQUFDLFFBQVEsT0FBTyxjQUFjLDJCQUFtQixTQUFTLENBQUMsQ0FBQztJQUMxRyxPQUFPLE1BQU0sTUFBTSxFQUFFLENBQUM7QUFDMUIsQ0FBQztBQTFHRCw4Q0EwR0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY3J5cHRvIGZyb20gXCJjcnlwdG9cIjtcbmltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBzcGF3blByb21pc2UgfSBmcm9tIFwiLi4vLi4vaGVscGVycy9zcGF3blwiO1xuaW1wb3J0IHsgbm9ybWFsaXplS2V5cyB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5c1wiO1xuaW1wb3J0IHsgc291cmNlUmVwb1BhdGggfSBmcm9tIFwiLi4vc291cmNlc1wiO1xuaW1wb3J0IHsgTUFOSUZFU1RfTkFNRSwgbm9ybWFsaXplQ29udGVudCwgU0lHTl9OQU1FU1BBQ0UsIFNJR05BVFVSRV9OQU1FIH0gZnJvbSBcIi4uLy4uL3NpZ25lZEZpbGVzL21hbmlmZXN0XCI7XG5pbXBvcnQgeyBNQVhfRVJST1JfQk9EWV9MRU5HVEgsIFNJR05FUl9DSEFOR0VfREVMQVkgfSBmcm9tIFwiLi9wYXRoc1wiO1xuaW1wb3J0IHsgbm90aWZ5IH0gZnJvbSBcIi4vbm90aWZ5XCI7XG5pbXBvcnQgeyBzYXZlU3RhdGUsIHNvdXJjZVN0YXRlIH0gZnJvbSBcIi4vc3RhdGVcIjtcblxuLy8gQSBzb3VyY2UgdGhhdCBoYXMgbmV2ZXIgYmVlbiBzaWduZWQgcmVhZHMgYXMgdGhpcywgc28gbG9zaW5nIGEgc2lnbmF0dXJlIGNvdW50cyBhcyBhIGNoYW5nZSBvZlxuLy8gc2lnbmVyIHJhdGhlciB0aGFuIGFzIHNvbWV0aGluZyB0byB3YXZlIHRocm91Z2guXG5jb25zdCBVTlNJR05FRCA9IFwiXCI7XG4vLyBXaGF0IGZpeGVzIGJvdGggb2YgdGhlIHNpZ25hdHVyZSBwcm9ibGVtcyB3ZSByZXBvcnQsIHNvIHRoZSBtZXNzYWdlIGNhbiBzYXkgc28gcmF0aGVyIHRoYW5cbi8vIGxlYXZpbmcgc29tZW9uZSB0byB3b3JrIGl0IG91dC5cbmNvbnN0IFNJR05fQ09NTUFORCA9IFwieWFybiBzaWduZmlsZXMgZ2l0XCI7XG5cbmFzeW5jIGZ1bmN0aW9uIHBhdGhFeGlzdHMoZmlsZVBhdGg6IHN0cmluZykge1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZzLmFjY2VzcyhmaWxlUGF0aCk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gcmVhZFNTSFN0cmluZyhidWZmZXI6IEJ1ZmZlciwgb2Zmc2V0OiBudW1iZXIpIHtcbiAgICBsZXQgbGVuZ3RoID0gYnVmZmVyLnJlYWRVSW50MzJCRShvZmZzZXQpO1xuICAgIHJldHVybiBidWZmZXIuc3ViYXJyYXkob2Zmc2V0ICsgNCwgb2Zmc2V0ICsgNCArIGxlbmd0aCk7XG59XG5cbi8qKiBUaGUgc2lnbmF0dXJlIGNhcnJpZXMgdGhlIHB1YmxpYyBrZXkgdGhhdCBtYWRlIGl0LCBzbyB0aGUga2V5IGl0c2VsZiBjYW4gYmUgcmVwb3J0ZWQgcmF0aGVyXG4gICAgdGhhbiBvbmx5IGEgZmluZ2VycHJpbnQuIFJldHVybnMgaXQgaW4gdGhlIHVzdWFsIFwidHlwZSBiYXNlNjRcIiBmb3JtLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHB1YmxpY0tleUZyb21TaWduYXR1cmUoc2lnbmF0dXJlVGV4dDogc3RyaW5nKSB7XG4gICAgbGV0IGJvZHkgPSBzaWduYXR1cmVUZXh0LnNwbGl0KFwiXFxuXCIpLmZpbHRlcihsaW5lID0+IGxpbmUgJiYgIWxpbmUuc3RhcnRzV2l0aChcIi0tLS0tXCIpKS5qb2luKFwiXCIpO1xuICAgIGxldCBibG9iID0gQnVmZmVyLmZyb20oYm9keSwgXCJiYXNlNjRcIik7XG4gICAgaWYgKGJsb2Iuc3ViYXJyYXkoMCwgNikudG9TdHJpbmcoKSAhPT0gXCJTU0hTSUdcIikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGFuIFNTSFNJRyBzaWduYXR1cmUsIHN0YXJ0ZWQgd2l0aCAke2Jsb2Iuc3ViYXJyYXkoMCwgNikudG9TdHJpbmcoKX1gKTtcbiAgICB9XG4gICAgLy8gVGhlIG1hZ2ljLCB0aGVuIGEgdWludDMyIHZlcnNpb24sIHRoZW4gdGhlIHB1YmxpYyBrZXkuXG4gICAgbGV0IHB1YmxpY0tleSA9IHJlYWRTU0hTdHJpbmcoYmxvYiwgNiArIDQpO1xuICAgIGxldCBrZXlUeXBlID0gcmVhZFNTSFN0cmluZyhwdWJsaWNLZXksIDApLnRvU3RyaW5nKCk7XG4gICAgbGV0IGZpbmdlcnByaW50ID0gXCJTSEEyNTY6XCIgKyBjcnlwdG8uY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUocHVibGljS2V5KS5kaWdlc3QoXCJiYXNlNjRcIikucmVwbGFjZSgvPSskLywgXCJcIik7XG4gICAgcmV0dXJuIHsgcHVibGljS2V5OiBgJHtrZXlUeXBlfSAke3B1YmxpY0tleS50b1N0cmluZyhcImJhc2U2NFwiKX1gLCBmaW5nZXJwcmludCB9O1xufVxuXG4vKiogUmVhZHMgb25lIGNoZWNrb3V0J3Mga2V5cy4gUHJlZmVycyBhIHRvcCBsZXZlbCBhdXRob3JpemVkX2tleXMgZmlsZSBhbmQgb3RoZXJ3aXNlIGNvbmNhdGVuYXRlc1xuICAgIGV2ZXJ5IC5wdWIgYXQgdGhlIHRvcCBsZXZlbC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkQ2hlY2tvdXRLZXlzKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICBsZXQgY29tYmluZWRQYXRoID0gcGF0aC5qb2luKHJlcG9QYXRoLCBcImF1dGhvcml6ZWRfa2V5c1wiKTtcbiAgICBpZiAoYXdhaXQgcGF0aEV4aXN0cyhjb21iaW5lZFBhdGgpKSB7XG4gICAgICAgIHJldHVybiBub3JtYWxpemVLZXlzKGF3YWl0IGZzLnJlYWRGaWxlKGNvbWJpbmVkUGF0aCwgXCJ1dGY4XCIpKTtcbiAgICB9XG4gICAgbGV0IHB1YkZpbGVzID0gKGF3YWl0IGZzLnJlYWRkaXIocmVwb1BhdGgpKS5maWx0ZXIobmFtZSA9PiBuYW1lLmVuZHNXaXRoKFwiLnB1YlwiKSkuc29ydCgpO1xuICAgIGlmICghcHViRmlsZXMubGVuZ3RoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYXV0aG9yaXplZF9rZXlzIG9yIGF0IGxlYXN0IG9uZSAucHViIGZpbGUgaW4gJHtyZXBvUGF0aH0sIGZvdW5kIG5laXRoZXJgKTtcbiAgICB9XG4gICAgbGV0IGtleXM6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChsZXQgbmFtZSBvZiBwdWJGaWxlcykge1xuICAgICAgICBrZXlzLnB1c2goLi4ubm9ybWFsaXplS2V5cyhhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4ocmVwb1BhdGgsIG5hbWUpLCBcInV0ZjhcIikpKTtcbiAgICB9XG4gICAgcmV0dXJuIGtleXM7XG59XG5cbi8qKiBFdmVyeSBmaWxlIGluIGEgY2hlY2tvdXQsIG90aGVyIHRoYW4gZ2l0J3Mgb3duIGRpcmVjdG9yeSBhbmQgdGhlIHNpZ25hdHVyZSBmaWxlcywgd2hpY2ggY2Fubm90XG4gICAgZGVzY3JpYmUgdGhlbXNlbHZlcy4gKi9cbmFzeW5jIGZ1bmN0aW9uIGxpc3RDaGVja291dEZpbGVzKHJlcG9QYXRoOiBzdHJpbmcsIHByZWZpeD86IHN0cmluZyk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBsZXQgZmlsZXM6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChsZXQgZW50cnkgb2YgYXdhaXQgZnMucmVhZGRpcihwYXRoLmpvaW4ocmVwb1BhdGgsIHByZWZpeCB8fCBcIlwiKSwgeyB3aXRoRmlsZVR5cGVzOiB0cnVlIH0pKSB7XG4gICAgICAgIGxldCByZWxhdGl2ZVBhdGggPSBwcmVmaXggJiYgYCR7cHJlZml4fS8ke2VudHJ5Lm5hbWV9YCB8fCBlbnRyeS5uYW1lO1xuICAgICAgICBpZiAoZW50cnkubmFtZSA9PT0gXCIuZ2l0XCIpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGlmICghcHJlZml4ICYmIChlbnRyeS5uYW1lID09PSBNQU5JRkVTVF9OQU1FIHx8IGVudHJ5Lm5hbWUgPT09IFNJR05BVFVSRV9OQU1FKSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVudHJ5LmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgICAgIGZpbGVzLnB1c2goLi4uYXdhaXQgbGlzdENoZWNrb3V0RmlsZXMocmVwb1BhdGgsIHJlbGF0aXZlUGF0aCkpO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgZmlsZXMucHVzaChyZWxhdGl2ZVBhdGgpO1xuICAgIH1cbiAgICByZXR1cm4gZmlsZXMuc29ydCgpO1xufVxuXG4vKiogVGhlIHNpZ25hdHVyZSBvbmx5IGNvdmVycyB0aGUgbWFuaWZlc3QsIHNvIHRoZSBtYW5pZmVzdCBoYXMgdG8gYmUgY2hlY2tlZCBhZ2FpbnN0IHdoYXQgaXNcbiAgICBhY3R1YWxseSBvbiBkaXNrLiBCb3RoIGRpcmVjdGlvbnMgbWF0dGVyOiBhIG1pc3NpbmcgZmlsZSBjaGFuZ2VzIHdoYXQgdGhlIGtleXMgbWVhbiwgYW5kIGFuXG4gICAgZXh0cmEgdW5saXN0ZWQgZmlsZSBjb3VsZCBhZGQga2V5cyBub2JvZHkgc2lnbmVkIGZvci4gKi9cbmFzeW5jIGZ1bmN0aW9uIHZlcmlmeU1hbmlmZXN0TWF0Y2hlc0ZpbGVzKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICBsZXQgbWFuaWZlc3QgPSBKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKHBhdGguam9pbihyZXBvUGF0aCwgTUFOSUZFU1RfTkFNRSksIFwidXRmOFwiKSk7XG4gICAgbGV0IGxpc3RlZCA9IG5ldyBNYXA8c3RyaW5nLCB7IHBhdGg6IHN0cmluZzsgc2l6ZTogbnVtYmVyOyBzaGEyNTY6IHN0cmluZyB9PihcbiAgICAgICAgKG1hbmlmZXN0LmZpbGVzIHx8IFtdKS5tYXAoKGZpbGU6IHsgcGF0aDogc3RyaW5nIH0pID0+IFtmaWxlLnBhdGgsIGZpbGVdKVxuICAgICk7XG4gICAgbGV0IGFjdHVhbCA9IGF3YWl0IGxpc3RDaGVja291dEZpbGVzKHJlcG9QYXRoKTtcblxuICAgIGxldCBtaXNzaW5nID0gWy4uLmxpc3RlZC5rZXlzKCldLmZpbHRlcihmaWxlUGF0aCA9PiAhYWN0dWFsLmluY2x1ZGVzKGZpbGVQYXRoKSk7XG4gICAgaWYgKG1pc3NpbmcubGVuZ3RoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgdGhlIHNpZ25lZCBmaWxlcyB0byBiZSBwcmVzZW50LCAke21pc3NpbmcubGVuZ3RofSBtaXNzaW5nLCBmaXJzdCAke21pc3NpbmdbMF19YCk7XG4gICAgfVxuICAgIGxldCBleHRyYSA9IGFjdHVhbC5maWx0ZXIoZmlsZVBhdGggPT4gIWxpc3RlZC5oYXMoZmlsZVBhdGgpKTtcbiAgICBpZiAoZXh0cmEubGVuZ3RoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgb25seSBzaWduZWQgZmlsZXMgdG8gYmUgcHJlc2VudCwgJHtleHRyYS5sZW5ndGh9IGV4dHJhLCBmaXJzdCAke2V4dHJhWzBdfWApO1xuICAgIH1cbiAgICBmb3IgKGxldCBmaWxlUGF0aCBvZiBhY3R1YWwpIHtcbiAgICAgICAgbGV0IGV4cGVjdGVkID0gbGlzdGVkLmdldChmaWxlUGF0aCk7XG4gICAgICAgIGlmICghZXhwZWN0ZWQpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGxldCBjb250ZW50cyA9IG5vcm1hbGl6ZUNvbnRlbnQoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKHJlcG9QYXRoLCBmaWxlUGF0aCkpKTtcbiAgICAgICAgaWYgKGNvbnRlbnRzLmxlbmd0aCAhPT0gZXhwZWN0ZWQuc2l6ZSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCAke2ZpbGVQYXRofSB0byBiZSAke2V4cGVjdGVkLnNpemV9IGJ5dGVzLCB3YXMgJHtjb250ZW50cy5sZW5ndGh9YCk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGhhc2ggPSBjcnlwdG8uY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUoY29udGVudHMpLmRpZ2VzdChcImhleFwiKTtcbiAgICAgICAgaWYgKGhhc2ggIT09IGV4cGVjdGVkLnNoYTI1Nikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCAke2ZpbGVQYXRofSB0byBoYXNoIHRvICR7ZXhwZWN0ZWQuc2hhMjU2fSwgd2FzICR7aGFzaH1gKTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqIFRoZSBtYW5pZmVzdCBhbmQgc2lnbmF0dXJlIGFzIHRoZXkgc3RhbmQsIHdoZXRoZXIgb3Igbm90IHRoZXkgYXJlIGFueSBnb29kLiBUaGUgaGFzaGVzIGFyZVxuICAgIHdoYXQgdGVsbCBhIHNpZ25hdHVyZSB0aGF0IHdhcyBuZXZlciB1cGRhdGVkIGFwYXJ0IGZyb20gb25lIHRoYXQgaXMgYnJva2VuLiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVhZFNpZ25hdHVyZUZpbGVzKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICBsZXQgbWFuaWZlc3RQYXRoID0gcGF0aC5qb2luKHJlcG9QYXRoLCBNQU5JRkVTVF9OQU1FKTtcbiAgICBsZXQgc2lnbmF0dXJlUGF0aCA9IHBhdGguam9pbihyZXBvUGF0aCwgU0lHTkFUVVJFX05BTUUpO1xuICAgIGxldCBtYW5pZmVzdCA9IGF3YWl0IHBhdGhFeGlzdHMobWFuaWZlc3RQYXRoKSAmJiBhd2FpdCBmcy5yZWFkRmlsZShtYW5pZmVzdFBhdGgpIHx8IHVuZGVmaW5lZDtcbiAgICBsZXQgc2lnbmF0dXJlID0gYXdhaXQgcGF0aEV4aXN0cyhzaWduYXR1cmVQYXRoKSAmJiBhd2FpdCBmcy5yZWFkRmlsZShzaWduYXR1cmVQYXRoKSB8fCB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgbWFuaWZlc3QsXG4gICAgICAgIHNpZ25hdHVyZSxcbiAgICAgICAgbWFuaWZlc3RIYXNoOiBtYW5pZmVzdCAmJiBjcnlwdG8uY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUobWFuaWZlc3QpLmRpZ2VzdChcImhleFwiKSB8fCBcIlwiLFxuICAgICAgICBzaWduYXR1cmVIYXNoOiBzaWduYXR1cmUgJiYgY3J5cHRvLmNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKHNpZ25hdHVyZSkuZGlnZXN0KFwiaGV4XCIpIHx8IFwiXCIsXG4gICAgfTtcbn1cblxuLyoqIFdobyBzaWduZWQgdGhpcyBjaGVja291dC4gUmV0dXJucyBVTlNJR05FRCB3aGVuIHRoZXJlIGlzIG5vIHNpZ25hdHVyZSBhdCBhbGwsIGFuZCB0aHJvd3Mgd2hlblxuICAgIHRoZXJlIGlzIG9uZSB0aGF0IGRvZXMgbm90IGhvbGQgdXAgLSBhbiB1bnZlcmlmaWFibGUgc2lnbmF0dXJlIGlzIG5ldmVyIHRyZWF0ZWQgYXMgYW4gaWRlbnRpdHksXG4gICAgc28gaXQgY2FuIG5ldmVyIGJlY29tZSBzb21ldGhpbmcgd2UgYWNjZXB0LiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5Q2hlY2tvdXRTaWduZXIoY29uZmlnOiB7XG4gICAgcmVwb1BhdGg6IHN0cmluZztcbiAgICBmaWxlczogeyBtYW5pZmVzdD86IEJ1ZmZlcjsgc2lnbmF0dXJlPzogQnVmZmVyIH07XG59KSB7XG4gICAgbGV0IHsgcmVwb1BhdGgsIGZpbGVzIH0gPSBjb25maWc7XG4gICAgaWYgKCFmaWxlcy5tYW5pZmVzdCAmJiAhZmlsZXMuc2lnbmF0dXJlKSB7XG4gICAgICAgIHJldHVybiBVTlNJR05FRDtcbiAgICB9XG4gICAgaWYgKCFmaWxlcy5tYW5pZmVzdCB8fCAhZmlsZXMuc2lnbmF0dXJlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYm90aCAke01BTklGRVNUX05BTUV9IGFuZCAke1NJR05BVFVSRV9OQU1FfSwgb25seSBvbmUgaXMgcHJlc2VudGApO1xuICAgIH1cbiAgICBsZXQgcmVzdWx0ID0gYXdhaXQgc3Bhd25Qcm9taXNlKHtcbiAgICAgICAgY29tbWFuZDogXCJzc2gta2V5Z2VuXCIsXG4gICAgICAgIGFyZ3M6IFtcIi1ZXCIsIFwiY2hlY2stbm92YWxpZGF0ZVwiLCBcIi1uXCIsIFNJR05fTkFNRVNQQUNFLCBcIi1zXCIsIHBhdGguam9pbihyZXBvUGF0aCwgU0lHTkFUVVJFX05BTUUpXSxcbiAgICAgICAgLy8gVGhlIHNpZ25hdHVyZSBjb3ZlcnMgdGhlIG5vcm1hbGlzZWQgYnl0ZXMsIHNvIGEgY2hlY2tvdXQgdGhhdCBhcnJpdmVkIHdpdGggQ1JMRiBzdGlsbFxuICAgICAgICAvLyB2ZXJpZmllcyByYXRoZXIgdGhhbiBsb29raW5nIGxpa2UgdGFtcGVyaW5nLlxuICAgICAgICBpbnB1dDogbm9ybWFsaXplQ29udGVudChmaWxlcy5tYW5pZmVzdCkudG9TdHJpbmcoXCJ1dGY4XCIpLFxuICAgIH0pO1xuICAgIGlmIChyZXN1bHQuc3RhdHVzICE9PSAwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGB0aGUgc2lnbmF0dXJlIG92ZXIgJHtNQU5JRkVTVF9OQU1FfSBkb2VzIG5vdCB2ZXJpZnk6IGBcbiAgICAgICAgICAgICsgYCR7KHJlc3VsdC5zdGRvdXQgKyByZXN1bHQuc3RkZXJyKS50cmltKCkuc2xpY2UoMCwgTUFYX0VSUk9SX0JPRFlfTEVOR1RIKX1gXG4gICAgICAgICk7XG4gICAgfVxuICAgIGxldCByZXBvcnRlZCA9IGAke3Jlc3VsdC5zdGRvdXR9ICR7cmVzdWx0LnN0ZGVycn1gLm1hdGNoKC8oU0hBMjU2OltBLVphLXowLTkrLz1dKykvKTtcbiAgICBpZiAoIXJlcG9ydGVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgc3NoLWtleWdlbiByZXBvcnRlZCBubyBzaWduZXIgZmluZ2VycHJpbnQsIHNhaWQgJHtyZXN1bHQuc3Rkb3V0LnNsaWNlKDAsIE1BWF9FUlJPUl9CT0RZX0xFTkdUSCl9YCk7XG4gICAgfVxuICAgIGxldCB7IHB1YmxpY0tleSwgZmluZ2VycHJpbnQgfSA9IHB1YmxpY0tleUZyb21TaWduYXR1cmUoZmlsZXMuc2lnbmF0dXJlLnRvU3RyaW5nKFwidXRmOFwiKSk7XG4gICAgLy8gVGhlIGtleSB3ZSByZWFkIG91dCBvZiB0aGUgc2lnbmF0dXJlIGhhcyB0byBiZSB0aGUgb25lIHNzaC1rZXlnZW4ganVzdCBjaGVja2VkIGFnYWluc3QsXG4gICAgLy8gb3RoZXJ3aXNlIHdlIHdvdWxkIGJlIHJlcG9ydGluZyBhbiBpZGVudGl0eSB0aGF0IGRpZCBub3Qgc2lnbiBhbnl0aGluZy5cbiAgICBpZiAoZmluZ2VycHJpbnQgIT09IHJlcG9ydGVkWzFdKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgdGhlIHNpZ25pbmcga2V5IHJlYWRzIGFzICR7ZmluZ2VycHJpbnR9IGJ1dCBzc2gta2V5Z2VuIHZlcmlmaWVkICR7cmVwb3J0ZWRbMV19YCk7XG4gICAgfVxuICAgIGF3YWl0IHZlcmlmeU1hbmlmZXN0TWF0Y2hlc0ZpbGVzKHJlcG9QYXRoKTtcbiAgICByZXR1cm4gcHVibGljS2V5O1xufVxuXG4vKiogUHJvdmVzIGEgY2hlY2tvdXQgb24gZGlzayB3YXMgc2lnbmVkLCBhbmQgdGhhdCB0aGUgZmlsZXMgaW4gaXQgYXJlIHRoZSBvbmVzIHRoYXQgd2VyZSBzaWduZWQuXG4gICAgUmV0dXJucyB0aGUgc2lnbmVyLCBhbmQgdGhyb3dzIGlmIGFueXRoaW5nIGFib3V0IHRoYXQgZG9lcyBub3QgaG9sZC5cblxuICAgIEFueXRoaW5nIHJlYWRpbmcgYSByZXBvIGZvciBzb21ldGhpbmcgb3RoZXIgdGhhbiBzc2gga2V5cyBnb2VzIHRocm91Z2ggdGhpcyBmaXJzdDogdGhlIHdob2xlXG4gICAgY2hlY2tvdXQgaXMgY292ZXJlZCBieSBvbmUgbWFuaWZlc3QsIHNvIGEgbWFjaGluZSBsaXN0IGlzIGV4YWN0bHkgYXMgdHJ1c3R3b3J0aHkgYXMgdGhlIGtleXNcbiAgICBiZXNpZGUgaXQsIGFuZCBuZWl0aGVyIGlzIHdvcnRoIHJlYWRpbmcgdW5zaWduZWQuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdmVyaWZ5Q2hlY2tvdXQocmVwb1BhdGg6IHN0cmluZykge1xuICAgIHJldHVybiBhd2FpdCB2ZXJpZnlDaGVja291dFNpZ25lcih7IHJlcG9QYXRoLCBmaWxlczogYXdhaXQgcmVhZFNpZ25hdHVyZUZpbGVzKHJlcG9QYXRoKSB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlc2NyaWJlU2lnbmVyKHNpZ25lcjogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHNpZ25lciA9PT0gVU5TSUdORUQgJiYgXCI8bm8gcHVibGljIGtleT5cIiB8fCBzaWduZXI7XG59XG5cbi8qKiBSZXBvcnRzIGEgcHJvYmxlbSB3aXRoIGEgc291cmNlJ3Mgc2lnbmF0dXJlLCBidXQgb25seSB3aGVuIGl0IGlzIG5vdCB0aGUgc2FtZSBwcm9ibGVtIHdlXG4gICAgYWxyZWFkeSByZXBvcnRlZCwgc28gYSBmYXVsdCB0aGF0IHBlcnNpc3RzIGRvZXMgbm90IHJlcGVhdCBldmVyeSBjaGVjay4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlcG9ydFByb2JsZW0oY29uZmlnOiB7IHJlcG9VUkw6IHN0cmluZzsgcHJvYmxlbTogc3RyaW5nOyBoZWFkbGluZTogc3RyaW5nOyBib2R5OiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHJlcG9VUkwsIHByb2JsZW0sIGhlYWRsaW5lLCBib2R5IH0gPSBjb25maWc7XG4gICAgbGV0IHNvdXJjZVN0YXRlVmFsdWUgPSBzb3VyY2VTdGF0ZShyZXBvVVJMKTtcbiAgICBpZiAoc291cmNlU3RhdGVWYWx1ZS5yZXBvcnRlZFByb2JsZW0gPT09IHByb2JsZW0pIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBzb3VyY2VTdGF0ZVZhbHVlLnJlcG9ydGVkUHJvYmxlbSA9IHByb2JsZW07XG4gICAgYXdhaXQgc2F2ZVN0YXRlKCk7XG4gICAgYXdhaXQgbm90aWZ5KGhlYWRsaW5lLCBib2R5KTtcbn1cblxuLyoqIFRoZSBrZXlzIGEgc291cmNlIGlzIGFsbG93ZWQgdG8gY29udHJpYnV0ZSByaWdodCBub3cuIEEgc291cmNlIHNpZ25lZCBieSBzb21lb25lIHdlIGhhdmUgbm90XG4gICAgYWNjZXB0ZWQga2VlcHMgY29udHJpYnV0aW5nIHRoZSBrZXlzIHdlIGxhc3QgYWNjZXB0ZWQsIHVudGlsIHRoZSBkZWxheSBoYXMgcGFzc2VkLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlc29sdmVTb3VyY2VLZXlzKHJlcG9VUkw6IHN0cmluZykge1xuICAgIGxldCBzb3VyY2VTdGF0ZVZhbHVlID0gc291cmNlU3RhdGUocmVwb1VSTCk7XG4gICAgbGV0IHJlcG9QYXRoID0gc291cmNlUmVwb1BhdGgocmVwb1VSTCk7XG4gICAgbGV0IGZpbGVzID0gYXdhaXQgcmVhZFNpZ25hdHVyZUZpbGVzKHJlcG9QYXRoKTtcblxuICAgIGxldCBzaWduZXI6IHN0cmluZztcbiAgICB0cnkge1xuICAgICAgICBzaWduZXIgPSBhd2FpdCB2ZXJpZnlDaGVja291dFNpZ25lcih7IHJlcG9QYXRoLCBmaWxlcyB9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIE5vdGhpbmcgaGVyZSBpcyB0cnVzdHdvcnRoeSwgc28gbm90aGluZyBoZXJlIGlzIHVzZWQuIFdoaWNoIG9mIHRoZSB0d28gcHJvYmxlbXMgaXQgaXNcbiAgICAgICAgLy8gZGVwZW5kcyBvbiB3aGV0aGVyIHRoZSBzaWduYXR1cmUgaXMgc2ltcGx5IHRoZSBvbmUgd2UgYWxyZWFkeSBhY2NlcHRlZC5cbiAgICAgICAgbGV0IHVuY2hhbmdlZCA9IGZpbGVzLm1hbmlmZXN0SGFzaCA9PT0gc291cmNlU3RhdGVWYWx1ZS5hY2NlcHRlZE1hbmlmZXN0SGFzaFxuICAgICAgICAgICAgJiYgZmlsZXMuc2lnbmF0dXJlSGFzaCA9PT0gc291cmNlU3RhdGVWYWx1ZS5hY2NlcHRlZFNpZ25hdHVyZUhhc2g7XG4gICAgICAgIGlmICh1bmNoYW5nZWQpIHtcbiAgICAgICAgICAgIGF3YWl0IHJlcG9ydFByb2JsZW0oe1xuICAgICAgICAgICAgICAgIHJlcG9VUkwsXG4gICAgICAgICAgICAgICAgcHJvYmxlbTogXCJzdGFsZVwiLFxuICAgICAgICAgICAgICAgIGhlYWRsaW5lOiBgS0VZIFJFUE8gQ0hBTkdFRCBXSVRIT1VUIEJFSU5HIFNJR05FRGAsXG4gICAgICAgICAgICAgICAgYm9keTogYFxcYCR7cmVwb1VSTH1cXGAgY2hhbmdlZCwgYnV0IG5vYm9keSBzaWduZWQgdGhlIGNoYW5nZSwgc28gaXQgaXMgYmVpbmcgaWdub3JlZGBcbiAgICAgICAgICAgICAgICAgICAgKyBgIGFuZCB0aGlzIG1hY2hpbmUgaXMgc3RpbGwgdXNpbmcgdGhlIGtleXMgc2lnbmVkIGJ5YFxuICAgICAgICAgICAgICAgICAgICArIGAgXFxgJHtkZXNjcmliZVNpZ25lcihzb3VyY2VTdGF0ZVZhbHVlLmFjY2VwdGVkU2lnbmVyKX1cXGAuYFxuICAgICAgICAgICAgICAgICAgICArIGBcXG5cXG5UbyBtYWtlIHRoZSBjaGFuZ2UgdGFrZSBlZmZlY3QsIHJ1biB0aGlzIGluIHRoYXQgcmVwbzpgXG4gICAgICAgICAgICAgICAgICAgICsgYFxcblxcYFxcYFxcYFxcbiR7U0lHTl9DT01NQU5EfVxcblxcYFxcYFxcYGAsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGF3YWl0IHJlcG9ydFByb2JsZW0oe1xuICAgICAgICAgICAgICAgIHJlcG9VUkwsXG4gICAgICAgICAgICAgICAgcHJvYmxlbTogXCJjb3JydXB0XCIsXG4gICAgICAgICAgICAgICAgaGVhZGxpbmU6IGBLRVkgUkVQTyBTSUdOQVRVUkUgSVMgQlJPS0VOYCxcbiAgICAgICAgICAgICAgICBib2R5OiBgVGhlIHNpZ25hdHVyZSBvbiBcXGAke3JlcG9VUkx9XFxgIGRvZXMgbm90IGNoZWNrIG91dCwgc28gZXZlcnl0aGluZyBpbiBpdCBpc2BcbiAgICAgICAgICAgICAgICAgICAgKyBgIGJlaW5nIGlnbm9yZWQgYW5kIHRoaXMgbWFjaGluZSBpcyBzdGlsbCB1c2luZyB0aGUga2V5cyBzaWduZWQgYnlgXG4gICAgICAgICAgICAgICAgICAgICsgYCBcXGAke2Rlc2NyaWJlU2lnbmVyKHNvdXJjZVN0YXRlVmFsdWUuYWNjZXB0ZWRTaWduZXIpfVxcYC5gXG4gICAgICAgICAgICAgICAgICAgICsgYFxcblxcbiR7ZX1gXG4gICAgICAgICAgICAgICAgICAgICsgYFxcblxcblRvIHJlcGxhY2UgdGhlIHNpZ25hdHVyZSwgcnVuIHRoaXMgaW4gdGhhdCByZXBvOmBcbiAgICAgICAgICAgICAgICAgICAgKyBgXFxuXFxgXFxgXFxgXFxuJHtTSUdOX0NPTU1BTkR9XFxuXFxgXFxgXFxgYCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzb3VyY2VTdGF0ZVZhbHVlLmFjY2VwdGVkS2V5cztcbiAgICB9XG4gICAgaWYgKHNvdXJjZVN0YXRlVmFsdWUucmVwb3J0ZWRQcm9ibGVtKSB7XG4gICAgICAgIHNvdXJjZVN0YXRlVmFsdWUucmVwb3J0ZWRQcm9ibGVtID0gXCJcIjtcbiAgICAgICAgYXdhaXQgc2F2ZVN0YXRlKCk7XG4gICAgfVxuICAgIGxldCBjaGVja291dEtleXMgPSBhd2FpdCByZWFkQ2hlY2tvdXRLZXlzKHJlcG9QYXRoKTtcblxuICAgIGxldCBhY2NlcHQgPSBhc3luYyAoKSA9PiB7XG4gICAgICAgIHNvdXJjZVN0YXRlVmFsdWUuYWNjZXB0ZWQgPSB0cnVlO1xuICAgICAgICBzb3VyY2VTdGF0ZVZhbHVlLmFjY2VwdGVkU2lnbmVyID0gc2lnbmVyO1xuICAgICAgICBzb3VyY2VTdGF0ZVZhbHVlLmFjY2VwdGVkS2V5cyA9IGNoZWNrb3V0S2V5cztcbiAgICAgICAgc291cmNlU3RhdGVWYWx1ZS5hY2NlcHRlZE1hbmlmZXN0SGFzaCA9IGZpbGVzLm1hbmlmZXN0SGFzaDtcbiAgICAgICAgc291cmNlU3RhdGVWYWx1ZS5hY2NlcHRlZFNpZ25hdHVyZUhhc2ggPSBmaWxlcy5zaWduYXR1cmVIYXNoO1xuICAgICAgICBzb3VyY2VTdGF0ZVZhbHVlLnBlbmRpbmdTaWduZXIgPSBVTlNJR05FRDtcbiAgICAgICAgc291cmNlU3RhdGVWYWx1ZS5wZW5kaW5nU2luY2UgPSAwO1xuICAgICAgICBhd2FpdCBzYXZlU3RhdGUoKTtcbiAgICAgICAgcmV0dXJuIGNoZWNrb3V0S2V5cztcbiAgICB9O1xuXG4gICAgLy8gTm90aGluZyBoYXMgZXZlciBiZWVuIGFjY2VwdGVkIGZyb20gdGhpcyBzb3VyY2UsIHNvIHRoaXMgaXMgd2hhdCB3ZSBzdGFydCB0cnVzdGluZy5cbiAgICBpZiAoIXNvdXJjZVN0YXRlVmFsdWUuYWNjZXB0ZWQpIHtcbiAgICAgICAgY29uc29sZS5sb2coYFRydXN0aW5nICR7cmVwb1VSTH0gYXMgc2lnbmVkIGJ5ICR7ZGVzY3JpYmVTaWduZXIoc2lnbmVyKX1gKTtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGFjY2VwdCgpO1xuICAgIH1cblxuICAgIGlmIChzaWduZXIgPT09IHNvdXJjZVN0YXRlVmFsdWUuYWNjZXB0ZWRTaWduZXIpIHtcbiAgICAgICAgLy8gQmFjayB0byB0aGUgc2lnbmVyIHdlIGFscmVhZHkgdHJ1c3QsIHNvIGFueXRoaW5nIHdlIHdlcmUgd2FpdGluZyBvbiBpcyBtb290LlxuICAgICAgICBpZiAoc291cmNlU3RhdGVWYWx1ZS5wZW5kaW5nU2luY2UpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGAke3JlcG9VUkx9IGlzIHNpZ25lZCBieSBpdHMgYWNjZXB0ZWQga2V5IGFnYWluLCBkcm9wcGluZyB0aGUgcGVuZGluZyBjaGFuZ2VgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXdhaXQgYWNjZXB0KCk7XG4gICAgfVxuXG4gICAgLy8gR29pbmcgZnJvbSBub3RoaW5nIHRvIHNvbWV0aGluZyBzaWduZWQgaXMgb25seSBldmVyIGFuIGltcHJvdmVtZW50LCBzbyBpdCBkb2VzIG5vdCB3YWl0LlxuICAgIGlmIChzb3VyY2VTdGF0ZVZhbHVlLmFjY2VwdGVkU2lnbmVyID09PSBVTlNJR05FRCkge1xuICAgICAgICBhd2FpdCBub3RpZnkoYEtFWSBSRVBPIElTIE5PVyBTSUdORURgLFxuICAgICAgICAgICAgYFxcYCR7cmVwb1VSTH1cXGAgd2FzIG5vdCBzaWduZWQgYXQgYWxsIGJlZm9yZSwgc28gdGhpcyBpcyBhbiBpbXByb3ZlbWVudCBhbmQgaXRzIGtleXNgXG4gICAgICAgICAgICArIGAgYXJlIGJlaW5nIGFwcGxpZWQgcmlnaHQgYXdheS5gXG4gICAgICAgICAgICArIGBcXG5cXG5zaWduZWQgYnk6IFxcYCR7c2lnbmVyfVxcYGBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGFjY2VwdCgpO1xuICAgIH1cblxuICAgIC8vIEEgc2lnbmVyIHdlIGhhdmUgbm90IGFjY2VwdGVkLiBBbnl0aGluZyBuZXcgcmVzdGFydHMgdGhlIHdhaXQsIHNvIHB1Ymxpc2hpbmcgdHdpY2UgaW4gYSByb3dcbiAgICAvLyBnYWlucyBhbiBhdHRhY2tlciBub3RoaW5nLiBwZW5kaW5nU2luY2UgaXMgd2hhdCBtYXJrcyBhIHdhaXQgYXMgcnVubmluZywgYmVjYXVzZSBhbiB1bnNpZ25lZFxuICAgIC8vIGNoZWNrb3V0IGlzIGl0c2VsZiBhIHNpZ25lciB2YWx1ZSBhbmQgY2Fubm90IGRvdWJsZSBhcyBcIm5vdGhpbmcgcGVuZGluZ1wiLlxuICAgIGlmICghc291cmNlU3RhdGVWYWx1ZS5wZW5kaW5nU2luY2UgfHwgc2lnbmVyICE9PSBzb3VyY2VTdGF0ZVZhbHVlLnBlbmRpbmdTaWduZXIpIHtcbiAgICAgICAgc291cmNlU3RhdGVWYWx1ZS5wZW5kaW5nU2lnbmVyID0gc2lnbmVyO1xuICAgICAgICBzb3VyY2VTdGF0ZVZhbHVlLnBlbmRpbmdTaW5jZSA9IERhdGUubm93KCk7XG4gICAgICAgIGF3YWl0IHNhdmVTdGF0ZSgpO1xuICAgICAgICBhd2FpdCBub3RpZnkoYEtFWSBSRVBPIFNJR05FRCBCWSBBIERJRkZFUkVOVCBLRVlgLFxuICAgICAgICAgICAgYFNvbWVib2R5IGVsc2UgaXMgc2lnbmluZyBcXGAke3JlcG9VUkx9XFxgIG5vdy4gSXRzIGtleXMgYXJlIE5PVCBiZWluZyBhcHBsaWVkLCBhbmQgdGhpc2BcbiAgICAgICAgICAgICsgYCBtYWNoaW5lIGlzIHN0aWxsIHVzaW5nIHRoZSBvbmVzIGl0IGFscmVhZHkgaGFkLiBJZiBub3RoaW5nIGNoYW5nZXMsIHRoZSBuZXcgc2lnbmVyYFxuICAgICAgICAgICAgKyBgIGlzIGFjY2VwdGVkIGluIDI0IGhvdXJzLmBcbiAgICAgICAgICAgICsgYFxcblxcbklmIHRoYXQgd2FzIG5vdCB5b3UsIGZpeCB0aGUgcmVwbyBiZWZvcmUgdGhlIDI0IGhvdXJzIGFyZSB1cC5gXG4gICAgICAgICAgICArIGBcXG5cXG5zaWduZWQgYnkgbm93OiBcXGAke2Rlc2NyaWJlU2lnbmVyKHNpZ25lcil9XFxgYFxuICAgICAgICAgICAgKyBgXFxuc2lnbmVkIGJ5IGJlZm9yZTogXFxgJHtkZXNjcmliZVNpZ25lcihzb3VyY2VTdGF0ZVZhbHVlLmFjY2VwdGVkU2lnbmVyKX1cXGBgXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiBzb3VyY2VTdGF0ZVZhbHVlLmFjY2VwdGVkS2V5cztcbiAgICB9XG5cbiAgICBpZiAoRGF0ZS5ub3coKSAtIHNvdXJjZVN0YXRlVmFsdWUucGVuZGluZ1NpbmNlIDwgU0lHTkVSX0NIQU5HRV9ERUxBWSkge1xuICAgICAgICByZXR1cm4gc291cmNlU3RhdGVWYWx1ZS5hY2NlcHRlZEtleXM7XG4gICAgfVxuXG4gICAgLy8gU2FtZSBuZXcgc2lnbmVyLCAyNCBob3VycyBsYXRlciwgYW5kIG5vYm9keSBzdG9wcGVkIGl0LlxuICAgIGNvbnNvbGUubG9nKGBBY2NlcHRpbmcgJHtkZXNjcmliZVNpZ25lcihzaWduZXIpfSBmb3IgJHtyZXBvVVJMfSBhZnRlciB0aGUgJHtTSUdORVJfQ0hBTkdFX0RFTEFZfW1zIHdhaXRgKTtcbiAgICByZXR1cm4gYXdhaXQgYWNjZXB0KCk7XG59XG4iXX0=
|
|
295
|
+
/* _JS_SOURCE_HASH = "2791d2b7398a863c4757a111c3f83e155ece380b6fa541c5bb7384f2c2829041"; */
|
|
@@ -11,8 +11,14 @@ export function setHostLabel(value: string) {
|
|
|
11
11
|
// notification is only ever added when the user explicitly asks for that specific case. Startup,
|
|
12
12
|
// success, errors, retries and recoveries all belong in console.log instead. The complete list of
|
|
13
13
|
// cases that are allowed to notify is at the top of daemon.ts.
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/** A headline and the rest.
|
|
15
|
+
|
|
16
|
+
The headline is all a phone shows. It gets the whole preview to itself: no product name, no
|
|
17
|
+
host, no timestamp in front of it, because a person glancing at their lock screen needs to read
|
|
18
|
+
what happened, not which machine said it or when. Those go at the end, where they are there for
|
|
19
|
+
whoever opens the message and out of the way of whoever does not. */
|
|
20
|
+
export async function notify(headline: string, body: string) {
|
|
21
|
+
let full = `**${headline}**\n\n${body}\n\n${hostLabelValue || os.hostname()}`;
|
|
16
22
|
// Logged before it is sent, and whether or not it arrives, so the journal is a complete record
|
|
17
23
|
// of what this machine had to say even when Discord is unreachable or the webhook is wrong.
|
|
18
24
|
console.log(`Discord: ${full}`);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { deriveRevokeKey, findRevokeKey, REVOKE_KEY_LABEL, revokeKeyPath, revokeRepoPath, revokeRepoURL } from "../revokeSource";
|
|
4
|
+
import { findSourceKey, sourceKeyPath, sourceRepoPath } from "../sources";
|
|
5
|
+
import { cloneRepo, repoIsUsable, runGit } from "./git";
|
|
6
|
+
|
|
7
|
+
/** Reading a repo, made to look like reading a directory. A caller asks for a file and gets its
|
|
8
|
+
contents; keeping a checkout on disk, cloning it once and pulling it since, is this module's
|
|
9
|
+
business and nobody else's.
|
|
10
|
+
|
|
11
|
+
Nothing here ever answers "there is nothing" when the truth is "I could not look". A repo that
|
|
12
|
+
cannot be reached throws, and the caller skips that source, because reading an unreachable
|
|
13
|
+
revoke repo as empty would un-revoke every key in it. */
|
|
14
|
+
export type RepoRef = {
|
|
15
|
+
repoURL: string;
|
|
16
|
+
repoPath: string;
|
|
17
|
+
keyPath: () => Promise<string>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function sourceRepo(sourceURL: string): RepoRef {
|
|
21
|
+
return {
|
|
22
|
+
repoURL: sourceURL,
|
|
23
|
+
repoPath: sourceRepoPath(sourceURL),
|
|
24
|
+
keyPath: async () => await findSourceKey(sourceURL) || sourceKeyPath(sourceURL),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function revokeRepo(sourceURL: string): RepoRef {
|
|
29
|
+
return {
|
|
30
|
+
repoURL: revokeRepoURL(sourceURL),
|
|
31
|
+
repoPath: revokeRepoPath(sourceURL),
|
|
32
|
+
keyPath: () => ensureRevokeKey(sourceURL),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The revoke repo's key is worked out from the source's, so nothing extra had to be uploaded and
|
|
37
|
+
nothing extra is stored anywhere it could be taken from. */
|
|
38
|
+
export async function ensureRevokeKey(sourceURL: string) {
|
|
39
|
+
let existing = await findRevokeKey(sourceURL);
|
|
40
|
+
if (existing) {
|
|
41
|
+
return existing;
|
|
42
|
+
}
|
|
43
|
+
let sourceKey = await findSourceKey(sourceURL);
|
|
44
|
+
if (!sourceKey) {
|
|
45
|
+
throw new Error(`Expected a key for ${sourceURL} at ${sourceKeyPath(sourceURL)}, no such file exists`);
|
|
46
|
+
}
|
|
47
|
+
let derived = deriveRevokeKey(await fs.readFile(sourceKey, "utf8"));
|
|
48
|
+
let keyPath = revokeKeyPath(sourceURL);
|
|
49
|
+
await fs.mkdir(path.dirname(keyPath), { recursive: true, mode: 0o700 });
|
|
50
|
+
await fs.writeFile(keyPath, derived.privateKeyFile, { mode: 0o600 });
|
|
51
|
+
await fs.writeFile(`${keyPath}.pub`, `${derived.publicKey} ${REVOKE_KEY_LABEL}\n`, { mode: 0o644 });
|
|
52
|
+
console.log(`Derived the revoke key for ${sourceURL} into ${keyPath}`);
|
|
53
|
+
return keyPath;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function pathExists(filePath: string) {
|
|
57
|
+
try {
|
|
58
|
+
await fs.access(filePath);
|
|
59
|
+
return true;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Brings the checkout in line with the remote. Cloned the first time, pulled after that, and the
|
|
66
|
+
usual case of nothing having changed costs one ref listing and no objects. Throws if the repo
|
|
67
|
+
cannot be read, so a caller cannot mistake that for an empty repo. */
|
|
68
|
+
export async function syncRepoFiles(repo: RepoRef) {
|
|
69
|
+
let { repoURL, repoPath } = repo;
|
|
70
|
+
let keyPath = await repo.keyPath();
|
|
71
|
+
if (!await repoIsUsable({ repoPath, keyPath })) {
|
|
72
|
+
await cloneRepo({ repoURL, repoPath, keyPath });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let localHead = await runGit({ args: ["rev-parse", "HEAD"], cwd: repoPath, keyPath, allowFailure: true });
|
|
76
|
+
if (localHead.status !== 0) {
|
|
77
|
+
// A repo with no commits yet, which is the state every revoke repo starts in. There is
|
|
78
|
+
// nothing to pull and nothing to say.
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let branch = (await runGit({ args: ["rev-parse", "--abbrev-ref", "HEAD"], cwd: repoPath, keyPath })).stdout.trim();
|
|
82
|
+
let listing = (await runGit({ args: ["ls-remote", "origin", branch], cwd: repoPath, keyPath })).stdout;
|
|
83
|
+
let remoteSha = (listing.split(/\s+/)[0] || "").trim();
|
|
84
|
+
if (remoteSha && remoteSha === localHead.stdout.trim()) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await runGit({ args: ["fetch", "--prune", "origin"], cwd: repoPath, keyPath });
|
|
88
|
+
await runGit({ args: ["reset", "--hard", `origin/${branch}`], cwd: repoPath, keyPath });
|
|
89
|
+
await runGit({ args: ["clean", "-fdx"], cwd: repoPath, keyPath });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Throws when there is no checkout at all. "I have never managed to read this repo" and "this
|
|
93
|
+
repo is empty" are different answers and must not collapse into one. */
|
|
94
|
+
async function requireCheckout(repo: RepoRef) {
|
|
95
|
+
if (!await pathExists(path.join(repo.repoPath, ".git"))) {
|
|
96
|
+
throw new Error(`Expected a checkout of ${repo.repoURL} at ${repo.repoPath}, there is none`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** One file out of the repo, or undefined if the repo does not have that file. */
|
|
101
|
+
export async function readRepoFile(repo: RepoRef, filePath: string) {
|
|
102
|
+
await requireCheckout(repo);
|
|
103
|
+
try {
|
|
104
|
+
return await fs.readFile(path.join(repo.repoPath, filePath), "utf8");
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The names in one of the repo's directories, or nothing if it has no such directory. */
|
|
111
|
+
export async function listRepoDir(repo: RepoRef, directory: string) {
|
|
112
|
+
await requireCheckout(repo);
|
|
113
|
+
try {
|
|
114
|
+
return (await fs.readdir(path.join(repo.repoPath, directory))).sort();
|
|
115
|
+
} catch (e) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
Binary file
|