sliftutils 1.7.127 → 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.
Files changed (54) hide show
  1. package/bin/addmachine.js +9 -0
  2. package/bin/machineclient.js +9 -0
  3. package/bin/machineserver.js +10 -0
  4. package/index.d.ts +15 -0
  5. package/misc/dist/ownIPs.ts.cache +43 -0
  6. package/misc/dist/strings.ts.cache +2 -2
  7. package/misc/https/dist/certs.ts.cache +2 -2
  8. package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
  9. package/misc/ownIPs.d.ts +11 -0
  10. package/misc/ownIPs.ts +34 -0
  11. package/package.json +9 -3
  12. package/security/authorizedKeys/authorizedKeys.ts +34 -0
  13. package/security/authorizedKeys/daemon/authLog.ts +12 -5
  14. package/security/authorizedKeys/daemon/changes.ts +16 -1
  15. package/security/authorizedKeys/daemon/daemon.ts +50 -40
  16. package/security/authorizedKeys/daemon/dist/changes.ts.cache +40 -0
  17. package/security/authorizedKeys/daemon/dist/git.ts.cache +121 -0
  18. package/security/authorizedKeys/daemon/dist/notify.ts.cache +39 -0
  19. package/security/authorizedKeys/daemon/dist/paths.ts.cache +29 -0
  20. package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +114 -0
  21. package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
  22. package/security/authorizedKeys/daemon/dist/sessions.ts.cache +147 -0
  23. package/security/authorizedKeys/daemon/dist/state.ts.cache +74 -0
  24. package/security/authorizedKeys/daemon/dist/trust.ts.cache +295 -0
  25. package/security/authorizedKeys/daemon/git.ts +3 -3
  26. package/security/authorizedKeys/daemon/notify.ts +8 -2
  27. package/security/authorizedKeys/daemon/repoFiles.ts +118 -0
  28. package/security/authorizedKeys/daemon/revocation.ts +0 -0
  29. package/security/authorizedKeys/daemon/rootKeys.ts +77 -10
  30. package/security/authorizedKeys/daemon/state.ts +25 -7
  31. package/security/authorizedKeys/daemon/trust.ts +37 -16
  32. package/security/authorizedKeys/daemon/userKeys.ts +5 -4
  33. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +37 -3
  34. package/security/authorizedKeys/dist/revokeSource.ts.cache +17 -4
  35. package/security/authorizedKeys/dist/sources.ts.cache +52 -8
  36. package/security/authorizedKeys/dist/unrevoke.ts.cache +113 -17
  37. package/security/authorizedKeys/revokeSource.ts +12 -2
  38. package/security/authorizedKeys/secureSSH.ts +66 -12
  39. package/security/authorizedKeys/sources.ts +47 -5
  40. package/security/authorizedKeys/unrevoke.ts +176 -16
  41. package/security/helpers/dist/remoteSSH.ts.cache +19 -9
  42. package/security/machines/addMachine.ts +83 -0
  43. package/security/machines/dist/addMachine.ts.cache +88 -0
  44. package/security/machines/dist/identity.ts.cache +176 -0
  45. package/security/machines/dist/machines.ts.cache +322 -0
  46. package/security/machines/dist/trustClient.ts.cache +48 -0
  47. package/security/machines/dist/trustState.ts.cache +57 -0
  48. package/security/machines/identity.ts +199 -0
  49. package/security/machines/machines.ts +355 -0
  50. package/security/machines/trustClient.ts +51 -0
  51. package/security/machines/trustServer.ts +85 -0
  52. package/security/machines/trustState.ts +53 -0
  53. package/security/notifications/discord.ts +14 -5
  54. package/security/notifications/dist/discord.ts.cache +17 -8
@@ -0,0 +1,176 @@
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.verifyTrustPacket = exports.signTrustPacket = exports.createRemoteIdentity = exports.remoteIdentity = exports.localIdentityKeys = exports.localIdentity = exports.localDomains = exports.PACKET_LIFETIME = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const certs_1 = require("../../misc/https/certs");
12
+ const remoteSSH_1 = require("../helpers/remoteSSH");
13
+ // A machine's identity is the CA it generated for itself, kept in one file in the home directory
14
+ // of whoever runs it. The name carries the domain it belongs to, so a machine can hold one per
15
+ // domain, and the id itself is a hash of that CA's public key - which is why a machine cannot
16
+ // claim to be another one without holding its key.
17
+ const IDENTITY_PREFIX = "keystore_";
18
+ const IDENTITY_SUFFIX = `_${certs_1.identityStorageKey}.json`;
19
+ // How stale a signed packet may be. Long enough for clock skew between two machines, short enough
20
+ // that a captured packet is not worth replaying.
21
+ exports.PACKET_LIFETIME = 2 * 60 * 1000;
22
+ function identityFileName(domain) {
23
+ return `${IDENTITY_PREFIX}${domain}${IDENTITY_SUFFIX}`;
24
+ }
25
+ /** The domain out of an identity file's name, or "" if it is not one. */
26
+ function domainOfFileName(fileName) {
27
+ if (!fileName.startsWith(IDENTITY_PREFIX) || !fileName.endsWith(IDENTITY_SUFFIX)) {
28
+ return "";
29
+ }
30
+ return fileName.slice(IDENTITY_PREFIX.length, -IDENTITY_SUFFIX.length);
31
+ }
32
+ function describeIdentity(config) {
33
+ let { domain, stored } = config;
34
+ return {
35
+ machineId: (0, certs_1.getMachineId)(stored.domain, domain),
36
+ domain,
37
+ fullDomain: stored.domain,
38
+ fileName: identityFileName(domain),
39
+ };
40
+ }
41
+ /** Every domain this machine holds an identity under. A machine has one identity per domain, and
42
+ they are different machine ids, so which one is meant is a real question rather than a detail. */
43
+ async function localDomains() {
44
+ let domains = [];
45
+ for (let name of (await promises_1.default.readdir(os_1.default.homedir()).catch(() => [])).sort()) {
46
+ let domain = domainOfFileName(name);
47
+ if (domain) {
48
+ domains.push(domain);
49
+ }
50
+ }
51
+ return domains;
52
+ }
53
+ exports.localDomains = localDomains;
54
+ /** This machine's identity under one domain. Undefined when it has none under that one.
55
+
56
+ The domain is required everywhere it appears. A machine has a different identity, and so a
57
+ different machine id, under every domain it runs under, so "this machine" on its own does not
58
+ name anything: answering under the wrong one means answering as a machine we are not. */
59
+ async function localIdentity(domain) {
60
+ if (!domain) {
61
+ throw new Error(`Expected a domain, was ${JSON.stringify(domain)}`);
62
+ }
63
+ let contents = await promises_1.default.readFile(path_1.default.join(os_1.default.homedir(), identityFileName(domain)), "utf8").catch(() => "");
64
+ if (!contents) {
65
+ return undefined;
66
+ }
67
+ return describeIdentity({ domain, stored: JSON.parse(contents) });
68
+ }
69
+ exports.localIdentity = localIdentity;
70
+ /** This machine's identity and its keys, for signing. */
71
+ async function localIdentityKeys(domain) {
72
+ let identity = await localIdentity(domain);
73
+ if (!identity) {
74
+ throw new Error(`Expected an identity for ${domain} at ${path_1.default.join(os_1.default.homedir(), identityFileName(domain))},`
75
+ + ` there is none.\n`
76
+ + `This machine has: ${(await localDomains()).join(", ") || "no identities at all"}`);
77
+ }
78
+ let stored = JSON.parse(await promises_1.default.readFile(path_1.default.join(os_1.default.homedir(), identity.fileName), "utf8"));
79
+ return { identity, stored };
80
+ }
81
+ exports.localIdentityKeys = localIdentityKeys;
82
+ /** The identity of another machine under one domain, over ssh. Undefined when it has none under
83
+ that domain, whatever else it may have under others. */
84
+ async function remoteIdentity(host, domain) {
85
+ if (!domain) {
86
+ throw new Error(`Expected a domain, was ${JSON.stringify(domain)}`);
87
+ }
88
+ let home = (await (0, remoteSSH_1.runOverSSH)({ host, script: `echo $HOME` })).stdout.trim();
89
+ if (!home) {
90
+ throw new Error(`Expected ${host} to report a home directory, it reported nothing`);
91
+ }
92
+ let contents = await (0, remoteSSH_1.runOverSSH)({
93
+ host,
94
+ script: `cat ${home}/${identityFileName(domain)} 2>/dev/null || true`,
95
+ });
96
+ if (!contents.stdout.trim()) {
97
+ return undefined;
98
+ }
99
+ return describeIdentity({ domain, stored: JSON.parse(contents.stdout) });
100
+ }
101
+ exports.remoteIdentity = remoteIdentity;
102
+ /** Gives a machine an identity it does not have yet, by generating one here and writing it there.
103
+
104
+ The CA is self signed and never leaves that machine afterwards, so generating it here is only a
105
+ convenience: what matters is that the private key ends up in one place and the id is the hash
106
+ of its public half. */
107
+ async function createRemoteIdentity(config) {
108
+ let { host, domain } = config;
109
+ let generated = (0, certs_1.generateTestCA)(domain);
110
+ let stored = {
111
+ domain: generated.domain,
112
+ certB64: generated.cert.toString("base64"),
113
+ keyB64: generated.key.toString("base64"),
114
+ };
115
+ let home = (await (0, remoteSSH_1.runOverSSH)({ host, script: `echo $HOME` })).stdout.trim();
116
+ await (0, remoteSSH_1.writeRemoteFile)({
117
+ host,
118
+ filePath: `${home}/${identityFileName(domain)}`,
119
+ contents: JSON.stringify(stored),
120
+ fileMode: "600",
121
+ // The home directory is already there, this is only what it is created with if it is not.
122
+ directoryMode: "700",
123
+ });
124
+ return describeIdentity({ domain, stored });
125
+ }
126
+ exports.createRemoteIdentity = createRemoteIdentity;
127
+ /** The raw ed25519 public key out of a certificate, which is what the machine id is a hash of. */
128
+ function publicKeyBytes(certPEM) {
129
+ let spki = new crypto_1.default.X509Certificate(certPEM).publicKey.export({ type: "spki", format: "der" });
130
+ // An ed25519 SPKI is a fixed 12 byte header followed by the 32 byte key.
131
+ return spki.subarray(spki.length - 32);
132
+ }
133
+ /** Proves we hold the private key behind our machine id, right now. The signature covers the id
134
+ and the time, so the packet cannot be replayed later or reused for another id. */
135
+ async function signTrustPacket(domain) {
136
+ let { identity, stored } = await localIdentityKeys(domain);
137
+ let signedAt = Date.now();
138
+ let signature = crypto_1.default.sign(null, Buffer.from(`${identity.machineId} ${signedAt}`), crypto_1.default.createPrivateKey(Buffer.from(stored.keyB64, "base64").toString()));
139
+ return {
140
+ machineId: identity.machineId,
141
+ certB64: stored.certB64,
142
+ signedAt,
143
+ signatureB64: signature.toString("base64"),
144
+ };
145
+ }
146
+ exports.signTrustPacket = signTrustPacket;
147
+ /** Whether a packet really came from the machine it names. Says why when it did not, because
148
+ "this is not that machine" and "this arrived too late" are different problems. */
149
+ function verifyTrustPacket(packet) {
150
+ if (!packet || !packet.machineId || !packet.certB64 || !packet.signatureB64) {
151
+ return { valid: false, problem: "the packet is missing fields" };
152
+ }
153
+ if (Math.abs(Date.now() - packet.signedAt) > exports.PACKET_LIFETIME) {
154
+ return { valid: false, problem: `it was signed ${Math.round((Date.now() - packet.signedAt) / 1000)}s ago` };
155
+ }
156
+ let certPEM = Buffer.from(packet.certB64, "base64").toString();
157
+ let publicKey;
158
+ try {
159
+ publicKey = new crypto_1.default.X509Certificate(certPEM).publicKey;
160
+ }
161
+ catch (e) {
162
+ return { valid: false, problem: `its certificate could not be read, ${e}` };
163
+ }
164
+ // The id is a hash of the public key, so this is what stops a machine claiming another's id.
165
+ if (!(0, certs_1.verifyMachineIdForPublicKey)({ machineId: packet.machineId, publicKey: publicKeyBytes(certPEM) })) {
166
+ return { valid: false, problem: "the machine id does not belong to that certificate" };
167
+ }
168
+ let signed = crypto_1.default.verify(null, Buffer.from(`${packet.machineId} ${packet.signedAt}`), publicKey, Buffer.from(packet.signatureB64, "base64"));
169
+ if (!signed) {
170
+ return { valid: false, problem: "the signature does not check out" };
171
+ }
172
+ return { valid: true, problem: "" };
173
+ }
174
+ exports.verifyTrustPacket = verifyTrustPacket;
175
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWRlbnRpdHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpZGVudGl0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLDRDQUFvQjtBQUNwQixnREFBd0I7QUFDeEIsa0RBQTRJO0FBQzVJLG9EQUFtRTtBQUVuRSxpR0FBaUc7QUFDakcsK0ZBQStGO0FBQy9GLDhGQUE4RjtBQUM5RixtREFBbUQ7QUFDbkQsTUFBTSxlQUFlLEdBQUcsV0FBVyxDQUFDO0FBQ3BDLE1BQU0sZUFBZSxHQUFHLElBQUksMEJBQWtCLE9BQU8sQ0FBQztBQUN0RCxrR0FBa0c7QUFDbEcsaURBQWlEO0FBQ3BDLFFBQUEsZUFBZSxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBUzdDLFNBQVMsZ0JBQWdCLENBQUMsTUFBYztJQUNwQyxPQUFPLEdBQUcsZUFBZSxHQUFHLE1BQU0sR0FBRyxlQUFlLEVBQUUsQ0FBQztBQUMzRCxDQUFDO0FBRUQseUVBQXlFO0FBQ3pFLFNBQVMsZ0JBQWdCLENBQUMsUUFBZ0I7SUFDdEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7UUFDL0UsT0FBTyxFQUFFLENBQUM7SUFDZCxDQUFDO0lBQ0QsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDM0UsQ0FBQztBQUVELFNBQVMsZ0JBQWdCLENBQUMsTUFBdUQ7SUFDN0UsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLENBQUM7SUFDaEMsT0FBTztRQUNILFNBQVMsRUFBRSxJQUFBLG9CQUFZLEVBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUM7UUFDOUMsTUFBTTtRQUNO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LENBQUMsT0FBTyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQzlELENBQUM7SUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ1QsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLHNDQUFzQyxDQUFDLEVBQUUsRUFBRSxDQUFDO0lBQ2hGLENBQUM7SUFDRCw2RkFBNkY7SUFDN0YsSUFBSSxDQUFDLElBQUEsbUNBQTJCLEVBQUMsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLFNBQVMsRUFBRSxTQUFTLEVBQUUsY0FBYyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQ3BHLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxvREFBb0QsRUFBRSxDQUFDO0lBQzNGLENBQUM7SUFDRCxJQUFJLE1BQU0sR0FBRyxnQkFBTSxDQUFDLE1BQU0sQ0FDdEIsSUFBSSxFQUNKLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLENBQUMsU0FBUyxJQUFJLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxFQUNyRCxTQUFTLEVBQ1QsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLFFBQVEsQ0FBQyxDQUM3QyxDQUFDO0lBQ0YsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ1YsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLGtDQUFrQyxFQUFFLENBQUM7SUFDekUsQ0FBQztJQUNELE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsQ0FBQztBQUN4QyxDQUFDO0FBNUJELDhDQTRCQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjcnlwdG8gZnJvbSBcImNyeXB0b1wiO1xuaW1wb3J0IGZzIGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IG9zIGZyb20gXCJvc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IGdlbmVyYXRlVGVzdENBLCBnZXRNYWNoaW5lSWQsIGlkZW50aXR5U3RvcmFnZUtleSwgSWRlbnRpdHlTdG9yYWdlVHlwZSwgdmVyaWZ5TWFjaGluZUlkRm9yUHVibGljS2V5IH0gZnJvbSBcIi4uLy4uL21pc2MvaHR0cHMvY2VydHNcIjtcbmltcG9ydCB7IHJ1bk92ZXJTU0gsIHdyaXRlUmVtb3RlRmlsZSB9IGZyb20gXCIuLi9oZWxwZXJzL3JlbW90ZVNTSFwiO1xuXG4vLyBBIG1hY2hpbmUncyBpZGVudGl0eSBpcyB0aGUgQ0EgaXQgZ2VuZXJhdGVkIGZvciBpdHNlbGYsIGtlcHQgaW4gb25lIGZpbGUgaW4gdGhlIGhvbWUgZGlyZWN0b3J5XG4vLyBvZiB3aG9ldmVyIHJ1bnMgaXQuIFRoZSBuYW1lIGNhcnJpZXMgdGhlIGRvbWFpbiBpdCBiZWxvbmdzIHRvLCBzbyBhIG1hY2hpbmUgY2FuIGhvbGQgb25lIHBlclxuLy8gZG9tYWluLCBhbmQgdGhlIGlkIGl0c2VsZiBpcyBhIGhhc2ggb2YgdGhhdCBDQSdzIHB1YmxpYyBrZXkgLSB3aGljaCBpcyB3aHkgYSBtYWNoaW5lIGNhbm5vdFxuLy8gY2xhaW0gdG8gYmUgYW5vdGhlciBvbmUgd2l0aG91dCBob2xkaW5nIGl0cyBrZXkuXG5jb25zdCBJREVOVElUWV9QUkVGSVggPSBcImtleXN0b3JlX1wiO1xuY29uc3QgSURFTlRJVFlfU1VGRklYID0gYF8ke2lkZW50aXR5U3RvcmFnZUtleX0uanNvbmA7XG4vLyBIb3cgc3RhbGUgYSBzaWduZWQgcGFja2V0IG1heSBiZS4gTG9uZyBlbm91Z2ggZm9yIGNsb2NrIHNrZXcgYmV0d2VlbiB0d28gbWFjaGluZXMsIHNob3J0IGVub3VnaFxuLy8gdGhhdCBhIGNhcHR1cmVkIHBhY2tldCBpcyBub3Qgd29ydGggcmVwbGF5aW5nLlxuZXhwb3J0IGNvbnN0IFBBQ0tFVF9MSUZFVElNRSA9IDIgKiA2MCAqIDEwMDA7XG5cbmV4cG9ydCB0eXBlIElkZW50aXR5ID0ge1xuICAgIG1hY2hpbmVJZDogc3RyaW5nO1xuICAgIGRvbWFpbjogc3RyaW5nO1xuICAgIGZ1bGxEb21haW46IHN0cmluZztcbiAgICBmaWxlTmFtZTogc3RyaW5nO1xufTtcblxuZnVuY3Rpb24gaWRlbnRpdHlGaWxlTmFtZShkb21haW46IHN0cmluZykge1xuICAgIHJldHVybiBgJHtJREVOVElUWV9QUkVGSVh9JHtkb21haW59JHtJREVOVElUWV9TVUZGSVh9YDtcbn1cblxuLyoqIFRoZSBkb21haW4gb3V0IG9mIGFuIGlkZW50aXR5IGZpbGUncyBuYW1lLCBvciBcIlwiIGlmIGl0IGlzIG5vdCBvbmUuICovXG5mdW5jdGlvbiBkb21haW5PZkZpbGVOYW1lKGZpbGVOYW1lOiBzdHJpbmcpIHtcbiAgICBpZiAoIWZpbGVOYW1lLnN0YXJ0c1dpdGgoSURFTlRJVFlfUFJFRklYKSB8fCAhZmlsZU5hbWUuZW5kc1dpdGgoSURFTlRJVFlfU1VGRklYKSkge1xuICAgICAgICByZXR1cm4gXCJcIjtcbiAgICB9XG4gICAgcmV0dXJuIGZpbGVOYW1lLnNsaWNlKElERU5USVRZX1BSRUZJWC5sZW5ndGgsIC1JREVOVElUWV9TVUZGSVgubGVuZ3RoKTtcbn1cblxuZnVuY3Rpb24gZGVzY3JpYmVJZGVudGl0eShjb25maWc6IHsgZG9tYWluOiBzdHJpbmc7IHN0b3JlZDogSWRlbnRpdHlTdG9yYWdlVHlwZSB9KSB7XG4gICAgbGV0IHsgZG9tYWluLCBzdG9yZWQgfSA9IGNvbmZpZztcbiAgICByZXR1cm4ge1xuICAgICAgICBtYWNoaW5lSWQ6IGdldE1hY2hpbmVJZChzdG9yZWQuZG9tYWluLCBkb21haW4pLFxuICAgICAgICBkb21haW4sXG4gICAgICAgIGZ1bGxEb21haW46IHN0b3JlZC5kb21haW4sXG4gICAgICAgIGZpbGVOYW1lOiBpZGVudGl0eUZpbGVOYW1lKGRvbWFpbiksXG4gICAgfTtcbn1cblxuLyoqIEV2ZXJ5IGRvbWFpbiB0aGlzIG1hY2hpbmUgaG9sZHMgYW4gaWRlbnRpdHkgdW5kZXIuIEEgbWFjaGluZSBoYXMgb25lIGlkZW50aXR5IHBlciBkb21haW4sIGFuZFxuICAgIHRoZXkgYXJlIGRpZmZlcmVudCBtYWNoaW5lIGlkcywgc28gd2hpY2ggb25lIGlzIG1lYW50IGlzIGEgcmVhbCBxdWVzdGlvbiByYXRoZXIgdGhhbiBhIGRldGFpbC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2NhbERvbWFpbnMoKSB7XG4gICAgbGV0IGRvbWFpbnM6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChsZXQgbmFtZSBvZiAoYXdhaXQgZnMucmVhZGRpcihvcy5ob21lZGlyKCkpLmNhdGNoKCgpID0+IFtdIGFzIHN0cmluZ1tdKSkuc29ydCgpKSB7XG4gICAgICAgIGxldCBkb21haW4gPSBkb21haW5PZkZpbGVOYW1lKG5hbWUpO1xuICAgICAgICBpZiAoZG9tYWluKSB7XG4gICAgICAgICAgICBkb21haW5zLnB1c2goZG9tYWluKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZG9tYWlucztcbn1cblxuLyoqIFRoaXMgbWFjaGluZSdzIGlkZW50aXR5IHVuZGVyIG9uZSBkb21haW4uIFVuZGVmaW5lZCB3aGVuIGl0IGhhcyBub25lIHVuZGVyIHRoYXQgb25lLlxuXG4gICAgVGhlIGRvbWFpbiBpcyByZXF1aXJlZCBldmVyeXdoZXJlIGl0IGFwcGVhcnMuIEEgbWFjaGluZSBoYXMgYSBkaWZmZXJlbnQgaWRlbnRpdHksIGFuZCBzbyBhXG4gICAgZGlmZmVyZW50IG1hY2hpbmUgaWQsIHVuZGVyIGV2ZXJ5IGRvbWFpbiBpdCBydW5zIHVuZGVyLCBzbyBcInRoaXMgbWFjaGluZVwiIG9uIGl0cyBvd24gZG9lcyBub3RcbiAgICBuYW1lIGFueXRoaW5nOiBhbnN3ZXJpbmcgdW5kZXIgdGhlIHdyb25nIG9uZSBtZWFucyBhbnN3ZXJpbmcgYXMgYSBtYWNoaW5lIHdlIGFyZSBub3QuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9jYWxJZGVudGl0eShkb21haW46IHN0cmluZyk6IFByb21pc2U8SWRlbnRpdHkgfCB1bmRlZmluZWQ+IHtcbiAgICBpZiAoIWRvbWFpbikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGEgZG9tYWluLCB3YXMgJHtKU09OLnN0cmluZ2lmeShkb21haW4pfWApO1xuICAgIH1cbiAgICBsZXQgY29udGVudHMgPSBhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4ob3MuaG9tZWRpcigpLCBpZGVudGl0eUZpbGVOYW1lKGRvbWFpbikpLCBcInV0ZjhcIikuY2F0Y2goKCkgPT4gXCJcIik7XG4gICAgaWYgKCFjb250ZW50cykge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICByZXR1cm4gZGVzY3JpYmVJZGVudGl0eSh7IGRvbWFpbiwgc3RvcmVkOiBKU09OLnBhcnNlKGNvbnRlbnRzKSB9KTtcbn1cblxuLyoqIFRoaXMgbWFjaGluZSdzIGlkZW50aXR5IGFuZCBpdHMga2V5cywgZm9yIHNpZ25pbmcuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9jYWxJZGVudGl0eUtleXMoZG9tYWluOiBzdHJpbmcpIHtcbiAgICBsZXQgaWRlbnRpdHkgPSBhd2FpdCBsb2NhbElkZW50aXR5KGRvbWFpbik7XG4gICAgaWYgKCFpZGVudGl0eSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgYW4gaWRlbnRpdHkgZm9yICR7ZG9tYWlufSBhdCAke3BhdGguam9pbihvcy5ob21lZGlyKCksIGlkZW50aXR5RmlsZU5hbWUoZG9tYWluKSl9LGBcbiAgICAgICAgICAgICsgYCB0aGVyZSBpcyBub25lLlxcbmBcbiAgICAgICAgICAgICsgYFRoaXMgbWFjaGluZSBoYXM6ICR7KGF3YWl0IGxvY2FsRG9tYWlucygpKS5qb2luKFwiLCBcIikgfHwgXCJubyBpZGVudGl0aWVzIGF0IGFsbFwifWBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgbGV0IHN0b3JlZDogSWRlbnRpdHlTdG9yYWdlVHlwZSA9IEpTT04ucGFyc2UoXG4gICAgICAgIGF3YWl0IGZzLnJlYWRGaWxlKHBhdGguam9pbihvcy5ob21lZGlyKCksIGlkZW50aXR5LmZpbGVOYW1lKSwgXCJ1dGY4XCIpXG4gICAgKTtcbiAgICByZXR1cm4geyBpZGVudGl0eSwgc3RvcmVkIH07XG59XG5cbi8qKiBUaGUgaWRlbnRpdHkgb2YgYW5vdGhlciBtYWNoaW5lIHVuZGVyIG9uZSBkb21haW4sIG92ZXIgc3NoLiBVbmRlZmluZWQgd2hlbiBpdCBoYXMgbm9uZSB1bmRlclxuICAgIHRoYXQgZG9tYWluLCB3aGF0ZXZlciBlbHNlIGl0IG1heSBoYXZlIHVuZGVyIG90aGVycy4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW1vdGVJZGVudGl0eShob3N0OiBzdHJpbmcsIGRvbWFpbjogc3RyaW5nKTogUHJvbWlzZTxJZGVudGl0eSB8IHVuZGVmaW5lZD4ge1xuICAgIGlmICghZG9tYWluKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSBkb21haW4sIHdhcyAke0pTT04uc3RyaW5naWZ5KGRvbWFpbil9YCk7XG4gICAgfVxuICAgIGxldCBob21lID0gKGF3YWl0IHJ1bk92ZXJTU0goeyBob3N0LCBzY3JpcHQ6IGBlY2hvICRIT01FYCB9KSkuc3Rkb3V0LnRyaW0oKTtcbiAgICBpZiAoIWhvbWUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCAke2hvc3R9IHRvIHJlcG9ydCBhIGhvbWUgZGlyZWN0b3J5LCBpdCByZXBvcnRlZCBub3RoaW5nYCk7XG4gICAgfVxuICAgIGxldCBjb250ZW50cyA9IGF3YWl0IHJ1bk92ZXJTU0goe1xuICAgICAgICBob3N0LFxuICAgICAgICBzY3JpcHQ6IGBjYXQgJHtob21lfS8ke2lkZW50aXR5RmlsZU5hbWUoZG9tYWluKX0gMj4vZGV2L251bGwgfHwgdHJ1ZWAsXG4gICAgfSk7XG4gICAgaWYgKCFjb250ZW50cy5zdGRvdXQudHJpbSgpKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiBkZXNjcmliZUlkZW50aXR5KHsgZG9tYWluLCBzdG9yZWQ6IEpTT04ucGFyc2UoY29udGVudHMuc3Rkb3V0KSB9KTtcbn1cblxuLyoqIEdpdmVzIGEgbWFjaGluZSBhbiBpZGVudGl0eSBpdCBkb2VzIG5vdCBoYXZlIHlldCwgYnkgZ2VuZXJhdGluZyBvbmUgaGVyZSBhbmQgd3JpdGluZyBpdCB0aGVyZS5cblxuICAgIFRoZSBDQSBpcyBzZWxmIHNpZ25lZCBhbmQgbmV2ZXIgbGVhdmVzIHRoYXQgbWFjaGluZSBhZnRlcndhcmRzLCBzbyBnZW5lcmF0aW5nIGl0IGhlcmUgaXMgb25seSBhXG4gICAgY29udmVuaWVuY2U6IHdoYXQgbWF0dGVycyBpcyB0aGF0IHRoZSBwcml2YXRlIGtleSBlbmRzIHVwIGluIG9uZSBwbGFjZSBhbmQgdGhlIGlkIGlzIHRoZSBoYXNoXG4gICAgb2YgaXRzIHB1YmxpYyBoYWxmLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZVJlbW90ZUlkZW50aXR5KGNvbmZpZzogeyBob3N0OiBzdHJpbmc7IGRvbWFpbjogc3RyaW5nIH0pIHtcbiAgICBsZXQgeyBob3N0LCBkb21haW4gfSA9IGNvbmZpZztcbiAgICBsZXQgZ2VuZXJhdGVkID0gZ2VuZXJhdGVUZXN0Q0EoZG9tYWluKTtcbiAgICBsZXQgc3RvcmVkOiBJZGVudGl0eVN0b3JhZ2VUeXBlID0ge1xuICAgICAgICBkb21haW46IGdlbmVyYXRlZC5kb21haW4sXG4gICAgICAgIGNlcnRCNjQ6IGdlbmVyYXRlZC5jZXJ0LnRvU3RyaW5nKFwiYmFzZTY0XCIpLFxuICAgICAgICBrZXlCNjQ6IGdlbmVyYXRlZC5rZXkudG9TdHJpbmcoXCJiYXNlNjRcIiksXG4gICAgfTtcbiAgICBsZXQgaG9tZSA9IChhd2FpdCBydW5PdmVyU1NIKHsgaG9zdCwgc2NyaXB0OiBgZWNobyAkSE9NRWAgfSkpLnN0ZG91dC50cmltKCk7XG4gICAgYXdhaXQgd3JpdGVSZW1vdGVGaWxlKHtcbiAgICAgICAgaG9zdCxcbiAgICAgICAgZmlsZVBhdGg6IGAke2hvbWV9LyR7aWRlbnRpdHlGaWxlTmFtZShkb21haW4pfWAsXG4gICAgICAgIGNvbnRlbnRzOiBKU09OLnN0cmluZ2lmeShzdG9yZWQpLFxuICAgICAgICBmaWxlTW9kZTogXCI2MDBcIixcbiAgICAgICAgLy8gVGhlIGhvbWUgZGlyZWN0b3J5IGlzIGFscmVhZHkgdGhlcmUsIHRoaXMgaXMgb25seSB3aGF0IGl0IGlzIGNyZWF0ZWQgd2l0aCBpZiBpdCBpcyBub3QuXG4gICAgICAgIGRpcmVjdG9yeU1vZGU6IFwiNzAwXCIsXG4gICAgfSk7XG4gICAgcmV0dXJuIGRlc2NyaWJlSWRlbnRpdHkoeyBkb21haW4sIHN0b3JlZCB9KTtcbn1cblxuLyoqIFRoZSByYXcgZWQyNTUxOSBwdWJsaWMga2V5IG91dCBvZiBhIGNlcnRpZmljYXRlLCB3aGljaCBpcyB3aGF0IHRoZSBtYWNoaW5lIGlkIGlzIGEgaGFzaCBvZi4gKi9cbmZ1bmN0aW9uIHB1YmxpY0tleUJ5dGVzKGNlcnRQRU06IHN0cmluZykge1xuICAgIGxldCBzcGtpID0gbmV3IGNyeXB0by5YNTA5Q2VydGlmaWNhdGUoY2VydFBFTSkucHVibGljS2V5LmV4cG9ydCh7IHR5cGU6IFwic3BraVwiLCBmb3JtYXQ6IFwiZGVyXCIgfSk7XG4gICAgLy8gQW4gZWQyNTUxOSBTUEtJIGlzIGEgZml4ZWQgMTIgYnl0ZSBoZWFkZXIgZm9sbG93ZWQgYnkgdGhlIDMyIGJ5dGUga2V5LlxuICAgIHJldHVybiBzcGtpLnN1YmFycmF5KHNwa2kubGVuZ3RoIC0gMzIpO1xufVxuXG5leHBvcnQgdHlwZSBUcnVzdFBhY2tldCA9IHtcbiAgICBtYWNoaW5lSWQ6IHN0cmluZztcbiAgICBjZXJ0QjY0OiBzdHJpbmc7XG4gICAgc2lnbmVkQXQ6IG51bWJlcjtcbiAgICBzaWduYXR1cmVCNjQ6IHN0cmluZztcbn07XG5cbi8qKiBQcm92ZXMgd2UgaG9sZCB0aGUgcHJpdmF0ZSBrZXkgYmVoaW5kIG91ciBtYWNoaW5lIGlkLCByaWdodCBub3cuIFRoZSBzaWduYXR1cmUgY292ZXJzIHRoZSBpZFxuICAgIGFuZCB0aGUgdGltZSwgc28gdGhlIHBhY2tldCBjYW5ub3QgYmUgcmVwbGF5ZWQgbGF0ZXIgb3IgcmV1c2VkIGZvciBhbm90aGVyIGlkLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpZ25UcnVzdFBhY2tldChkb21haW46IHN0cmluZyk6IFByb21pc2U8VHJ1c3RQYWNrZXQ+IHtcbiAgICBsZXQgeyBpZGVudGl0eSwgc3RvcmVkIH0gPSBhd2FpdCBsb2NhbElkZW50aXR5S2V5cyhkb21haW4pO1xuICAgIGxldCBzaWduZWRBdCA9IERhdGUubm93KCk7XG4gICAgbGV0IHNpZ25hdHVyZSA9IGNyeXB0by5zaWduKFxuICAgICAgICBudWxsLFxuICAgICAgICBCdWZmZXIuZnJvbShgJHtpZGVudGl0eS5tYWNoaW5lSWR9ICR7c2lnbmVkQXR9YCksXG4gICAgICAgIGNyeXB0by5jcmVhdGVQcml2YXRlS2V5KEJ1ZmZlci5mcm9tKHN0b3JlZC5rZXlCNjQsIFwiYmFzZTY0XCIpLnRvU3RyaW5nKCkpXG4gICAgKTtcbiAgICByZXR1cm4ge1xuICAgICAgICBtYWNoaW5lSWQ6IGlkZW50aXR5Lm1hY2hpbmVJZCxcbiAgICAgICAgY2VydEI2NDogc3RvcmVkLmNlcnRCNjQsXG4gICAgICAgIHNpZ25lZEF0LFxuICAgICAgICBzaWduYXR1cmVCNjQ6IHNpZ25hdHVyZS50b1N0cmluZyhcImJhc2U2NFwiKSxcbiAgICB9O1xufVxuXG4vKiogV2hldGhlciBhIHBhY2tldCByZWFsbHkgY2FtZSBmcm9tIHRoZSBtYWNoaW5lIGl0IG5hbWVzLiBTYXlzIHdoeSB3aGVuIGl0IGRpZCBub3QsIGJlY2F1c2VcbiAgICBcInRoaXMgaXMgbm90IHRoYXQgbWFjaGluZVwiIGFuZCBcInRoaXMgYXJyaXZlZCB0b28gbGF0ZVwiIGFyZSBkaWZmZXJlbnQgcHJvYmxlbXMuICovXG5leHBvcnQgZnVuY3Rpb24gdmVyaWZ5VHJ1c3RQYWNrZXQocGFja2V0OiBUcnVzdFBhY2tldCkge1xuICAgIGlmICghcGFja2V0IHx8ICFwYWNrZXQubWFjaGluZUlkIHx8ICFwYWNrZXQuY2VydEI2NCB8fCAhcGFja2V0LnNpZ25hdHVyZUI2NCkge1xuICAgICAgICByZXR1cm4geyB2YWxpZDogZmFsc2UsIHByb2JsZW06IFwidGhlIHBhY2tldCBpcyBtaXNzaW5nIGZpZWxkc1wiIH07XG4gICAgfVxuICAgIGlmIChNYXRoLmFicyhEYXRlLm5vdygpIC0gcGFja2V0LnNpZ25lZEF0KSA+IFBBQ0tFVF9MSUZFVElNRSkge1xuICAgICAgICByZXR1cm4geyB2YWxpZDogZmFsc2UsIHByb2JsZW06IGBpdCB3YXMgc2lnbmVkICR7TWF0aC5yb3VuZCgoRGF0ZS5ub3coKSAtIHBhY2tldC5zaWduZWRBdCkgLyAxMDAwKX1zIGFnb2AgfTtcbiAgICB9XG4gICAgbGV0IGNlcnRQRU0gPSBCdWZmZXIuZnJvbShwYWNrZXQuY2VydEI2NCwgXCJiYXNlNjRcIikudG9TdHJpbmcoKTtcbiAgICBsZXQgcHVibGljS2V5OiBjcnlwdG8uS2V5T2JqZWN0O1xuICAgIHRyeSB7XG4gICAgICAgIHB1YmxpY0tleSA9IG5ldyBjcnlwdG8uWDUwOUNlcnRpZmljYXRlKGNlcnRQRU0pLnB1YmxpY0tleTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgcHJvYmxlbTogYGl0cyBjZXJ0aWZpY2F0ZSBjb3VsZCBub3QgYmUgcmVhZCwgJHtlfWAgfTtcbiAgICB9XG4gICAgLy8gVGhlIGlkIGlzIGEgaGFzaCBvZiB0aGUgcHVibGljIGtleSwgc28gdGhpcyBpcyB3aGF0IHN0b3BzIGEgbWFjaGluZSBjbGFpbWluZyBhbm90aGVyJ3MgaWQuXG4gICAgaWYgKCF2ZXJpZnlNYWNoaW5lSWRGb3JQdWJsaWNLZXkoeyBtYWNoaW5lSWQ6IHBhY2tldC5tYWNoaW5lSWQsIHB1YmxpY0tleTogcHVibGljS2V5Qnl0ZXMoY2VydFBFTSkgfSkpIHtcbiAgICAgICAgcmV0dXJuIHsgdmFsaWQ6IGZhbHNlLCBwcm9ibGVtOiBcInRoZSBtYWNoaW5lIGlkIGRvZXMgbm90IGJlbG9uZyB0byB0aGF0IGNlcnRpZmljYXRlXCIgfTtcbiAgICB9XG4gICAgbGV0IHNpZ25lZCA9IGNyeXB0by52ZXJpZnkoXG4gICAgICAgIG51bGwsXG4gICAgICAgIEJ1ZmZlci5mcm9tKGAke3BhY2tldC5tYWNoaW5lSWR9ICR7cGFja2V0LnNpZ25lZEF0fWApLFxuICAgICAgICBwdWJsaWNLZXksXG4gICAgICAgIEJ1ZmZlci5mcm9tKHBhY2tldC5zaWduYXR1cmVCNjQsIFwiYmFzZTY0XCIpXG4gICAgKTtcbiAgICBpZiAoIXNpZ25lZCkge1xuICAgICAgICByZXR1cm4geyB2YWxpZDogZmFsc2UsIHByb2JsZW06IFwidGhlIHNpZ25hdHVyZSBkb2VzIG5vdCBjaGVjayBvdXRcIiB9O1xuICAgIH1cbiAgICByZXR1cm4geyB2YWxpZDogdHJ1ZSwgcHJvYmxlbTogXCJcIiB9O1xufVxuIl19
176
+ /* _JS_SOURCE_HASH = "2b16a13142ed08ef2e755cf33ce9a940aaef44261d0b1bdd42b8efe807f54df2"; */
@@ -0,0 +1,322 @@
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.isMachineAccepted = exports.resolveKeysRepo = exports.machineState = exports.MACHINES_DIR = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const paths_1 = require("../authorizedKeys/daemon/paths");
11
+ const repoFiles_1 = require("../authorizedKeys/daemon/repoFiles");
12
+ const repoFiles_2 = require("../authorizedKeys/daemon/repoFiles");
13
+ const revocation_1 = require("../authorizedKeys/daemon/revocation");
14
+ const git_1 = require("../authorizedKeys/daemon/git");
15
+ const repoFiles_3 = require("../authorizedKeys/daemon/repoFiles");
16
+ const trust_1 = require("../authorizedKeys/daemon/trust");
17
+ const revokeSource_1 = require("../authorizedKeys/revokeSource");
18
+ const sources_1 = require("../authorizedKeys/sources");
19
+ const notify_1 = require("../authorizedKeys/daemon/notify");
20
+ const discord_1 = require("../notifications/discord");
21
+ const trustState_1 = require("./trustState");
22
+ const spawn_1 = require("../helpers/spawn");
23
+ // Which machines this system talks to, kept in the same repo as the ssh keys. That repo is already
24
+ // signed, already distributed to every machine, and already has somewhere to record a rejection,
25
+ // so a second one would only be a second thing to keep in step.
26
+ exports.MACHINES_DIR = "machines";
27
+ const REVOCATIONS_DIR = "revocations";
28
+ const REVOCATION_REASON = "a machine talked to us from an unapproved IP";
29
+ // The revoke repo is pulled at most this often. A check happens per request, and a request must
30
+ // not cost a round trip to github.
31
+ const REVOKE_SYNC_INTERVAL = 60 * 1000;
32
+ // Where a Windows machine is expected to keep the repo, relative to the working directory. There
33
+ // is no daemon there to ask, and no /etc to look in.
34
+ const WINDOWS_REPO_PATH = "../authorized_keys";
35
+ async function pathExists(filePath) {
36
+ try {
37
+ await promises_1.default.access(filePath);
38
+ return true;
39
+ }
40
+ catch (e) {
41
+ return false;
42
+ }
43
+ }
44
+ function machineFilePath(repoPath, machineId) {
45
+ return path_1.default.join(repoPath, exports.MACHINES_DIR, `${machineId}.json`);
46
+ }
47
+ /** Reads every machine a checkout lists, or sets them.
48
+
49
+ Passing `machines` makes the repo match it exactly: machines named are written with the
50
+ addresses given, and machines not named are removed. That is why the addresses are part of
51
+ setting rather than a separate step - a machine with no address it may talk from is not a
52
+ machine we would accept anyway, so there is no state where naming one without them means
53
+ anything. Read first, change what you want, write the result.
54
+
55
+ Nothing here signs or commits anything. The signature is what every other machine checks before
56
+ believing any of this, and it takes the hardware key, so `yarn signfiles git` is still yours to
57
+ run afterwards. */
58
+ async function machineState(config) {
59
+ var _a;
60
+ let { repoPath, machines } = config;
61
+ let existing = await readMachines(repoPath);
62
+ if (!machines) {
63
+ return [...existing.values()];
64
+ }
65
+ let directory = path_1.default.join(repoPath, exports.MACHINES_DIR);
66
+ let written = [];
67
+ for (let machine of machines) {
68
+ if (!machine.machineId) {
69
+ throw new Error(`Expected a machineId, was ${JSON.stringify(machine.machineId)}`);
70
+ }
71
+ if (!machine.ips.length) {
72
+ throw new Error(`Expected addresses for ${machine.machineId}, was none. A machine is trusted from`
73
+ + ` the addresses it may talk from, so leave it out of the list to remove it.`);
74
+ }
75
+ let ips = machine.ips.filter((ip, index) => ip && machine.ips.indexOf(ip) === index);
76
+ let state = {
77
+ machineId: machine.machineId,
78
+ // In the order given, without duplicates, so the file reads the way it was set.
79
+ ips,
80
+ addedAt: ((_a = existing.get(machine.machineId)) === null || _a === void 0 ? void 0 : _a.addedAt) || new Date().toISOString(),
81
+ };
82
+ await promises_1.default.mkdir(directory, { recursive: true });
83
+ await promises_1.default.writeFile(machineFilePath(repoPath, state.machineId), JSON.stringify(state, undefined, 4) + "\n");
84
+ written.push(state);
85
+ }
86
+ // Whatever the repo had and the caller did not name is no longer trusted.
87
+ for (let machineId of existing.keys()) {
88
+ if (!machines.some(machine => machine.machineId === machineId)) {
89
+ await promises_1.default.rm(machineFilePath(repoPath, machineId), { force: true });
90
+ }
91
+ }
92
+ return written;
93
+ }
94
+ exports.machineState = machineState;
95
+ /** Every machine the repo lists. */
96
+ async function readMachines(repoPath) {
97
+ let machines = new Map();
98
+ let directory = path_1.default.join(repoPath, exports.MACHINES_DIR);
99
+ let names = await promises_1.default.readdir(directory).catch(() => []);
100
+ for (let name of names.sort()) {
101
+ if (!name.endsWith(".json")) {
102
+ continue;
103
+ }
104
+ try {
105
+ let parsed = JSON.parse(await promises_1.default.readFile(path_1.default.join(directory, name), "utf8"));
106
+ let machineId = parsed.machineId || name.replace(/\.json$/, "");
107
+ machines.set(machineId, { machineId, ips: parsed.ips || [], addedAt: parsed.addedAt || "" });
108
+ }
109
+ catch (e) {
110
+ console.log(`Ignoring unreadable machine file ${name}. ${e}`);
111
+ }
112
+ }
113
+ return machines;
114
+ }
115
+ async function isKeysRepo(repoPath) {
116
+ return await pathExists(path_1.default.join(repoPath, ".git"))
117
+ && await pathExists(path_1.default.join(repoPath, "authorized_keys"));
118
+ }
119
+ async function originOf(repoPath) {
120
+ let origin = await (0, spawn_1.spawnPromise)({ command: "git", args: ["remote", "get-url", "origin"], cwd: repoPath });
121
+ let url = origin.stdout.trim();
122
+ if (origin.status !== 0 || !url) {
123
+ throw new Error(`Expected ${repoPath} to have an origin remote, it has none`);
124
+ }
125
+ return url;
126
+ }
127
+ /** The keys repo this machine answers from, and the one anything editing the machine list edits.
128
+
129
+ On a host, that is the checkout the daemon already keeps up to date. On Windows there is no
130
+ daemon and no /etc, so the repo is expected beside the working directory, which is where a
131
+ developer working on both would have it.
132
+
133
+ Never the directory the command happens to be run from. Which repo this machine trusts is a
134
+ property of the machine, not of where somebody was standing when they typed something. */
135
+ async function resolveKeysRepo() {
136
+ if (os_1.default.platform() === "win32") {
137
+ let repoPath = path_1.default.resolve(WINDOWS_REPO_PATH);
138
+ if (!await isKeysRepo(repoPath)) {
139
+ throw new Error(`Expected ${repoPath} to be an authorized_keys repo, it is not.\n`
140
+ + `On Windows the repo is read from there, so clone it beside this one:\n`
141
+ + ` git clone <your authorized_keys repo> ${repoPath}`);
142
+ }
143
+ return { repoPath, sourceURL: await originOf(repoPath) };
144
+ }
145
+ let config = await promises_1.default.readFile(paths_1.CONFIG_PATH, "utf8").catch(() => "");
146
+ let sourceURL = config && (JSON.parse(config).repoSources || [])[0] || "";
147
+ if (!sourceURL) {
148
+ throw new Error(`Expected this machine to be set up with an authorized_keys repo, ${paths_1.CONFIG_PATH} names none.\n`
149
+ + `Set it up first:\n`
150
+ + ` yarn setupnotify <discord-webhook-url>\n`
151
+ + ` yarn securessh add <repo-private-key> <repo-url>`);
152
+ }
153
+ let repoPath = (0, sources_1.sourceRepoPath)(sourceURL);
154
+ if (!await isKeysRepo(repoPath)) {
155
+ throw new Error(`Expected a checkout of ${sourceURL} at ${repoPath}, there is none.\n`
156
+ + `Run \`yarn securessh update\` to put it back.`);
157
+ }
158
+ return { repoPath, sourceURL };
159
+ }
160
+ exports.resolveKeysRepo = resolveKeysRepo;
161
+ let lastRevokeSync = 0;
162
+ /** Pulled at most once a REVOKE_SYNC_INTERVAL, because this is asked per request. A sync that
163
+ fails leaves the checkout we already have, which is the safe direction: revocations we know
164
+ about stay known. */
165
+ async function syncRevocations(sourceURL) {
166
+ if (Date.now() - lastRevokeSync < REVOKE_SYNC_INTERVAL) {
167
+ return;
168
+ }
169
+ lastRevokeSync = Date.now();
170
+ try {
171
+ await (0, repoFiles_1.syncRepoFiles)((0, repoFiles_1.revokeRepo)(sourceURL));
172
+ }
173
+ catch (e) {
174
+ console.log(`Could not read ${(0, revokeSource_1.revokeRepoURL)(sourceURL)}, using the revocations already here. ${e}`);
175
+ }
176
+ }
177
+ /** Machine revocations, read out of the revoke repo the same way key revocations are. */
178
+ async function readMachineRevocations(sourceURL) {
179
+ let repo = (0, repoFiles_1.revokeRepo)(sourceURL);
180
+ let revocations = [];
181
+ for (let name of await (0, repoFiles_2.listRepoDir)(repo, REVOCATIONS_DIR)) {
182
+ if (!name.endsWith(".json")) {
183
+ continue;
184
+ }
185
+ try {
186
+ let parsed = JSON.parse(await (0, repoFiles_2.readRepoFile)(repo, path_1.default.join(REVOCATIONS_DIR, name)) || "");
187
+ if (parsed.machineId) {
188
+ revocations.push({
189
+ revocationId: parsed.revocationId || name.replace(/\.json$/, ""),
190
+ machineId: parsed.machineId,
191
+ ip: parsed.ip || "",
192
+ });
193
+ }
194
+ }
195
+ catch (e) {
196
+ console.log(`Ignoring unreadable revocation ${name}. ${e}`);
197
+ }
198
+ }
199
+ return revocations;
200
+ }
201
+ /** Sends the one notification this file is allowed to send, when it can.
202
+
203
+ The daemon configures notifications on startup and would simply send. This can also run in some
204
+ other process, which has not, so the webhook is picked up here if it is readable. A machine
205
+ with no webhook set up still records the revocation - being unable to tell anyone is not a
206
+ reason to keep accepting a machine that is being misused. */
207
+ async function notifyBestEffort(headline, body) {
208
+ try {
209
+ if (!(0, discord_1.areDiscordNotificationsConfigured)()) {
210
+ // Read first: configureDiscordNotifications exits the process when the file is missing,
211
+ // which is right for the daemon at startup and wrong for a library call.
212
+ await promises_1.default.readFile(discord_1.DEFAULT_WEBHOOK_FILE_PATH, "utf8");
213
+ await (0, discord_1.configureDiscordNotifications)({ filePath: discord_1.DEFAULT_WEBHOOK_FILE_PATH });
214
+ }
215
+ await (0, notify_1.notify)(headline, body);
216
+ }
217
+ catch (e) {
218
+ console.log(`Could not send a notification about this, it is only in the log. ${e}`);
219
+ }
220
+ }
221
+ /** Records that a machine we accept talked to us from an address it is not allowed from. One per
222
+ machine and address, so a second address is a second revocation and an unrevoke of the first
223
+ does not cover it. */
224
+ async function recordMachineRevocation(config) {
225
+ let { sourceURL, machineId, ip, hostLabel } = config;
226
+ let repoPath = (0, revokeSource_1.revokeRepoPath)(sourceURL);
227
+ let keyPath = await (0, repoFiles_3.ensureRevokeKey)(sourceURL);
228
+ let revocationId = (0, revocation_1.newRevocationId)(machineId);
229
+ let directory = path_1.default.join(repoPath, REVOCATIONS_DIR);
230
+ await promises_1.default.mkdir(directory, { recursive: true });
231
+ await promises_1.default.writeFile(path_1.default.join(directory, `${revocationId}.json`), JSON.stringify({
232
+ revocationId,
233
+ machineId,
234
+ ip,
235
+ revokedAt: new Date().toISOString(),
236
+ revokedBy: hostLabel,
237
+ reason: REVOCATION_REASON,
238
+ }, undefined, 4) + "\n");
239
+ await (0, git_1.runGit)({ args: ["add", "-A"], cwd: repoPath, keyPath });
240
+ await (0, git_1.runGit)({
241
+ args: ["-c", "user.email=portsecure@localhost", "-c", "user.name=portsecure", "commit", "-m", `revoke ${revocationId}`],
242
+ cwd: repoPath, keyPath,
243
+ });
244
+ let push = await (0, git_1.runGit)({ args: ["push", "origin", "HEAD"], cwd: repoPath, keyPath, allowFailure: true });
245
+ if (push.status !== 0) {
246
+ // Another machine most likely recorded the same thing first, and the next read picks it up.
247
+ console.log(`Could not push the revocation of ${machineId} from ${ip}. ${(push.stdout + push.stderr).trim()}`);
248
+ return;
249
+ }
250
+ console.log(`Revoked ${machineId} from ${ip}, ${revocationId}`);
251
+ // Said by whoever wrote the revocation, once, the same as for an ssh key. Machines that only
252
+ // read it later say nothing, or one event would be reported by every machine that saw it.
253
+ await notifyBestEffort(`SUSPICIOUS IP ${ip} FROZE MACHINE ${machineId}`, `A machine we trust talked to us from ${ip}, which is not an address it is allowed to talk`
254
+ + ` from. It proved it holds that machine's key, so either someone else has a copy of it,`
255
+ + ` or that machine's address changed.`
256
+ + `\n\nIt is frozen everywhere now, and nothing accepts it.`
257
+ + `\n\nIf this was an attack, remove \`machines/${machineId}.json\` from \`${sourceURL}\` now.`
258
+ + `\nIf it was legitimate, run \`yarn unrevoke git\` in that repo. It allows ${ip} for that`
259
+ + ` machine, and takes an hour to reach every machine.`
260
+ + `\n\nmachine: \`${machineId}\``
261
+ + `\nfrozen by: \`${hostLabel}\``);
262
+ }
263
+ /** Whether we will talk to this machine, coming from this address.
264
+
265
+ Both halves are required and neither is a guess: the caller knows the machine id because the
266
+ connection proved it, and knows the address because the packets came from there. A machine we
267
+ do not list is simply not accepted. A machine we do list, arriving from an address it does not
268
+ have, is treated as the same kind of event as a stolen ssh key - it is revoked everywhere, and
269
+ stays revoked until an unrevoke allows that machine from that address.
270
+
271
+ Throws when the repo itself cannot be read or is not signed, rather than answering false: that
272
+ is a broken installation, not a rejected machine, and the two deserve different handling. */
273
+ async function isMachineAccepted(config) {
274
+ let { machineId, ip } = config;
275
+ if (!machineId || !ip) {
276
+ throw new Error(`Expected a machineId and an ip, was ${JSON.stringify(machineId)} and ${JSON.stringify(ip)}`);
277
+ }
278
+ let { repoPath, sourceURL } = await resolveKeysRepo();
279
+ // Unsigned, or signed over different contents, means the machine list is not evidence of
280
+ // anything. Same gate the ssh keys go through.
281
+ await (0, trust_1.verifyCheckout)(repoPath);
282
+ let machine = (await readMachines(repoPath)).get(machineId);
283
+ if (!machine) {
284
+ return false;
285
+ }
286
+ await syncRevocations(sourceURL);
287
+ let unrevokes = await (0, revocation_1.readUnrevokes)(sourceURL).catch(() => ({ pairs: new Map(), legacyIds: new Map() }));
288
+ // An unrevoke is only honoured once it has waited out its hour, exactly as an ssh key's is.
289
+ let allowedAgain = async (pair) => {
290
+ let unrevokeId = unrevokes.pairs.get(pair);
291
+ return !!unrevokeId && await (0, trustState_1.unrevokeInEffect)(unrevokeId);
292
+ };
293
+ let revocations = await readMachineRevocations(sourceURL);
294
+ // Any revocation nothing has undone keeps the machine out, from everywhere, the way a revoked
295
+ // ssh key is out everywhere rather than only from the address it was misused from.
296
+ let revoked = false;
297
+ for (let revocation of revocations) {
298
+ if (revocation.machineId !== machineId) {
299
+ continue;
300
+ }
301
+ if (!await allowedAgain((0, revocation_1.pairKey)({ fingerprint: revocation.machineId, ip: revocation.ip }))) {
302
+ revoked = true;
303
+ }
304
+ }
305
+ if (revoked) {
306
+ return false;
307
+ }
308
+ if (machine.ips.includes(ip)) {
309
+ return true;
310
+ }
311
+ // Listed, but talking to us from somewhere it should not be. Recorded once for this machine
312
+ // and address, so being talked to repeatedly does not write repeatedly.
313
+ let pair = (0, revocation_1.pairKey)({ fingerprint: machineId, ip });
314
+ let alreadyRecorded = revocations.some(revocation => (0, revocation_1.pairKey)({ fingerprint: revocation.machineId, ip: revocation.ip }) === pair);
315
+ if (!alreadyRecorded && !await allowedAgain(pair)) {
316
+ await recordMachineRevocation({ sourceURL, machineId, ip, hostLabel: os_1.default.hostname() });
317
+ }
318
+ return false;
319
+ }
320
+ exports.isMachineAccepted = isMachineAccepted;
321
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFjaGluZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJtYWNoaW5lcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSwyREFBNkI7QUFDN0IsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QiwwREFBNkQ7QUFDN0Qsa0VBQStFO0FBQy9FLGtFQUErRTtBQUMvRSxvRUFBbUg7QUFDbkgsc0RBQXNEO0FBQ3RELGtFQUFxRTtBQUNyRSwwREFBZ0U7QUFDaEUsaUVBQStFO0FBQy9FLHVEQUEyRDtBQUMzRCw0REFBeUQ7QUFDekQsc0RBQXVJO0FBQ3ZJLDZDQUFnRDtBQUNoRCw0Q0FBZ0Q7QUFFaEQsbUdBQW1HO0FBQ25HLGlHQUFpRztBQUNqRyxnRUFBZ0U7QUFDbkQsUUFBQSxZQUFZLEdBQUcsVUFBVSxDQUFDO0FBQ3ZDLE1BQU0sZUFBZSxHQUFHLGFBQWEsQ0FBQztBQUN0QyxNQUFNLGlCQUFpQixHQUFHLDhDQUE4QyxDQUFDO0FBQ3pFLGdHQUFnRztBQUNoRyxtQ0FBbUM7QUFDbkMsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ3ZDLGlHQUFpRztBQUNqRyxxREFBcUQ7QUFDckQsTUFBTSxpQkFBaUIsR0FBRyxvQkFBb0IsQ0FBQztBQVMvQyxLQUFLLFVBQVUsVUFBVSxDQUFDLFFBQWdCO0lBQ3RDLElBQUksQ0FBQztRQUNELE1BQU0sa0JBQUUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUM7SUFDaEIsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDVCxPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsZUFBZSxDQUFDLFFBQWdCLEVBQUUsU0FBaUI7SUFDeEQsT0FBTyxjQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxvQkFBWSxFQUFFLEdBQUcsU0FBUyxPQUFPLENBQUMsQ0FBQztBQUNsRSxDQUFDO0FBRUQ7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SUFFRCw0RkFBNEY7SUFDNUYsd0VBQXdFO0lBQ3hFLElBQUksSUFBSSxHQUFHLElBQUEsb0JBQU8sRUFBQyxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNuRCxJQUFJLGVBQWUsR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQ2hELElBQUEsb0JBQU8sRUFBQyxFQUFFLFdBQVcsRUFBRSxVQUFVLENBQUMsU0FBUyxFQUFFLEVBQUUsRUFBRSxVQUFVLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQztJQUNoRixJQUFJLENBQUMsZUFBZSxJQUFJLENBQUMsTUFBTSxZQUFZLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUNoRCxNQUFNLHVCQUF1QixDQUFDLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLFlBQUUsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUYsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2pCLENBQUM7QUFuREQsOENBbURDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZzIGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IG9zIGZyb20gXCJvc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IENPTkZJR19QQVRIIH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9wYXRoc1wiO1xuaW1wb3J0IHsgcmV2b2tlUmVwbywgc3luY1JlcG9GaWxlcyB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vcmVwb0ZpbGVzXCI7XG5pbXBvcnQgeyBsaXN0UmVwb0RpciwgcmVhZFJlcG9GaWxlIH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9yZXBvRmlsZXNcIjtcbmltcG9ydCB7IG5ld1Jldm9jYXRpb25JZCwgcGFpcktleSwgcmVhZFJldm9jYXRpb25GaWxlcywgcmVhZFVucmV2b2tlcyB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vcmV2b2NhdGlvblwiO1xuaW1wb3J0IHsgcnVuR2l0IH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9naXRcIjtcbmltcG9ydCB7IGVuc3VyZVJldm9rZUtleSB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vcmVwb0ZpbGVzXCI7XG5pbXBvcnQgeyB2ZXJpZnlDaGVja291dCB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vdHJ1c3RcIjtcbmltcG9ydCB7IHJldm9rZVJlcG9QYXRoLCByZXZva2VSZXBvVVJMIH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL3Jldm9rZVNvdXJjZVwiO1xuaW1wb3J0IHsgc291cmNlUmVwb1BhdGggfSBmcm9tIFwiLi4vYXV0aG9yaXplZEtleXMvc291cmNlc1wiO1xuaW1wb3J0IHsgbm90aWZ5IH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9ub3RpZnlcIjtcbmltcG9ydCB7IGFyZURpc2NvcmROb3RpZmljYXRpb25zQ29uZmlndXJlZCwgY29uZmlndXJlRGlzY29yZE5vdGlmaWNhdGlvbnMsIERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEggfSBmcm9tIFwiLi4vbm90aWZpY2F0aW9ucy9kaXNjb3JkXCI7XG5pbXBvcnQgeyB1bnJldm9rZUluRWZmZWN0IH0gZnJvbSBcIi4vdHJ1c3RTdGF0ZVwiO1xuaW1wb3J0IHsgc3Bhd25Qcm9taXNlIH0gZnJvbSBcIi4uL2hlbHBlcnMvc3Bhd25cIjtcblxuLy8gV2hpY2ggbWFjaGluZXMgdGhpcyBzeXN0ZW0gdGFsa3MgdG8sIGtlcHQgaW4gdGhlIHNhbWUgcmVwbyBhcyB0aGUgc3NoIGtleXMuIFRoYXQgcmVwbyBpcyBhbHJlYWR5XG4vLyBzaWduZWQsIGFscmVhZHkgZGlzdHJpYnV0ZWQgdG8gZXZlcnkgbWFjaGluZSwgYW5kIGFscmVhZHkgaGFzIHNvbWV3aGVyZSB0byByZWNvcmQgYSByZWplY3Rpb24sXG4vLyBzbyBhIHNlY29uZCBvbmUgd291bGQgb25seSBiZSBhIHNlY29uZCB0aGluZyB0byBrZWVwIGluIHN0ZXAuXG5leHBvcnQgY29uc3QgTUFDSElORVNfRElSID0gXCJtYWNoaW5lc1wiO1xuY29uc3QgUkVWT0NBVElPTlNfRElSID0gXCJyZXZvY2F0aW9uc1wiO1xuY29uc3QgUkVWT0NBVElPTl9SRUFTT04gPSBcImEgbWFjaGluZSB0YWxrZWQgdG8gdXMgZnJvbSBhbiB1bmFwcHJvdmVkIElQXCI7XG4vLyBUaGUgcmV2b2tlIHJlcG8gaXMgcHVsbGVkIGF0IG1vc3QgdGhpcyBvZnRlbi4gQSBjaGVjayBoYXBwZW5zIHBlciByZXF1ZXN0LCBhbmQgYSByZXF1ZXN0IG11c3Rcbi8vIG5vdCBjb3N0IGEgcm91bmQgdHJpcCB0byBnaXRodWIuXG5jb25zdCBSRVZPS0VfU1lOQ19JTlRFUlZBTCA9IDYwICogMTAwMDtcbi8vIFdoZXJlIGEgV2luZG93cyBtYWNoaW5lIGlzIGV4cGVjdGVkIHRvIGtlZXAgdGhlIHJlcG8sIHJlbGF0aXZlIHRvIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4gVGhlcmVcbi8vIGlzIG5vIGRhZW1vbiB0aGVyZSB0byBhc2ssIGFuZCBubyAvZXRjIHRvIGxvb2sgaW4uXG5jb25zdCBXSU5ET1dTX1JFUE9fUEFUSCA9IFwiLi4vYXV0aG9yaXplZF9rZXlzXCI7XG5cbi8qKiBPbmUgbWFjaGluZSB3ZSBhcmUgd2lsbGluZyB0byB0YWxrIHRvLCBhbmQgdGhlIGFkZHJlc3NlcyBpdCBtYXkgdGFsayB0byB1cyBmcm9tLiAqL1xuZXhwb3J0IHR5cGUgTWFjaGluZVN0YXRlID0ge1xuICAgIG1hY2hpbmVJZDogc3RyaW5nO1xuICAgIGlwczogc3RyaW5nW107XG4gICAgYWRkZWRBdDogc3RyaW5nO1xufTtcblxuYXN5bmMgZnVuY3Rpb24gcGF0aEV4aXN0cyhmaWxlUGF0aDogc3RyaW5nKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZnMuYWNjZXNzKGZpbGVQYXRoKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBtYWNoaW5lRmlsZVBhdGgocmVwb1BhdGg6IHN0cmluZywgbWFjaGluZUlkOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gcGF0aC5qb2luKHJlcG9QYXRoLCBNQUNISU5FU19ESVIsIGAke21hY2hpbmVJZH0uanNvbmApO1xufVxuXG4vKiogUmVhZHMgZXZlcnkgbWFjaGluZSBhIGNoZWNrb3V0IGxpc3RzLCBvciBzZXRzIHRoZW0uXG5cbiAgICBQYXNzaW5nIGBtYWNoaW5lc2AgbWFrZXMgdGhlIHJlcG8gbWF0Y2ggaXQgZXhhY3RseTogbWFjaGluZXMgbmFtZWQgYXJlIHdyaXR0ZW4gd2l0aCB0aGVcbiAgICBhZGRyZXNzZXMgZ2l2ZW4sIGFuZCBtYWNoaW5lcyBub3QgbmFtZWQgYXJlIHJlbW92ZWQuIFRoYXQgaXMgd2h5IHRoZSBhZGRyZXNzZXMgYXJlIHBhcnQgb2ZcbiAgICBzZXR0aW5nIHJhdGhlciB0aGFuIGEgc2VwYXJhdGUgc3RlcCAtIGEgbWFjaGluZSB3aXRoIG5vIGFkZHJlc3MgaXQgbWF5IHRhbGsgZnJvbSBpcyBub3QgYVxuICAgIG1hY2hpbmUgd2Ugd291bGQgYWNjZXB0IGFueXdheSwgc28gdGhlcmUgaXMgbm8gc3RhdGUgd2hlcmUgbmFtaW5nIG9uZSB3aXRob3V0IHRoZW0gbWVhbnNcbiAgICBhbnl0aGluZy4gUmVhZCBmaXJzdCwgY2hhbmdlIHdoYXQgeW91IHdhbnQsIHdyaXRlIHRoZSByZXN1bHQuXG5cbiAgICBOb3RoaW5nIGhlcmUgc2lnbnMgb3IgY29tbWl0cyBhbnl0aGluZy4gVGhlIHNpZ25hdHVyZSBpcyB3aGF0IGV2ZXJ5IG90aGVyIG1hY2hpbmUgY2hlY2tzIGJlZm9yZVxuICAgIGJlbGlldmluZyBhbnkgb2YgdGhpcywgYW5kIGl0IHRha2VzIHRoZSBoYXJkd2FyZSBrZXksIHNvIGB5YXJuIHNpZ25maWxlcyBnaXRgIGlzIHN0aWxsIHlvdXJzIHRvXG4gICAgcnVuIGFmdGVyd2FyZHMuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFjaGluZVN0YXRlKGNvbmZpZzoge1xuICAgIHJlcG9QYXRoOiBzdHJpbmc7XG4gICAgbWFjaGluZXM/OiB7IG1hY2hpbmVJZDogc3RyaW5nOyBpcHM6IHN0cmluZ1tdIH1bXTtcbn0pOiBQcm9taXNlPE1hY2hpbmVTdGF0ZVtdPiB7XG4gICAgbGV0IHsgcmVwb1BhdGgsIG1hY2hpbmVzIH0gPSBjb25maWc7XG4gICAgbGV0IGV4aXN0aW5nID0gYXdhaXQgcmVhZE1hY2hpbmVzKHJlcG9QYXRoKTtcbiAgICBpZiAoIW1hY2hpbmVzKSB7XG4gICAgICAgIHJldHVybiBbLi4uZXhpc3RpbmcudmFsdWVzKCldO1xuICAgIH1cblxuICAgIGxldCBkaXJlY3RvcnkgPSBwYXRoLmpvaW4ocmVwb1BhdGgsIE1BQ0hJTkVTX0RJUik7XG4gICAgbGV0IHdyaXR0ZW46IE1hY2hpbmVTdGF0ZVtdID0gW107XG4gICAgZm9yIChsZXQgbWFjaGluZSBvZiBtYWNoaW5lcykge1xuICAgICAgICBpZiAoIW1hY2hpbmUubWFjaGluZUlkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGEgbWFjaGluZUlkLCB3YXMgJHtKU09OLnN0cmluZ2lmeShtYWNoaW5lLm1hY2hpbmVJZCl9YCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFtYWNoaW5lLmlwcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICBgRXhwZWN0ZWQgYWRkcmVzc2VzIGZvciAke21hY2hpbmUubWFjaGluZUlkfSwgd2FzIG5vbmUuIEEgbWFjaGluZSBpcyB0cnVzdGVkIGZyb21gXG4gICAgICAgICAgICAgICAgKyBgIHRoZSBhZGRyZXNzZXMgaXQgbWF5IHRhbGsgZnJvbSwgc28gbGVhdmUgaXQgb3V0IG9mIHRoZSBsaXN0IHRvIHJlbW92ZSBpdC5gXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGxldCBpcHMgPSBtYWNoaW5lLmlwcy5maWx0ZXIoKGlwLCBpbmRleCkgPT4gaXAgJiYgbWFjaGluZS5pcHMuaW5kZXhPZihpcCkgPT09IGluZGV4KTtcbiAgICAgICAgbGV0IHN0YXRlOiBNYWNoaW5lU3RhdGUgPSB7XG4gICAgICAgICAgICBtYWNoaW5lSWQ6IG1hY2hpbmUubWFjaGluZUlkLFxuICAgICAgICAgICAgLy8gSW4gdGhlIG9yZGVyIGdpdmVuLCB3aXRob3V0IGR1cGxpY2F0ZXMsIHNvIHRoZSBmaWxlIHJlYWRzIHRoZSB3YXkgaXQgd2FzIHNldC5cbiAgICAgICAgICAgIGlwcyxcbiAgICAgICAgICAgIGFkZGVkQXQ6IGV4aXN0aW5nLmdldChtYWNoaW5lLm1hY2hpbmVJZCk/LmFkZGVkQXQgfHwgbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICB9O1xuICAgICAgICBhd2FpdCBmcy5ta2RpcihkaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgICBhd2FpdCBmcy53cml0ZUZpbGUobWFjaGluZUZpbGVQYXRoKHJlcG9QYXRoLCBzdGF0ZS5tYWNoaW5lSWQpLCBKU09OLnN0cmluZ2lmeShzdGF0ZSwgdW5kZWZpbmVkLCA0KSArIFwiXFxuXCIpO1xuICAgICAgICB3cml0dGVuLnB1c2goc3RhdGUpO1xuICAgIH1cblxuICAgIC8vIFdoYXRldmVyIHRoZSByZXBvIGhhZCBhbmQgdGhlIGNhbGxlciBkaWQgbm90IG5hbWUgaXMgbm8gbG9uZ2VyIHRydXN0ZWQuXG4gICAgZm9yIChsZXQgbWFjaGluZUlkIG9mIGV4aXN0aW5nLmtleXMoKSkge1xuICAgICAgICBpZiAoIW1hY2hpbmVzLnNvbWUobWFjaGluZSA9PiBtYWNoaW5lLm1hY2hpbmVJZCA9PT0gbWFjaGluZUlkKSkge1xuICAgICAgICAgICAgYXdhaXQgZnMucm0obWFjaGluZUZpbGVQYXRoKHJlcG9QYXRoLCBtYWNoaW5lSWQpLCB7IGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiB3cml0dGVuO1xufVxuXG4vKiogRXZlcnkgbWFjaGluZSB0aGUgcmVwbyBsaXN0cy4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlYWRNYWNoaW5lcyhyZXBvUGF0aDogc3RyaW5nKSB7XG4gICAgbGV0IG1hY2hpbmVzID0gbmV3IE1hcDxzdHJpbmcsIE1hY2hpbmVTdGF0ZT4oKTtcbiAgICBsZXQgZGlyZWN0b3J5ID0gcGF0aC5qb2luKHJlcG9QYXRoLCBNQUNISU5FU19ESVIpO1xuICAgIGxldCBuYW1lcyA9IGF3YWl0IGZzLnJlYWRkaXIoZGlyZWN0b3J5KS5jYXRjaCgoKSA9PiBbXSBhcyBzdHJpbmdbXSk7XG4gICAgZm9yIChsZXQgbmFtZSBvZiBuYW1lcy5zb3J0KCkpIHtcbiAgICAgICAgaWYgKCFuYW1lLmVuZHNXaXRoKFwiLmpzb25cIikpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBsZXQgcGFyc2VkID0gSlNPTi5wYXJzZShhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBuYW1lKSwgXCJ1dGY4XCIpKTtcbiAgICAgICAgICAgIGxldCBtYWNoaW5lSWQgPSBwYXJzZWQubWFjaGluZUlkIHx8IG5hbWUucmVwbGFjZSgvXFwuanNvbiQvLCBcIlwiKTtcbiAgICAgICAgICAgIG1hY2hpbmVzLnNldChtYWNoaW5lSWQsIHsgbWFjaGluZUlkLCBpcHM6IHBhcnNlZC5pcHMgfHwgW10sIGFkZGVkQXQ6IHBhcnNlZC5hZGRlZEF0IHx8IFwiXCIgfSk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBJZ25vcmluZyB1bnJlYWRhYmxlIG1hY2hpbmUgZmlsZSAke25hbWV9LiAke2V9YCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1hY2hpbmVzO1xufVxuXG5hc3luYyBmdW5jdGlvbiBpc0tleXNSZXBvKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYXdhaXQgcGF0aEV4aXN0cyhwYXRoLmpvaW4ocmVwb1BhdGgsIFwiLmdpdFwiKSlcbiAgICAgICAgJiYgYXdhaXQgcGF0aEV4aXN0cyhwYXRoLmpvaW4ocmVwb1BhdGgsIFwiYXV0aG9yaXplZF9rZXlzXCIpKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gb3JpZ2luT2YocmVwb1BhdGg6IHN0cmluZykge1xuICAgIGxldCBvcmlnaW4gPSBhd2FpdCBzcGF3blByb21pc2UoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzOiBbXCJyZW1vdGVcIiwgXCJnZXQtdXJsXCIsIFwib3JpZ2luXCJdLCBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgIGxldCB1cmwgPSBvcmlnaW4uc3Rkb3V0LnRyaW0oKTtcbiAgICBpZiAob3JpZ2luLnN0YXR1cyAhPT0gMCB8fCAhdXJsKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtyZXBvUGF0aH0gdG8gaGF2ZSBhbiBvcmlnaW4gcmVtb3RlLCBpdCBoYXMgbm9uZWApO1xuICAgIH1cbiAgICByZXR1cm4gdXJsO1xufVxuXG4vKiogVGhlIGtleXMgcmVwbyB0aGlzIG1hY2hpbmUgYW5zd2VycyBmcm9tLCBhbmQgdGhlIG9uZSBhbnl0aGluZyBlZGl0aW5nIHRoZSBtYWNoaW5lIGxpc3QgZWRpdHMuXG5cbiAgICBPbiBhIGhvc3QsIHRoYXQgaXMgdGhlIGNoZWNrb3V0IHRoZSBkYWVtb24gYWxyZWFkeSBrZWVwcyB1cCB0byBkYXRlLiBPbiBXaW5kb3dzIHRoZXJlIGlzIG5vXG4gICAgZGFlbW9uIGFuZCBubyAvZXRjLCBzbyB0aGUgcmVwbyBpcyBleHBlY3RlZCBiZXNpZGUgdGhlIHdvcmtpbmcgZGlyZWN0b3J5LCB3aGljaCBpcyB3aGVyZSBhXG4gICAgZGV2ZWxvcGVyIHdvcmtpbmcgb24gYm90aCB3b3VsZCBoYXZlIGl0LlxuXG4gICAgTmV2ZXIgdGhlIGRpcmVjdG9yeSB0aGUgY29tbWFuZCBoYXBwZW5zIHRvIGJlIHJ1biBmcm9tLiBXaGljaCByZXBvIHRoaXMgbWFjaGluZSB0cnVzdHMgaXMgYVxuICAgIHByb3BlcnR5IG9mIHRoZSBtYWNoaW5lLCBub3Qgb2Ygd2hlcmUgc29tZWJvZHkgd2FzIHN0YW5kaW5nIHdoZW4gdGhleSB0eXBlZCBzb21ldGhpbmcuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZUtleXNSZXBvKCkge1xuICAgIGlmIChvcy5wbGF0Zm9ybSgpID09PSBcIndpbjMyXCIpIHtcbiAgICAgICAgbGV0IHJlcG9QYXRoID0gcGF0aC5yZXNvbHZlKFdJTkRPV1NfUkVQT19QQVRIKTtcbiAgICAgICAgaWYgKCFhd2FpdCBpc0tleXNSZXBvKHJlcG9QYXRoKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgIGBFeHBlY3RlZCAke3JlcG9QYXRofSB0byBiZSBhbiBhdXRob3JpemVkX2tleXMgcmVwbywgaXQgaXMgbm90LlxcbmBcbiAgICAgICAgICAgICAgICArIGBPbiBXaW5kb3dzIHRoZSByZXBvIGlzIHJlYWQgZnJvbSB0aGVyZSwgc28gY2xvbmUgaXQgYmVzaWRlIHRoaXMgb25lOlxcbmBcbiAgICAgICAgICAgICAgICArIGAgIGdpdCBjbG9uZSA8eW91ciBhdXRob3JpemVkX2tleXMgcmVwbz4gJHtyZXBvUGF0aH1gXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IHJlcG9QYXRoLCBzb3VyY2VVUkw6IGF3YWl0IG9yaWdpbk9mKHJlcG9QYXRoKSB9O1xuICAgIH1cblxuICAgIGxldCBjb25maWcgPSBhd2FpdCBmcy5yZWFkRmlsZShDT05GSUdfUEFUSCwgXCJ1dGY4XCIpLmNhdGNoKCgpID0+IFwiXCIpO1xuICAgIGxldCBzb3VyY2VVUkwgPSBjb25maWcgJiYgKEpTT04ucGFyc2UoY29uZmlnKS5yZXBvU291cmNlcyB8fCBbXSlbMF0gfHwgXCJcIjtcbiAgICBpZiAoIXNvdXJjZVVSTCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgdGhpcyBtYWNoaW5lIHRvIGJlIHNldCB1cCB3aXRoIGFuIGF1dGhvcml6ZWRfa2V5cyByZXBvLCAke0NPTkZJR19QQVRIfSBuYW1lcyBub25lLlxcbmBcbiAgICAgICAgICAgICsgYFNldCBpdCB1cCBmaXJzdDpcXG5gXG4gICAgICAgICAgICArIGAgIHlhcm4gc2V0dXBub3RpZnkgPGRpc2NvcmQtd2ViaG9vay11cmw+XFxuYFxuICAgICAgICAgICAgKyBgICB5YXJuIHNlY3VyZXNzaCBhZGQgPHJlcG8tcHJpdmF0ZS1rZXk+IDxyZXBvLXVybD5gXG4gICAgICAgICk7XG4gICAgfVxuICAgIGxldCByZXBvUGF0aCA9IHNvdXJjZVJlcG9QYXRoKHNvdXJjZVVSTCk7XG4gICAgaWYgKCFhd2FpdCBpc0tleXNSZXBvKHJlcG9QYXRoKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgYSBjaGVja291dCBvZiAke3NvdXJjZVVSTH0gYXQgJHtyZXBvUGF0aH0sIHRoZXJlIGlzIG5vbmUuXFxuYFxuICAgICAgICAgICAgKyBgUnVuIFxcYHlhcm4gc2VjdXJlc3NoIHVwZGF0ZVxcYCB0byBwdXQgaXQgYmFjay5gXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB7IHJlcG9QYXRoLCBzb3VyY2VVUkwgfTtcbn1cblxubGV0IGxhc3RSZXZva2VTeW5jID0gMDtcblxuLyoqIFB1bGxlZCBhdCBtb3N0IG9uY2UgYSBSRVZPS0VfU1lOQ19JTlRFUlZBTCwgYmVjYXVzZSB0aGlzIGlzIGFza2VkIHBlciByZXF1ZXN0LiBBIHN5bmMgdGhhdFxuICAgIGZhaWxzIGxlYXZlcyB0aGUgY2hlY2tvdXQgd2UgYWxyZWFkeSBoYXZlLCB3aGljaCBpcyB0aGUgc2FmZSBkaXJlY3Rpb246IHJldm9jYXRpb25zIHdlIGtub3dcbiAgICBhYm91dCBzdGF5IGtub3duLiAqL1xuYXN5bmMgZnVuY3Rpb24gc3luY1Jldm9jYXRpb25zKHNvdXJjZVVSTDogc3RyaW5nKSB7XG4gICAgaWYgKERhdGUubm93KCkgLSBsYXN0UmV2b2tlU3luYyA8IFJFVk9LRV9TWU5DX0lOVEVSVkFMKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGFzdFJldm9rZVN5bmMgPSBEYXRlLm5vdygpO1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHN5bmNSZXBvRmlsZXMocmV2b2tlUmVwbyhzb3VyY2VVUkwpKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBDb3VsZCBub3QgcmVhZCAke3Jldm9rZVJlcG9VUkwoc291cmNlVVJMKX0sIHVzaW5nIHRoZSByZXZvY2F0aW9ucyBhbHJlYWR5IGhlcmUuICR7ZX1gKTtcbiAgICB9XG59XG5cbi8qKiBNYWNoaW5lIHJldm9jYXRpb25zLCByZWFkIG91dCBvZiB0aGUgcmV2b2tlIHJlcG8gdGhlIHNhbWUgd2F5IGtleSByZXZvY2F0aW9ucyBhcmUuICovXG5hc3luYyBmdW5jdGlvbiByZWFkTWFjaGluZVJldm9jYXRpb25zKHNvdXJjZVVSTDogc3RyaW5nKSB7XG4gICAgbGV0IHJlcG8gPSByZXZva2VSZXBvKHNvdXJjZVVSTCk7XG4gICAgbGV0IHJldm9jYXRpb25zOiB7IHJldm9jYXRpb25JZDogc3RyaW5nOyBtYWNoaW5lSWQ6IHN0cmluZzsgaXA6IHN0cmluZyB9W10gPSBbXTtcbiAgICBmb3IgKGxldCBuYW1lIG9mIGF3YWl0IGxpc3RSZXBvRGlyKHJlcG8sIFJFVk9DQVRJT05TX0RJUikpIHtcbiAgICAgICAgaWYgKCFuYW1lLmVuZHNXaXRoKFwiLmpzb25cIikpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBsZXQgcGFyc2VkID0gSlNPTi5wYXJzZShhd2FpdCByZWFkUmVwb0ZpbGUocmVwbywgcGF0aC5qb2luKFJFVk9DQVRJT05TX0RJUiwgbmFtZSkpIHx8IFwiXCIpO1xuICAgICAgICAgICAgaWYgKHBhcnNlZC5tYWNoaW5lSWQpIHtcbiAgICAgICAgICAgICAgICByZXZvY2F0aW9ucy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgcmV2b2NhdGlvbklkOiBwYXJzZWQucmV2b2NhdGlvbklkIHx8IG5hbWUucmVwbGFjZSgvXFwuanNvbiQvLCBcIlwiKSxcbiAgICAgICAgICAgICAgICAgICAgbWFjaGluZUlkOiBwYXJzZWQubWFjaGluZUlkLFxuICAgICAgICAgICAgICAgICAgICBpcDogcGFyc2VkLmlwIHx8IFwiXCIsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBJZ25vcmluZyB1bnJlYWRhYmxlIHJldm9jYXRpb24gJHtuYW1lfS4gJHtlfWApO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXZvY2F0aW9ucztcbn1cblxuLyoqIFNlbmRzIHRoZSBvbmUgbm90aWZpY2F0aW9uIHRoaXMgZmlsZSBpcyBhbGxvd2VkIHRvIHNlbmQsIHdoZW4gaXQgY2FuLlxuXG4gICAgVGhlIGRhZW1vbiBjb25maWd1cmVzIG5vdGlmaWNhdGlvbnMgb24gc3RhcnR1cCBhbmQgd291bGQgc2ltcGx5IHNlbmQuIFRoaXMgY2FuIGFsc28gcnVuIGluIHNvbWVcbiAgICBvdGhlciBwcm9jZXNzLCB3aGljaCBoYXMgbm90LCBzbyB0aGUgd2ViaG9vayBpcyBwaWNrZWQgdXAgaGVyZSBpZiBpdCBpcyByZWFkYWJsZS4gQSBtYWNoaW5lXG4gICAgd2l0aCBubyB3ZWJob29rIHNldCB1cCBzdGlsbCByZWNvcmRzIHRoZSByZXZvY2F0aW9uIC0gYmVpbmcgdW5hYmxlIHRvIHRlbGwgYW55b25lIGlzIG5vdCBhXG4gICAgcmVhc29uIHRvIGtlZXAgYWNjZXB0aW5nIGEgbWFjaGluZSB0aGF0IGlzIGJlaW5nIG1pc3VzZWQuICovXG5hc3luYyBmdW5jdGlvbiBub3RpZnlCZXN0RWZmb3J0KGhlYWRsaW5lOiBzdHJpbmcsIGJvZHk6IHN0cmluZykge1xuICAgIHRyeSB7XG4gICAgICAgIGlmICghYXJlRGlzY29yZE5vdGlmaWNhdGlvbnNDb25maWd1cmVkKCkpIHtcbiAgICAgICAgICAgIC8vIFJlYWQgZmlyc3Q6IGNvbmZpZ3VyZURpc2NvcmROb3RpZmljYXRpb25zIGV4aXRzIHRoZSBwcm9jZXNzIHdoZW4gdGhlIGZpbGUgaXMgbWlzc2luZyxcbiAgICAgICAgICAgIC8vIHdoaWNoIGlzIHJpZ2h0IGZvciB0aGUgZGFlbW9uIGF0IHN0YXJ0dXAgYW5kIHdyb25nIGZvciBhIGxpYnJhcnkgY2FsbC5cbiAgICAgICAgICAgIGF3YWl0IGZzLnJlYWRGaWxlKERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEgsIFwidXRmOFwiKTtcbiAgICAgICAgICAgIGF3YWl0IGNvbmZpZ3VyZURpc2NvcmROb3RpZmljYXRpb25zKHsgZmlsZVBhdGg6IERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEggfSk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgbm90aWZ5KGhlYWRsaW5lLCBib2R5KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBDb3VsZCBub3Qgc2VuZCBhIG5vdGlmaWNhdGlvbiBhYm91dCB0aGlzLCBpdCBpcyBvbmx5IGluIHRoZSBsb2cuICR7ZX1gKTtcbiAgICB9XG59XG5cbi8qKiBSZWNvcmRzIHRoYXQgYSBtYWNoaW5lIHdlIGFjY2VwdCB0YWxrZWQgdG8gdXMgZnJvbSBhbiBhZGRyZXNzIGl0IGlzIG5vdCBhbGxvd2VkIGZyb20uIE9uZSBwZXJcbiAgICBtYWNoaW5lIGFuZCBhZGRyZXNzLCBzbyBhIHNlY29uZCBhZGRyZXNzIGlzIGEgc2Vjb25kIHJldm9jYXRpb24gYW5kIGFuIHVucmV2b2tlIG9mIHRoZSBmaXJzdFxuICAgIGRvZXMgbm90IGNvdmVyIGl0LiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVjb3JkTWFjaGluZVJldm9jYXRpb24oY29uZmlnOiB7XG4gICAgc291cmNlVVJMOiBzdHJpbmc7XG4gICAgbWFjaGluZUlkOiBzdHJpbmc7XG4gICAgaXA6IHN0cmluZztcbiAgICBob3N0TGFiZWw6IHN0cmluZztcbn0pIHtcbiAgICBsZXQgeyBzb3VyY2VVUkwsIG1hY2hpbmVJZCwgaXAsIGhvc3RMYWJlbCB9ID0gY29uZmlnO1xuICAgIGxldCByZXBvUGF0aCA9IHJldm9rZVJlcG9QYXRoKHNvdXJjZVVSTCk7XG4gICAgbGV0IGtleVBhdGggPSBhd2FpdCBlbnN1cmVSZXZva2VLZXkoc291cmNlVVJMKTtcbiAgICBsZXQgcmV2b2NhdGlvbklkID0gbmV3UmV2b2NhdGlvbklkKG1hY2hpbmVJZCk7XG4gICAgbGV0IGRpcmVjdG9yeSA9IHBhdGguam9pbihyZXBvUGF0aCwgUkVWT0NBVElPTlNfRElSKTtcbiAgICBhd2FpdCBmcy5ta2RpcihkaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBgJHtyZXZvY2F0aW9uSWR9Lmpzb25gKSwgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICByZXZvY2F0aW9uSWQsXG4gICAgICAgIG1hY2hpbmVJZCxcbiAgICAgICAgaXAsXG4gICAgICAgIHJldm9rZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICByZXZva2VkQnk6IGhvc3RMYWJlbCxcbiAgICAgICAgcmVhc29uOiBSRVZPQ0FUSU9OX1JFQVNPTixcbiAgICB9LCB1bmRlZmluZWQsIDQpICsgXCJcXG5cIik7XG5cbiAgICBhd2FpdCBydW5HaXQoeyBhcmdzOiBbXCJhZGRcIiwgXCItQVwiXSwgY3dkOiByZXBvUGF0aCwga2V5UGF0aCB9KTtcbiAgICBhd2FpdCBydW5HaXQoe1xuICAgICAgICBhcmdzOiBbXCItY1wiLCBcInVzZXIuZW1haWw9cG9ydHNlY3VyZUBsb2NhbGhvc3RcIiwgXCItY1wiLCBcInVzZXIubmFtZT1wb3J0c2VjdXJlXCIsIFwiY29tbWl0XCIsIFwiLW1cIiwgYHJldm9rZSAke3Jldm9jYXRpb25JZH1gXSxcbiAgICAgICAgY3dkOiByZXBvUGF0aCwga2V5UGF0aCxcbiAgICB9KTtcbiAgICBsZXQgcHVzaCA9IGF3YWl0IHJ1bkdpdCh7IGFyZ3M6IFtcInB1c2hcIiwgXCJvcmlnaW5cIiwgXCJIRUFEXCJdLCBjd2Q6IHJlcG9QYXRoLCBrZXlQYXRoLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgaWYgKHB1c2guc3RhdHVzICE9PSAwKSB7XG4gICAgICAgIC8vIEFub3RoZXIgbWFjaGluZSBtb3N0IGxpa2VseSByZWNvcmRlZCB0aGUgc2FtZSB0aGluZyBmaXJzdCwgYW5kIHRoZSBuZXh0IHJlYWQgcGlja3MgaXQgdXAuXG4gICAgICAgIGNvbnNvbGUubG9nKGBDb3VsZCBub3QgcHVzaCB0aGUgcmV2b2NhdGlvbiBvZiAke21hY2hpbmVJZH0gZnJvbSAke2lwfS4gJHsocHVzaC5zdGRvdXQgKyBwdXNoLnN0ZGVycikudHJpbSgpfWApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnNvbGUubG9nKGBSZXZva2VkICR7bWFjaGluZUlkfSBmcm9tICR7aXB9LCAke3Jldm9jYXRpb25JZH1gKTtcblxuICAgIC8vIFNhaWQgYnkgd2hvZXZlciB3cm90ZSB0aGUgcmV2b2NhdGlvbiwgb25jZSwgdGhlIHNhbWUgYXMgZm9yIGFuIHNzaCBrZXkuIE1hY2hpbmVzIHRoYXQgb25seVxuICAgIC8vIHJlYWQgaXQgbGF0ZXIgc2F5IG5vdGhpbmcsIG9yIG9uZSBldmVudCB3b3VsZCBiZSByZXBvcnRlZCBieSBldmVyeSBtYWNoaW5lIHRoYXQgc2F3IGl0LlxuICAgIGF3YWl0IG5vdGlmeUJlc3RFZmZvcnQoXG4gICAgICAgIGBTVVNQSUNJT1VTIElQICR7aXB9IEZST1pFIE1BQ0hJTkUgJHttYWNoaW5lSWR9YCxcbiAgICAgICAgYEEgbWFjaGluZSB3ZSB0cnVzdCB0YWxrZWQgdG8gdXMgZnJvbSAke2lwfSwgd2hpY2ggaXMgbm90IGFuIGFkZHJlc3MgaXQgaXMgYWxsb3dlZCB0byB0YWxrYFxuICAgICAgICArIGAgZnJvbS4gSXQgcHJvdmVkIGl0IGhvbGRzIHRoYXQgbWFjaGluZSdzIGtleSwgc28gZWl0aGVyIHNvbWVvbmUgZWxzZSBoYXMgYSBjb3B5IG9mIGl0LGBcbiAgICAgICAgKyBgIG9yIHRoYXQgbWFjaGluZSdzIGFkZHJlc3MgY2hhbmdlZC5gXG4gICAgICAgICsgYFxcblxcbkl0IGlzIGZyb3plbiBldmVyeXdoZXJlIG5vdywgYW5kIG5vdGhpbmcgYWNjZXB0cyBpdC5gXG4gICAgICAgICsgYFxcblxcbklmIHRoaXMgd2FzIGFuIGF0dGFjaywgcmVtb3ZlIFxcYG1hY2hpbmVzLyR7bWFjaGluZUlkfS5qc29uXFxgIGZyb20gXFxgJHtzb3VyY2VVUkx9XFxgIG5vdy5gXG4gICAgICAgICsgYFxcbklmIGl0IHdhcyBsZWdpdGltYXRlLCBydW4gXFxgeWFybiB1bnJldm9rZSBnaXRcXGAgaW4gdGhhdCByZXBvLiBJdCBhbGxvd3MgJHtpcH0gZm9yIHRoYXRgXG4gICAgICAgICsgYCBtYWNoaW5lLCBhbmQgdGFrZXMgYW4gaG91ciB0byByZWFjaCBldmVyeSBtYWNoaW5lLmBcbiAgICAgICAgKyBgXFxuXFxubWFjaGluZTogXFxgJHttYWNoaW5lSWR9XFxgYFxuICAgICAgICArIGBcXG5mcm96ZW4gYnk6IFxcYCR7aG9zdExhYmVsfVxcYGBcbiAgICApO1xufVxuXG4vKiogV2hldGhlciB3ZSB3aWxsIHRhbGsgdG8gdGhpcyBtYWNoaW5lLCBjb21pbmcgZnJvbSB0aGlzIGFkZHJlc3MuXG5cbiAgICBCb3RoIGhhbHZlcyBhcmUgcmVxdWlyZWQgYW5kIG5laXRoZXIgaXMgYSBndWVzczogdGhlIGNhbGxlciBrbm93cyB0aGUgbWFjaGluZSBpZCBiZWNhdXNlIHRoZVxuICAgIGNvbm5lY3Rpb24gcHJvdmVkIGl0LCBhbmQga25vd3MgdGhlIGFkZHJlc3MgYmVjYXVzZSB0aGUgcGFja2V0cyBjYW1lIGZyb20gdGhlcmUuIEEgbWFjaGluZSB3ZVxuICAgIGRvIG5vdCBsaXN0IGlzIHNpbXBseSBub3QgYWNjZXB0ZWQuIEEgbWFjaGluZSB3ZSBkbyBsaXN0LCBhcnJpdmluZyBmcm9tIGFuIGFkZHJlc3MgaXQgZG9lcyBub3RcbiAgICBoYXZlLCBpcyB0cmVhdGVkIGFzIHRoZSBzYW1lIGtpbmQgb2YgZXZlbnQgYXMgYSBzdG9sZW4gc3NoIGtleSAtIGl0IGlzIHJldm9rZWQgZXZlcnl3aGVyZSwgYW5kXG4gICAgc3RheXMgcmV2b2tlZCB1bnRpbCBhbiB1bnJldm9rZSBhbGxvd3MgdGhhdCBtYWNoaW5lIGZyb20gdGhhdCBhZGRyZXNzLlxuXG4gICAgVGhyb3dzIHdoZW4gdGhlIHJlcG8gaXRzZWxmIGNhbm5vdCBiZSByZWFkIG9yIGlzIG5vdCBzaWduZWQsIHJhdGhlciB0aGFuIGFuc3dlcmluZyBmYWxzZTogdGhhdFxuICAgIGlzIGEgYnJva2VuIGluc3RhbGxhdGlvbiwgbm90IGEgcmVqZWN0ZWQgbWFjaGluZSwgYW5kIHRoZSB0d28gZGVzZXJ2ZSBkaWZmZXJlbnQgaGFuZGxpbmcuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaXNNYWNoaW5lQWNjZXB0ZWQoY29uZmlnOiB7IG1hY2hpbmVJZDogc3RyaW5nOyBpcDogc3RyaW5nIH0pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBsZXQgeyBtYWNoaW5lSWQsIGlwIH0gPSBjb25maWc7XG4gICAgaWYgKCFtYWNoaW5lSWQgfHwgIWlwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSBtYWNoaW5lSWQgYW5kIGFuIGlwLCB3YXMgJHtKU09OLnN0cmluZ2lmeShtYWNoaW5lSWQpfSBhbmQgJHtKU09OLnN0cmluZ2lmeShpcCl9YCk7XG4gICAgfVxuICAgIGxldCB7IHJlcG9QYXRoLCBzb3VyY2VVUkwgfSA9IGF3YWl0IHJlc29sdmVLZXlzUmVwbygpO1xuICAgIC8vIFVuc2lnbmVkLCBvciBzaWduZWQgb3ZlciBkaWZmZXJlbnQgY29udGVudHMsIG1lYW5zIHRoZSBtYWNoaW5lIGxpc3QgaXMgbm90IGV2aWRlbmNlIG9mXG4gICAgLy8gYW55dGhpbmcuIFNhbWUgZ2F0ZSB0aGUgc3NoIGtleXMgZ28gdGhyb3VnaC5cbiAgICBhd2FpdCB2ZXJpZnlDaGVja291dChyZXBvUGF0aCk7XG5cbiAgICBsZXQgbWFjaGluZSA9IChhd2FpdCByZWFkTWFjaGluZXMocmVwb1BhdGgpKS5nZXQobWFjaGluZUlkKTtcbiAgICBpZiAoIW1hY2hpbmUpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGF3YWl0IHN5bmNSZXZvY2F0aW9ucyhzb3VyY2VVUkwpO1xuICAgIGxldCB1bnJldm9rZXMgPSBhd2FpdCByZWFkVW5yZXZva2VzKHNvdXJjZVVSTCkuY2F0Y2goKCkgPT4gKHsgcGFpcnM6IG5ldyBNYXAoKSwgbGVnYWN5SWRzOiBuZXcgTWFwKCkgfSkpO1xuICAgIC8vIEFuIHVucmV2b2tlIGlzIG9ubHkgaG9ub3VyZWQgb25jZSBpdCBoYXMgd2FpdGVkIG91dCBpdHMgaG91ciwgZXhhY3RseSBhcyBhbiBzc2gga2V5J3MgaXMuXG4gICAgbGV0IGFsbG93ZWRBZ2FpbiA9IGFzeW5jIChwYWlyOiBzdHJpbmcpID0+IHtcbiAgICAgICAgbGV0IHVucmV2b2tlSWQgPSB1bnJldm9rZXMucGFpcnMuZ2V0KHBhaXIpO1xuICAgICAgICByZXR1cm4gISF1bnJldm9rZUlkICYmIGF3YWl0IHVucmV2b2tlSW5FZmZlY3QodW5yZXZva2VJZCk7XG4gICAgfTtcbiAgICBsZXQgcmV2b2NhdGlvbnMgPSBhd2FpdCByZWFkTWFjaGluZVJldm9jYXRpb25zKHNvdXJjZVVSTCk7XG4gICAgLy8gQW55IHJldm9jYXRpb24gbm90aGluZyBoYXMgdW5kb25lIGtlZXBzIHRoZSBtYWNoaW5lIG91dCwgZnJvbSBldmVyeXdoZXJlLCB0aGUgd2F5IGEgcmV2b2tlZFxuICAgIC8vIHNzaCBrZXkgaXMgb3V0IGV2ZXJ5d2hlcmUgcmF0aGVyIHRoYW4gb25seSBmcm9tIHRoZSBhZGRyZXNzIGl0IHdhcyBtaXN1c2VkIGZyb20uXG4gICAgbGV0IHJldm9rZWQgPSBmYWxzZTtcbiAgICBmb3IgKGxldCByZXZvY2F0aW9uIG9mIHJldm9jYXRpb25zKSB7XG4gICAgICAgIGlmIChyZXZvY2F0aW9uLm1hY2hpbmVJZCAhPT0gbWFjaGluZUlkKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWF3YWl0IGFsbG93ZWRBZ2FpbihwYWlyS2V5KHsgZmluZ2VycHJpbnQ6IHJldm9jYXRpb24ubWFjaGluZUlkLCBpcDogcmV2b2NhdGlvbi5pcCB9KSkpIHtcbiAgICAgICAgICAgIHJldm9rZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfVxuICAgIGlmIChyZXZva2VkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBpZiAobWFjaGluZS5pcHMuaW5jbHVkZXMoaXApKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8vIExpc3RlZCwgYnV0IHRhbGtpbmcgdG8gdXMgZnJvbSBzb21ld2hlcmUgaXQgc2hvdWxkIG5vdCBiZS4gUmVjb3JkZWQgb25jZSBmb3IgdGhpcyBtYWNoaW5lXG4gICAgLy8gYW5kIGFkZHJlc3MsIHNvIGJlaW5nIHRhbGtlZCB0byByZXBlYXRlZGx5IGRvZXMgbm90IHdyaXRlIHJlcGVhdGVkbHkuXG4gICAgbGV0IHBhaXIgPSBwYWlyS2V5KHsgZmluZ2VycHJpbnQ6IG1hY2hpbmVJZCwgaXAgfSk7XG4gICAgbGV0IGFscmVhZHlSZWNvcmRlZCA9IHJldm9jYXRpb25zLnNvbWUocmV2b2NhdGlvbiA9PlxuICAgICAgICBwYWlyS2V5KHsgZmluZ2VycHJpbnQ6IHJldm9jYXRpb24ubWFjaGluZUlkLCBpcDogcmV2b2NhdGlvbi5pcCB9KSA9PT0gcGFpcik7XG4gICAgaWYgKCFhbHJlYWR5UmVjb3JkZWQgJiYgIWF3YWl0IGFsbG93ZWRBZ2FpbihwYWlyKSkge1xuICAgICAgICBhd2FpdCByZWNvcmRNYWNoaW5lUmV2b2NhdGlvbih7IHNvdXJjZVVSTCwgbWFjaGluZUlkLCBpcCwgaG9zdExhYmVsOiBvcy5ob3N0bmFtZSgpIH0pO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG59XG4iXX0=
322
+ /* _JS_SOURCE_HASH = "42ec23e7ee1a7879769634b3b36b652f35e6140757988d443d15d7a3a5d4adec"; */