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
@@ -1,7 +1,7 @@
1
1
  import fs from "fs/promises";
2
2
  import path from "path";
3
3
  import { spawnPromise } from "../../helpers/spawn";
4
- import { sourceKeyPath, sourceRepoPath } from "../sources";
4
+ import { findSourceKey, sourceKeyPath, sourceRepoPath } from "../sources";
5
5
  import { GIT_TIMEOUT, MAX_ERROR_BODY_LENGTH } from "./paths";
6
6
  import { sourceState } from "./state";
7
7
 
@@ -68,7 +68,7 @@ export async function currentBranch(config: { repoPath: string; keyPath: string
68
68
 
69
69
  async function ensureSourceRepo(repoURL: string) {
70
70
  let repoPath = sourceRepoPath(repoURL);
71
- let keyPath = sourceKeyPath(repoURL);
71
+ let keyPath = await findSourceKey(repoURL) || sourceKeyPath(repoURL);
72
72
  if (!await repoIsUsable({ repoPath, keyPath })) {
73
73
  await cloneRepo({ repoURL, repoPath, keyPath });
74
74
  }
@@ -82,7 +82,7 @@ async function ensureSourceRepo(repoURL: string) {
82
82
  export async function syncRepo(repoURL: string) {
83
83
  await ensureSourceRepo(repoURL);
84
84
  let repoPath = sourceRepoPath(repoURL);
85
- let keyPath = sourceKeyPath(repoURL);
85
+ let keyPath = await findSourceKey(repoURL) || sourceKeyPath(repoURL);
86
86
  let branch = sourceState(repoURL).branch;
87
87
  let localSha = (await runGit({ args: ["rev-parse", "HEAD"], cwd: repoPath, keyPath })).stdout.trim();
88
88
 
@@ -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
- export async function notify(message: string) {
15
- let full = `**portsecure [${hostLabelValue || os.hostname()}]**: ${message}`;
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
+ }
@@ -1,6 +1,6 @@
1
1
  import fs from "fs/promises";
2
2
  import path from "path";
3
- import { keyFingerprint, keyRestriction, keyRestrictionList, NO_RESTRICTION, summarizeKey } from "../authorizedKeys";
3
+ import { keyFingerprint, keyNiceName, keyRestriction, keyRestrictionList, NO_RESTRICTION, summarizeKey } from "../authorizedKeys";
4
4
  import { KEYS_HISTORY_PATH, ROOT_AUTHORIZED_KEYS } from "./paths";
5
5
  import { notify } from "./notify";
6
6
  import { getState, saveState } from "./state";
@@ -53,6 +53,64 @@ function describeAddressChange(config: { previousLine: string; currentLine: stri
53
53
  return lines.join("\n");
54
54
  }
55
55
 
56
+ /** The change in one line, for the notification preview. One key added, one removed, or one
57
+ address gained or lost, said plainly - that covers nearly every real change. Anything with more
58
+ than one part to it falls back to counting, because a line that tries to say four things says
59
+ none of them in the space a preview gives it. */
60
+ export function summarizeKeyDifference(config: { before: string[]; after: string[] }) {
61
+ let { before, after } = config;
62
+ let beforeByKey = new Map(before.map(key => [keyIdentity(key), key]));
63
+ let afterByKey = new Map(after.map(key => [keyIdentity(key), key]));
64
+
65
+ let added = [...afterByKey].filter(([identity]) => !beforeByKey.has(identity)).map(([, key]) => key);
66
+ let removed = [...beforeByKey].filter(([identity]) => !afterByKey.has(identity)).map(([, key]) => key);
67
+ let addressesAdded: { key: string; address: string }[] = [];
68
+ let addressesRemoved: { key: string; address: string }[] = [];
69
+ let otherwiseChanged: string[] = [];
70
+ for (let [identity, previousLine] of beforeByKey) {
71
+ let currentLine = afterByKey.get(identity);
72
+ if (!currentLine || currentLine === previousLine) {
73
+ continue;
74
+ }
75
+ let keyLine = currentLine;
76
+ let previous = keyRestrictionList(previousLine) || [];
77
+ let current = keyRestrictionList(keyLine) || [];
78
+ let gained = current.filter(address => !previous.includes(address));
79
+ let lost = previous.filter(address => !current.includes(address));
80
+ if (!gained.length && !lost.length) {
81
+ otherwiseChanged.push(keyLine);
82
+ continue;
83
+ }
84
+ addressesAdded.push(...gained.map(address => ({ key: keyLine, address })));
85
+ addressesRemoved.push(...lost.map(address => ({ key: keyLine, address })));
86
+ }
87
+
88
+ let changes = added.length + removed.length + addressesAdded.length + addressesRemoved.length
89
+ + otherwiseChanged.length;
90
+ if (changes === 1) {
91
+ if (added.length) {
92
+ return `SSH KEY ADDED ${keyNiceName(added[0])}`;
93
+ }
94
+ if (removed.length) {
95
+ return `SSH KEY REMOVED ${keyNiceName(removed[0])}`;
96
+ }
97
+ if (addressesAdded.length) {
98
+ return `IP ${addressesAdded[0].address} ALLOWED ON ${keyNiceName(addressesAdded[0].key)}`;
99
+ }
100
+ if (addressesRemoved.length) {
101
+ return `IP ${addressesRemoved[0].address} REMOVED FROM ${keyNiceName(addressesRemoved[0].key)}`;
102
+ }
103
+ return `SSH KEY CHANGED ${keyNiceName(otherwiseChanged[0])}`;
104
+ }
105
+ let counted = [
106
+ added.length && `${added.length} key(s) added` || "",
107
+ removed.length && `${removed.length} key(s) removed` || "",
108
+ addressesAdded.length && `${addressesAdded.length} IP(s) allowed` || "",
109
+ addressesRemoved.length && `${addressesRemoved.length} IP(s) removed` || "",
110
+ ].filter(part => part);
111
+ return `SSH KEYS CHANGED: ${counted.join(", ") || "no keys differ"}`;
112
+ }
113
+
56
114
  export function describeKeyDifference(config: { before: string[]; after: string[] }) {
57
115
  let { before, after } = config;
58
116
  let beforeByKey = new Map(before.map(key => [keyIdentity(key), key]));
@@ -181,18 +239,27 @@ export async function enforceRootKeys(keys: string[]) {
181
239
  state.appliedKeys = keys;
182
240
  await saveState();
183
241
 
184
- let reasons = takeChangeReasons();
185
- let why = reasons.length && `\n\n${reasons.join("\n\n")}` || "";
242
+ let { leading, reasons } = takeChangeReasons();
243
+ // An attack names the message. Whoever is looking at a preview needs to see that, not that
244
+ // some keys changed - the change is what the attack caused.
245
+ let alert = leading.map(entry => entry.body).join("\n\n");
246
+ let why = [alert, ...reasons].filter(part => part).join("\n\n");
247
+ let before = weChangedIt && previouslyApplied || keys;
248
+ let after = weChangedIt && keys || currentKeys;
249
+ let headline = leading[0]?.headline || summarizeKeyDifference({ before, after });
250
+
186
251
  if (weChangedIt) {
187
- await notify(
188
- `applied authorized key changes:`
189
- + `\n\`\`\`\n${describeKeyDifference({ before: previouslyApplied, after: keys })}\n\`\`\`${why}`
252
+ await notify(headline,
253
+ `Who can log in as root:`
254
+ + `\n\`\`\`\n${describeKeyDifference({ before, after })}\n\`\`\``
255
+ + `${why && `\n${why}` || ""}`
190
256
  );
191
257
  return;
192
258
  }
193
- await notify(
194
- `root's authorized_keys was edited by something other than portsecure. The edit below has`
195
- + ` been undone, and the keys from the repos are back in place.`
196
- + `\n\`\`\`\n${describeKeyDifference({ before: keys, after: currentKeys })}\n\`\`\`${why}`
259
+ await notify(leading[0]?.headline || `SSH KEYS EDITED BY SOMETHING ELSE`,
260
+ `Something other than portsecure edited root's authorized_keys. The edit below has been`
261
+ + ` undone, and the keys from the repos are back in place.`
262
+ + `\n\`\`\`\n${describeKeyDifference({ before, after })}\n\`\`\``
263
+ + `${why && `\n${why}` || ""}`
197
264
  );
198
265
  }
@@ -17,14 +17,26 @@ export type SourceState = {
17
17
  reportedProblem: string;
18
18
  };
19
19
 
20
- /** A key this machine has stopped accepting. Kept even when the revoke repo no longer lists it:
21
- the deploy key that writes revocations lives on every server, so an attacker who took one could
22
- otherwise delete the revocation that locked them out. */
20
+ /** One revocation event: this key, used from this address, so it is no longer accepted. Kept even
21
+ when the revoke repo no longer lists it: the deploy key that writes revocations lives on every
22
+ server, so an attacker who took one could otherwise delete the revocation that locked them out.
23
+
24
+ The id is unique to the event, never derived from the key. A key that has been forgiven for one
25
+ address is still an ordinary key, and using it from some other address revokes it again, with a
26
+ new id that no existing unrevoke names. */
23
27
  export type RevocationState = {
24
- fingerprint: string;
25
28
  revocationId: string;
26
- // Set once an unrevoke has been seen, so the wait can be served out across restarts.
27
- unrevokeSeenAt: number;
29
+ fingerprint: string;
30
+ // The address it was used from. Half of what a revocation says, and the half an unrevoke has
31
+ // to name to undo it.
32
+ ip: string;
33
+ // Why this key was revoked, carried from the revocation file so a machine that reads one
34
+ // written elsewhere can say what happened rather than only that something did.
35
+ revokedAt: string;
36
+ revokedBy: string;
37
+ reason: string;
38
+ // Which unrevoke allowed this key from this address, once one has been seen. When the wait it
39
+ // started runs out is in unrevokeFirstSeen, which outlives the daemon.
28
40
  unrevokeId: string;
29
41
  unrevoked: boolean;
30
42
  // Whether we have said that this key actually left root's authorized_keys. The removal is the
@@ -50,7 +62,12 @@ export type DaemonState = {
50
62
  // revocations is on every server, so whoever stole one could otherwise erase the record that
51
63
  // shut them out. Restarting the daemon does clear it, which is the way back from a revocation
52
64
  // that should not have happened.
53
- revocations: { [fingerprint: string]: RevocationState };
65
+ revocations: { [revocationId: string]: RevocationState };
66
+ // When this machine first saw each unrevoke, by unrevoke id. On disk, unlike the revocations
67
+ // themselves, because it is what the hour long wait is counted from: measuring from a time the
68
+ // unrevoke file states would let whoever wrote it backdate the file and skip the wait, and
69
+ // measuring from process start would restart the wait on every restart.
70
+ unrevokeFirstSeen: { [unrevokeId: string]: number };
54
71
  pendingRevocations: PendingRevocation[];
55
72
  // The keys we last wrote out. What tells a change we made apart from one somebody else made:
56
73
  // if this still matches what we want, and the file does not, the file was edited behind us.
@@ -64,6 +81,7 @@ let state: DaemonState = {
64
81
  sources: {},
65
82
  userKeyHashes: {},
66
83
  revocations: {},
84
+ unrevokeFirstSeen: {},
67
85
  pendingRevocations: [],
68
86
  appliedKeys: [],
69
87
  authLogOffset: 0,
@@ -174,21 +174,31 @@ async function verifyCheckoutSigner(config: {
174
174
  return publicKey;
175
175
  }
176
176
 
177
+ /** Proves a checkout on disk was signed, and that the files in it are the ones that were signed.
178
+ Returns the signer, and throws if anything about that does not hold.
179
+
180
+ Anything reading a repo for something other than ssh keys goes through this first: the whole
181
+ checkout is covered by one manifest, so a machine list is exactly as trustworthy as the keys
182
+ beside it, and neither is worth reading unsigned. */
183
+ export async function verifyCheckout(repoPath: string) {
184
+ return await verifyCheckoutSigner({ repoPath, files: await readSignatureFiles(repoPath) });
185
+ }
186
+
177
187
  export function describeSigner(signer: string) {
178
188
  return signer === UNSIGNED && "<no public key>" || signer;
179
189
  }
180
190
 
181
191
  /** Reports a problem with a source's signature, but only when it is not the same problem we
182
192
  already reported, so a fault that persists does not repeat every check. */
183
- async function reportProblem(config: { repoURL: string; problem: string; message: string }) {
184
- let { repoURL, problem, message } = config;
193
+ async function reportProblem(config: { repoURL: string; problem: string; headline: string; body: string }) {
194
+ let { repoURL, problem, headline, body } = config;
185
195
  let sourceStateValue = sourceState(repoURL);
186
196
  if (sourceStateValue.reportedProblem === problem) {
187
197
  return;
188
198
  }
189
199
  sourceStateValue.reportedProblem = problem;
190
200
  await saveState();
191
- await notify(message);
201
+ await notify(headline, body);
192
202
  }
193
203
 
194
204
  /** The keys a source is allowed to contribute right now. A source signed by someone we have not
@@ -210,17 +220,24 @@ export async function resolveSourceKeys(repoURL: string) {
210
220
  await reportProblem({
211
221
  repoURL,
212
222
  problem: "stale",
213
- message: `\`${repoURL}\` changed but its signature was not updated, so the changes are being`
214
- + ` ignored. Still using the keys signed by \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
215
- + `\nTo deploy the change, run this in that repo:\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
223
+ headline: `KEY REPO CHANGED WITHOUT BEING SIGNED`,
224
+ body: `\`${repoURL}\` changed, but nobody signed the change, so it is being ignored`
225
+ + ` and this machine is still using the keys signed by`
226
+ + ` \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
227
+ + `\n\nTo make the change take effect, run this in that repo:`
228
+ + `\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
216
229
  });
217
230
  } else {
218
231
  await reportProblem({
219
232
  repoURL,
220
233
  problem: "corrupt",
221
- message: `\`${repoURL}\` has a corrupted signature, so its contents are being ignored:`
222
- + ` ${e}.\nStill using the keys signed by \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
223
- + `\nTo replace it, run this in that repo:\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
234
+ headline: `KEY REPO SIGNATURE IS BROKEN`,
235
+ body: `The signature on \`${repoURL}\` does not check out, so everything in it is`
236
+ + ` being ignored and this machine is still using the keys signed by`
237
+ + ` \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
238
+ + `\n\n${e}`
239
+ + `\n\nTo replace the signature, run this in that repo:`
240
+ + `\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
224
241
  });
225
242
  }
226
243
  return sourceStateValue.acceptedKeys;
@@ -259,9 +276,10 @@ export async function resolveSourceKeys(repoURL: string) {
259
276
 
260
277
  // Going from nothing to something signed is only ever an improvement, so it does not wait.
261
278
  if (sourceStateValue.acceptedSigner === UNSIGNED) {
262
- await notify(
263
- `\`${repoURL}\` is now signed, by \`${signer}\`. It was not signed before, so its keys are`
264
- + ` being applied right away.`
279
+ await notify(`KEY REPO IS NOW SIGNED`,
280
+ `\`${repoURL}\` was not signed at all before, so this is an improvement and its keys`
281
+ + ` are being applied right away.`
282
+ + `\n\nsigned by: \`${signer}\``
265
283
  );
266
284
  return await accept();
267
285
  }
@@ -273,10 +291,13 @@ export async function resolveSourceKeys(repoURL: string) {
273
291
  sourceStateValue.pendingSigner = signer;
274
292
  sourceStateValue.pendingSince = Date.now();
275
293
  await saveState();
276
- await notify(
277
- `\`${repoURL}\` is now signed by \`${describeSigner(signer)}\`, where it was signed by`
278
- + ` \`${describeSigner(sourceStateValue.acceptedSigner)}\`. Its keys are NOT being applied.`
279
- + ` If nothing changes they will be applied in 24 hours.`
294
+ await notify(`KEY REPO SIGNED BY A DIFFERENT KEY`,
295
+ `Somebody else is signing \`${repoURL}\` now. Its keys are NOT being applied, and this`
296
+ + ` machine is still using the ones it already had. If nothing changes, the new signer`
297
+ + ` is accepted in 24 hours.`
298
+ + `\n\nIf that was not you, fix the repo before the 24 hours are up.`
299
+ + `\n\nsigned by now: \`${describeSigner(signer)}\``
300
+ + `\nsigned by before: \`${describeSigner(sourceStateValue.acceptedSigner)}\``
280
301
  );
281
302
  return sourceStateValue.acceptedKeys;
282
303
  }
@@ -65,10 +65,11 @@ export async function checkOtherUserKeys() {
65
65
  if (previousHash === undefined || previousHash === hash) {
66
66
  continue;
67
67
  }
68
- await notify(
69
- `authorized_keys for user \`${entry.name}\` changed (\`${entry.filePath}\`).`
70
- + ` portsecure does not manage this account, so the change was left in place.`
71
- + `\n\`\`\`\n${keys.map(summarizeKey).join("\n") || "(now empty)"}\n\`\`\``
68
+ await notify(`SSH KEYS CHANGED FOR USER ${entry.name}`,
69
+ `Somebody changed who can log in as \`${entry.name}\`. portsecure does not manage that`
70
+ + ` account, so the change was left alone. Who can log in as \`${entry.name}\` now:`
71
+ + `\n\`\`\`\n${keys.map(summarizeKey).join("\n") || "(nobody, the file is now empty)"}\n\`\`\``
72
+ + `\n${entry.filePath}`
72
73
  );
73
74
  }
74
75
  state.userKeyHashes = hashes;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
6
- //exports.readRepoKeys = exports.findKeyProblems = exports.summarizeKey = exports.keyRestriction = exports.keyRestrictionList = exports.NO_RESTRICTION = exports.keyFingerprint = exports.normalizeKeys = void 0;
6
+ //exports.readRepoKeys = exports.findKeyProblems = exports.keyNiceName = exports.summarizeKey = exports.allowAddresses = exports.keyRestriction = exports.keyRestrictionList = exports.NO_RESTRICTION = exports.keyFingerprint = exports.normalizeKeys = void 0;
7
7
  const crypto_1 = __importDefault(require("crypto"));
8
8
  const promises_1 = __importDefault(require("fs/promises"));
9
9
  const path_1 = __importDefault(require("path"));
@@ -44,6 +44,27 @@ function keyRestriction(keyLine) {
44
44
  return list.join(",");
45
45
  }
46
46
  exports.keyRestriction = keyRestriction;
47
+ /** Adds addresses to a key's from= list, leaving the rest of the line exactly as it was. Returns
48
+ which addresses were actually new, so a caller can report only what it changed.
49
+
50
+ A key with no from= at all is left alone: adding one would silently restrict a key that is
51
+ currently unrestricted, which is a different decision than the one being made here. */
52
+ function allowAddresses(keyLine, addresses) {
53
+ let list = keyRestrictionList(keyLine);
54
+ if (!list) {
55
+ return { keyLine, added: [] };
56
+ }
57
+ let allowed = list;
58
+ let added = addresses.filter(address => address && !allowed.includes(address));
59
+ if (!added.length) {
60
+ return { keyLine, added };
61
+ }
62
+ return {
63
+ keyLine: keyLine.replace(/from="[^"]*"/, `from="${[...allowed, ...added].join(",")}"`),
64
+ added,
65
+ };
66
+ }
67
+ exports.allowAddresses = allowAddresses;
47
68
  /** Enough to recognise whose key this is without printing the whole blob. */
48
69
  function summarizeKey(keyLine) {
49
70
  let parts = keyLine.trim().split(/\s+/);
@@ -57,6 +78,19 @@ function summarizeKey(keyLine) {
57
78
  return `${type} ...${blob.slice(-12)}${comment && ` ${comment}` || ""}`;
58
79
  }
59
80
  exports.summarizeKey = summarizeKey;
81
+ /** The shortest thing that still identifies a key to a person: the comment it carries, which is
82
+ usually user@machine, and otherwise the tail of the key itself. For the first line of a
83
+ notification, where there is only room for the one thing that matters. */
84
+ function keyNiceName(keyLine) {
85
+ let parts = keyLine.trim().split(/\s+/);
86
+ let typeIndex = parts.findIndex(part => /^(ssh-|ecdsa-|sk-)/.test(part));
87
+ if (typeIndex < 0) {
88
+ return keyLine.slice(0, 20);
89
+ }
90
+ let comment = parts.slice(typeIndex + 2).join(" ");
91
+ return comment || `...${(parts[typeIndex + 1] || "").slice(-8)}`;
92
+ }
93
+ exports.keyNiceName = keyNiceName;
60
94
  /** What a set of keys has to satisfy before it is worth signing, as a list of complaints.
61
95
 
62
96
  Every key needs a from=, because an unrestricted key can never be caught being used from the
@@ -107,5 +141,5 @@ async function readRepoKeys(repoPath) {
107
141
  return keys;
108
142
  }
109
143
  exports.readRepoKeys = readRepoKeys;
110
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXplZEtleXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhdXRob3JpemVkS2V5cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUV4QixTQUFnQixhQUFhLENBQUMsUUFBZ0I7SUFDMUMsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN2RyxDQUFDO0FBRkQsc0NBRUM7QUFFRDs4RUFDOEU7QUFDOUUsU0FBZ0IsY0FBYyxDQUFDLE9BQWU7SUFDMUMsSUFBSSxLQUFLLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN4QyxJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDekUsSUFBSSxJQUFJLEdBQUcsU0FBUyxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN4RCxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDUixPQUFPLEVBQUUsQ0FBQztJQUNkLENBQUM7SUFDRCxPQUFPLFNBQVMsR0FBRyxnQkFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsT0FB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QUFDO1FBQ25CLE1BQU0sSUFBSSxLQUFLLENBQUMseURBQXlELFFBQVEsaUJBQWlCLENBQUMsQ0FBQztJQUN4RyxDQUFDO0lBQ0QsSUFBSSxJQUFJLEdBQWEsRUFBRSxDQUFDO0lBQ3hCLEtBQUssSUFBSSxJQUFJLElBQUksUUFBUSxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLGFBQWEsQ0FBQyxNQUFNLGtCQUFFLENBQUMsUUFBUSxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDaEIsQ0FBQztBQWZELG9DQWVDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNyeXB0byBmcm9tIFwiY3J5cHRvXCI7XG5pbXBvcnQgZnMgZnJvbSBcImZzL3Byb21pc2VzXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplS2V5cyhjb250ZW50czogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGNvbnRlbnRzLnNwbGl0KFwiXFxuXCIpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKS5maWx0ZXIobGluZSA9PiBsaW5lICYmICFsaW5lLnN0YXJ0c1dpdGgoXCIjXCIpKTtcbn1cblxuLyoqIFRoZSBmaW5nZXJwcmludCBzc2ggaXRzZWxmIHJlcG9ydHMgZm9yIGEga2V5LCB3aGljaCBpcyB3aGF0IHRoZSBzc2hkIGxvZyBuYW1lcyBhbmQgdGhlcmVmb3JlXG4gICAgd2hhdCBhIHJldm9jYXRpb24gaXMga2V5ZWQgYnkuIFJldHVybnMgXCJcIiBmb3IgYSBsaW5lIHRoYXQgaG9sZHMgbm8ga2V5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleUZpbmdlcnByaW50KGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBwYXJ0cyA9IGtleUxpbmUudHJpbSgpLnNwbGl0KC9cXHMrLyk7XG4gICAgbGV0IHR5cGVJbmRleCA9IHBhcnRzLmZpbmRJbmRleChwYXJ0ID0+IC9eKHNzaC18ZWNkc2EtfHNrLSkvLnRlc3QocGFydCkpO1xuICAgIGxldCBibG9iID0gdHlwZUluZGV4ID49IDAgJiYgcGFydHNbdHlwZUluZGV4ICsgMV0gfHwgXCJcIjtcbiAgICBpZiAoIWJsb2IpIHtcbiAgICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBcIlNIQTI1NjpcIiArIGNyeXB0by5jcmVhdGVIYXNoKFwic2hhMjU2XCIpLnVwZGF0ZShCdWZmZXIuZnJvbShibG9iLCBcImJhc2U2NFwiKSkuZGlnZXN0KFwiYmFzZTY0XCIpLnJlcGxhY2UoLz0rJC8sIFwiXCIpO1xufVxuXG5leHBvcnQgY29uc3QgTk9fUkVTVFJJQ1RJT04gPSBcIkFOWSBBRERSRVNTIChubyBmcm9tPSByZXN0cmljdGlvbilcIjtcblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgb25lIGJ5IG9uZSwgb3IgdW5kZWZpbmVkIHdoZW4gdGhlIGtleSBjYXJyaWVzIG5vIGZyb209XG4gICAgYXQgYWxsLiBVbmRlZmluZWQgYW5kIGFuIGVtcHR5IGxpc3QgYXJlIHZlcnkgZGlmZmVyZW50IHRoaW5ncywgc28gdGhleSBzdGF5IGRpc3Rpbmd1aXNoYWJsZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBrZXlSZXN0cmljdGlvbkxpc3Qoa2V5TGluZTogc3RyaW5nKSB7XG4gICAgbGV0IG1hdGNoID0ga2V5TGluZS5tYXRjaCgvZnJvbT1cIihbXlwiXSopXCIvKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiBtYXRjaFsxXS5zcGxpdChcIixcIikubWFwKGVudHJ5ID0+IGVudHJ5LnRyaW0oKSkuZmlsdGVyKGVudHJ5ID0+IGVudHJ5KTtcbn1cblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgd2hpY2ggaXMgdGhlIHBhcnQgb2YgYW4gYXV0aG9yaXplZF9rZXlzIGxpbmUgdGhhdFxuICAgIGRlY2lkZXMgaG93IG11Y2ggYSBzdG9sZW4ga2V5IGlzIHdvcnRoLiBBIGtleSB3aXRoIG5vIHJlc3RyaWN0aW9uIHNheXMgc28gbG91ZGx5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleVJlc3RyaWN0aW9uKGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBsaXN0ID0ga2V5UmVzdHJpY3Rpb25MaXN0KGtleUxpbmUpO1xuICAgIGlmICghbGlzdCkge1xuICAgICAgICByZXR1cm4gTk9fUkVTVFJJQ1RJT047XG4gICAgfVxuICAgIHJldHVybiBsaXN0LmpvaW4oXCIsXCIpO1xufVxuXG4vKiogRW5vdWdoIHRvIHJlY29nbmlzZSB3aG9zZSBrZXkgdGhpcyBpcyB3aXRob3V0IHByaW50aW5nIHRoZSB3aG9sZSBibG9iLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcml6ZUtleShrZXlMaW5lOiBzdHJpbmcpIHtcbiAgICBsZXQgcGFydHMgPSBrZXlMaW5lLnRyaW0oKS5zcGxpdCgvXFxzKy8pO1xuICAgIGxldCB0eXBlSW5kZXggPSBwYXJ0cy5maW5kSW5kZXgocGFydCA9PiAvXihzc2gtfGVjZHNhLXxzay0pLy50ZXN0KHBhcnQpKTtcbiAgICBpZiAodHlwZUluZGV4IDwgMCkge1xuICAgICAgICByZXR1cm4ga2V5TGluZS5zbGljZSgwLCA2MCk7XG4gICAgfVxuICAgIGxldCB0eXBlID0gcGFydHNbdHlwZUluZGV4XTtcbiAgICBsZXQgYmxvYiA9IHBhcnRzW3R5cGVJbmRleCArIDFdIHx8IFwiXCI7XG4gICAgbGV0IGNvbW1lbnQgPSBwYXJ0cy5zbGljZSh0eXBlSW5kZXggKyAyKS5qb2luKFwiIFwiKTtcbiAgICByZXR1cm4gYCR7dHlwZX0gLi4uJHtibG9iLnNsaWNlKC0xMil9JHtjb21tZW50ICYmIGAgJHtjb21tZW50fWAgfHwgXCJcIn1gO1xufVxuXG4vKiogV2hhdCBhIHNldCBvZiBrZXlzIGhhcyB0byBzYXRpc2Z5IGJlZm9yZSBpdCBpcyB3b3J0aCBzaWduaW5nLCBhcyBhIGxpc3Qgb2YgY29tcGxhaW50cy5cblxuICAgIEV2ZXJ5IGtleSBuZWVkcyBhIGZyb209LCBiZWNhdXNlIGFuIHVucmVzdHJpY3RlZCBrZXkgY2FuIG5ldmVyIGJlIGNhdWdodCBiZWluZyB1c2VkIGZyb20gdGhlXG4gICAgd3JvbmcgcGxhY2UsIGFuZCBiZWluZyBjYXVnaHQgaXMgdGhlIG9ubHkgdGhpbmcgdGhhdCB0cmlnZ2VycyBhIHJldm9jYXRpb24uXG5cbiAgICBObyB0d28ga2V5cyBtYXkgYWxsb3cgdGhlIHNhbWUgYWRkcmVzc2VzLCBiZWNhdXNlIHRoYXQgaXMgb25lIHBlcnNvbiBob2xkaW5nIHR3byBrZXlzOiByZXZva2luZ1xuICAgIG9uZSBvZiB0aGVtIGxlYXZlcyB0aGUgb3RoZXIgd29ya2luZywgc28gdGhlIHJldm9jYXRpb24gYWNoaWV2ZXMgbm90aGluZy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaW5kS2V5UHJvYmxlbXMoa2V5czogc3RyaW5nW10pIHtcbiAgICBsZXQgcHJvYmxlbXM6IHN0cmluZ1tdID0gW107XG4gICAgbGV0IGJ5UmVzdHJpY3Rpb24gPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nW10+KCk7XG4gICAgZm9yIChsZXQga2V5IG9mIGtleXMpIHtcbiAgICAgICAgbGV0IHJlc3RyaWN0aW9uID0ga2V5UmVzdHJpY3Rpb25MaXN0KGtleSk7XG4gICAgICAgIGlmICghcmVzdHJpY3Rpb24pIHtcbiAgICAgICAgICAgIHByb2JsZW1zLnB1c2goYG5vIGZyb209IHJlc3RyaWN0aW9uLCBzbyBpdCBjYW4gYmUgdXNlZCBmcm9tIGFueXdoZXJlOlxcbiAgICAgICR7c3VtbWFyaXplS2V5KGtleSl9YCk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBTb3J0ZWQsIHNvIHRoZSBzYW1lIGFkZHJlc3NlcyB3cml0dGVuIGluIGEgZGlmZmVyZW50IG9yZGVyIHN0aWxsIGNvdW50IGFzIHRoZSBzYW1lLlxuICAgICAgICBsZXQgaWRlbnRpdHkgPSBbLi4ucmVzdHJpY3Rpb25dLnNvcnQoKS5qb2luKFwiLFwiKTtcbiAgICAgICAgYnlSZXN0cmljdGlvbi5zZXQoaWRlbnRpdHksIFsuLi4oYnlSZXN0cmljdGlvbi5nZXQoaWRlbnRpdHkpIHx8IFtdKSwga2V5XSk7XG4gICAgfVxuICAgIGZvciAobGV0IFtyZXN0cmljdGlvbiwgc2hhcmluZ10gb2YgYnlSZXN0cmljdGlvbikge1xuICAgICAgICBpZiAoc2hhcmluZy5sZW5ndGggPCAyKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBwcm9ibGVtcy5wdXNoKFxuICAgICAgICAgICAgYCR7c2hhcmluZy5sZW5ndGh9IGtleXMgYWxsb3cgZXhhY3RseSB0aGUgc2FtZSBhZGRyZXNzZXMgKCR7cmVzdHJpY3Rpb259KSwgd2hpY2ggaXMgb25lYFxuICAgICAgICAgICAgKyBgIHBlcnNvbiBob2xkaW5nIG1vcmUgdGhhbiBvbmUga2V5OlxcbmBcbiAgICAgICAgICAgICsgc2hhcmluZy5tYXAoa2V5ID0+IGAgICAgICAke3N1bW1hcml6ZUtleShrZXkpfWApLmpvaW4oXCJcXG5cIilcbiAgICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHByb2JsZW1zO1xufVxuXG4vKiogUmVhZHMgdGhlIGF1dGhvcml6ZWQga2V5cyBhIHJlcG8gY2hlY2tvdXQgd2FudHMgYXBwbGllZC4gUHJlZmVycyBhIHRvcCBsZXZlbCBhdXRob3JpemVkX2tleXNcbiAgICBmaWxlIGFuZCBvdGhlcndpc2UgY29uY2F0ZW5hdGVzIGV2ZXJ5IC5wdWIgYXQgdGhlIHRvcCBsZXZlbC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkUmVwb0tleXMocmVwb1BhdGg6IHN0cmluZykge1xuICAgIGxldCBjb21iaW5lZFBhdGggPSBwYXRoLmpvaW4ocmVwb1BhdGgsIFwiYXV0aG9yaXplZF9rZXlzXCIpO1xuICAgIGxldCBlbnRyaWVzID0gYXdhaXQgZnMucmVhZGRpcihyZXBvUGF0aCk7XG4gICAgaWYgKGVudHJpZXMuaW5jbHVkZXMoXCJhdXRob3JpemVkX2tleXNcIikpIHtcbiAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZUtleXMoYXdhaXQgZnMucmVhZEZpbGUoY29tYmluZWRQYXRoLCBcInV0ZjhcIikpO1xuICAgIH1cbiAgICBsZXQgcHViRmlsZXMgPSBlbnRyaWVzLmZpbHRlcihuYW1lID0+IG5hbWUuZW5kc1dpdGgoXCIucHViXCIpKS5zb3J0KCk7XG4gICAgaWYgKCFwdWJGaWxlcy5sZW5ndGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBhdXRob3JpemVkX2tleXMgb3IgYXQgbGVhc3Qgb25lIC5wdWIgZmlsZSBpbiAke3JlcG9QYXRofSwgZm91bmQgbmVpdGhlcmApO1xuICAgIH1cbiAgICBsZXQga2V5czogc3RyaW5nW10gPSBbXTtcbiAgICBmb3IgKGxldCBuYW1lIG9mIHB1YkZpbGVzKSB7XG4gICAgICAgIGtleXMucHVzaCguLi5ub3JtYWxpemVLZXlzKGF3YWl0IGZzLnJlYWRGaWxlKHBhdGguam9pbihyZXBvUGF0aCwgbmFtZSksIFwidXRmOFwiKSkpO1xuICAgIH1cbiAgICByZXR1cm4ga2V5cztcbn1cbiJdfQ==
111
- /* _JS_SOURCE_HASH = "a5147a2eb954468a9ddfcac36189a744f5b17f944d7e837ce0a525c780093a26"; */
144
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXplZEtleXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhdXRob3JpemVkS2V5cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUV4QixTQUFnQixhQUFhLENBQUMsUUFBZ0I7SUFDMUMsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN2RyxDQUFDO0FBRkQsc0NBRUM7QUFFRDs4RUFDOEU7QUFDOUUsU0FBZ0IsY0FBYyxDQUFDLE9BQWU7SUFDMUMsSUFBSSxLQUFLLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN4QyxJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDekUsSUFBSSxJQUFJLEdBQUcsU0FBUyxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN4RCxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDUixPQUFPLEVBQUUsQ0FBQztJQUNkLENBQUM7SUFDRCxPQUFPLFNBQVMsR0FBRyxnQkFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsT0FB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QUFFLENBQUM7UUFDdEMsT0FBTyxhQUFhLENBQUMsTUFBTSxrQkFBRSxDQUFDLFFBQVEsQ0FBQyxZQUFZLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBQ0QsSUFBSSxRQUFRLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNwRSxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ25CLE1BQU0sSUFBSSxLQUFLLENBQUMseURBQXlELFFBQVEsaUJBQWlCLENBQUMsQ0FBQztJQUN4RyxDQUFDO0lBQ0QsSUFBSSxJQUFJLEdBQWEsRUFBRSxDQUFDO0lBQ3hCLEtBQUssSUFBSSxJQUFJLElBQUksUUFBUSxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLGFBQWEsQ0FBQyxNQUFNLGtCQUFFLENBQUMsUUFBUSxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDaEIsQ0FBQztBQWZELG9DQWVDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNyeXB0byBmcm9tIFwiY3J5cHRvXCI7XG5pbXBvcnQgZnMgZnJvbSBcImZzL3Byb21pc2VzXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplS2V5cyhjb250ZW50czogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGNvbnRlbnRzLnNwbGl0KFwiXFxuXCIpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKS5maWx0ZXIobGluZSA9PiBsaW5lICYmICFsaW5lLnN0YXJ0c1dpdGgoXCIjXCIpKTtcbn1cblxuLyoqIFRoZSBmaW5nZXJwcmludCBzc2ggaXRzZWxmIHJlcG9ydHMgZm9yIGEga2V5LCB3aGljaCBpcyB3aGF0IHRoZSBzc2hkIGxvZyBuYW1lcyBhbmQgdGhlcmVmb3JlXG4gICAgd2hhdCBhIHJldm9jYXRpb24gaXMga2V5ZWQgYnkuIFJldHVybnMgXCJcIiBmb3IgYSBsaW5lIHRoYXQgaG9sZHMgbm8ga2V5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleUZpbmdlcnByaW50KGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBwYXJ0cyA9IGtleUxpbmUudHJpbSgpLnNwbGl0KC9cXHMrLyk7XG4gICAgbGV0IHR5cGVJbmRleCA9IHBhcnRzLmZpbmRJbmRleChwYXJ0ID0+IC9eKHNzaC18ZWNkc2EtfHNrLSkvLnRlc3QocGFydCkpO1xuICAgIGxldCBibG9iID0gdHlwZUluZGV4ID49IDAgJiYgcGFydHNbdHlwZUluZGV4ICsgMV0gfHwgXCJcIjtcbiAgICBpZiAoIWJsb2IpIHtcbiAgICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBcIlNIQTI1NjpcIiArIGNyeXB0by5jcmVhdGVIYXNoKFwic2hhMjU2XCIpLnVwZGF0ZShCdWZmZXIuZnJvbShibG9iLCBcImJhc2U2NFwiKSkuZGlnZXN0KFwiYmFzZTY0XCIpLnJlcGxhY2UoLz0rJC8sIFwiXCIpO1xufVxuXG5leHBvcnQgY29uc3QgTk9fUkVTVFJJQ1RJT04gPSBcIkFOWSBBRERSRVNTIChubyBmcm9tPSByZXN0cmljdGlvbilcIjtcblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgb25lIGJ5IG9uZSwgb3IgdW5kZWZpbmVkIHdoZW4gdGhlIGtleSBjYXJyaWVzIG5vIGZyb209XG4gICAgYXQgYWxsLiBVbmRlZmluZWQgYW5kIGFuIGVtcHR5IGxpc3QgYXJlIHZlcnkgZGlmZmVyZW50IHRoaW5ncywgc28gdGhleSBzdGF5IGRpc3Rpbmd1aXNoYWJsZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBrZXlSZXN0cmljdGlvbkxpc3Qoa2V5TGluZTogc3RyaW5nKSB7XG4gICAgbGV0IG1hdGNoID0ga2V5TGluZS5tYXRjaCgvZnJvbT1cIihbXlwiXSopXCIvKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiBtYXRjaFsxXS5zcGxpdChcIixcIikubWFwKGVudHJ5ID0+IGVudHJ5LnRyaW0oKSkuZmlsdGVyKGVudHJ5ID0+IGVudHJ5KTtcbn1cblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgd2hpY2ggaXMgdGhlIHBhcnQgb2YgYW4gYXV0aG9yaXplZF9rZXlzIGxpbmUgdGhhdFxuICAgIGRlY2lkZXMgaG93IG11Y2ggYSBzdG9sZW4ga2V5IGlzIHdvcnRoLiBBIGtleSB3aXRoIG5vIHJlc3RyaWN0aW9uIHNheXMgc28gbG91ZGx5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleVJlc3RyaWN0aW9uKGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBsaXN0ID0ga2V5UmVzdHJpY3Rpb25MaXN0KGtleUxpbmUpO1xuICAgIGlmICghbGlzdCkge1xuICAgICAgICByZXR1cm4gTk9fUkVTVFJJQ1RJT047XG4gICAgfVxuICAgIHJldHVybiBsaXN0LmpvaW4oXCIsXCIpO1xufVxuXG4vKiogQWRkcyBhZGRyZXNzZXMgdG8gYSBrZXkncyBmcm9tPSBsaXN0LCBsZWF2aW5nIHRoZSByZXN0IG9mIHRoZSBsaW5lIGV4YWN0bHkgYXMgaXQgd2FzLiBSZXR1cm5zXG4gICAgd2hpY2ggYWRkcmVzc2VzIHdlcmUgYWN0dWFsbHkgbmV3LCBzbyBhIGNhbGxlciBjYW4gcmVwb3J0IG9ubHkgd2hhdCBpdCBjaGFuZ2VkLlxuXG4gICAgQSBrZXkgd2l0aCBubyBmcm9tPSBhdCBhbGwgaXMgbGVmdCBhbG9uZTogYWRkaW5nIG9uZSB3b3VsZCBzaWxlbnRseSByZXN0cmljdCBhIGtleSB0aGF0IGlzXG4gICAgY3VycmVudGx5IHVucmVzdHJpY3RlZCwgd2hpY2ggaXMgYSBkaWZmZXJlbnQgZGVjaXNpb24gdGhhbiB0aGUgb25lIGJlaW5nIG1hZGUgaGVyZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhbGxvd0FkZHJlc3NlcyhrZXlMaW5lOiBzdHJpbmcsIGFkZHJlc3Nlczogc3RyaW5nW10pIHtcbiAgICBsZXQgbGlzdCA9IGtleVJlc3RyaWN0aW9uTGlzdChrZXlMaW5lKTtcbiAgICBpZiAoIWxpc3QpIHtcbiAgICAgICAgcmV0dXJuIHsga2V5TGluZSwgYWRkZWQ6IFtdIGFzIHN0cmluZ1tdIH07XG4gICAgfVxuICAgIGxldCBhbGxvd2VkID0gbGlzdDtcbiAgICBsZXQgYWRkZWQgPSBhZGRyZXNzZXMuZmlsdGVyKGFkZHJlc3MgPT4gYWRkcmVzcyAmJiAhYWxsb3dlZC5pbmNsdWRlcyhhZGRyZXNzKSk7XG4gICAgaWYgKCFhZGRlZC5sZW5ndGgpIHtcbiAgICAgICAgcmV0dXJuIHsga2V5TGluZSwgYWRkZWQgfTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgICAga2V5TGluZToga2V5TGluZS5yZXBsYWNlKC9mcm9tPVwiW15cIl0qXCIvLCBgZnJvbT1cIiR7Wy4uLmFsbG93ZWQsIC4uLmFkZGVkXS5qb2luKFwiLFwiKX1cImApLFxuICAgICAgICBhZGRlZCxcbiAgICB9O1xufVxuXG4vKiogRW5vdWdoIHRvIHJlY29nbmlzZSB3aG9zZSBrZXkgdGhpcyBpcyB3aXRob3V0IHByaW50aW5nIHRoZSB3aG9sZSBibG9iLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcml6ZUtleShrZXlMaW5lOiBzdHJpbmcpIHtcbiAgICBsZXQgcGFydHMgPSBrZXlMaW5lLnRyaW0oKS5zcGxpdCgvXFxzKy8pO1xuICAgIGxldCB0eXBlSW5kZXggPSBwYXJ0cy5maW5kSW5kZXgocGFydCA9PiAvXihzc2gtfGVjZHNhLXxzay0pLy50ZXN0KHBhcnQpKTtcbiAgICBpZiAodHlwZUluZGV4IDwgMCkge1xuICAgICAgICByZXR1cm4ga2V5TGluZS5zbGljZSgwLCA2MCk7XG4gICAgfVxuICAgIGxldCB0eXBlID0gcGFydHNbdHlwZUluZGV4XTtcbiAgICBsZXQgYmxvYiA9IHBhcnRzW3R5cGVJbmRleCArIDFdIHx8IFwiXCI7XG4gICAgbGV0IGNvbW1lbnQgPSBwYXJ0cy5zbGljZSh0eXBlSW5kZXggKyAyKS5qb2luKFwiIFwiKTtcbiAgICByZXR1cm4gYCR7dHlwZX0gLi4uJHtibG9iLnNsaWNlKC0xMil9JHtjb21tZW50ICYmIGAgJHtjb21tZW50fWAgfHwgXCJcIn1gO1xufVxuXG4vKiogVGhlIHNob3J0ZXN0IHRoaW5nIHRoYXQgc3RpbGwgaWRlbnRpZmllcyBhIGtleSB0byBhIHBlcnNvbjogdGhlIGNvbW1lbnQgaXQgY2Fycmllcywgd2hpY2ggaXNcbiAgICB1c3VhbGx5IHVzZXJAbWFjaGluZSwgYW5kIG90aGVyd2lzZSB0aGUgdGFpbCBvZiB0aGUga2V5IGl0c2VsZi4gRm9yIHRoZSBmaXJzdCBsaW5lIG9mIGFcbiAgICBub3RpZmljYXRpb24sIHdoZXJlIHRoZXJlIGlzIG9ubHkgcm9vbSBmb3IgdGhlIG9uZSB0aGluZyB0aGF0IG1hdHRlcnMuICovXG5leHBvcnQgZnVuY3Rpb24ga2V5TmljZU5hbWUoa2V5TGluZTogc3RyaW5nKSB7XG4gICAgbGV0IHBhcnRzID0ga2V5TGluZS50cmltKCkuc3BsaXQoL1xccysvKTtcbiAgICBsZXQgdHlwZUluZGV4ID0gcGFydHMuZmluZEluZGV4KHBhcnQgPT4gL14oc3NoLXxlY2RzYS18c2stKS8udGVzdChwYXJ0KSk7XG4gICAgaWYgKHR5cGVJbmRleCA8IDApIHtcbiAgICAgICAgcmV0dXJuIGtleUxpbmUuc2xpY2UoMCwgMjApO1xuICAgIH1cbiAgICBsZXQgY29tbWVudCA9IHBhcnRzLnNsaWNlKHR5cGVJbmRleCArIDIpLmpvaW4oXCIgXCIpO1xuICAgIHJldHVybiBjb21tZW50IHx8IGAuLi4keyhwYXJ0c1t0eXBlSW5kZXggKyAxXSB8fCBcIlwiKS5zbGljZSgtOCl9YDtcbn1cblxuLyoqIFdoYXQgYSBzZXQgb2Yga2V5cyBoYXMgdG8gc2F0aXNmeSBiZWZvcmUgaXQgaXMgd29ydGggc2lnbmluZywgYXMgYSBsaXN0IG9mIGNvbXBsYWludHMuXG5cbiAgICBFdmVyeSBrZXkgbmVlZHMgYSBmcm9tPSwgYmVjYXVzZSBhbiB1bnJlc3RyaWN0ZWQga2V5IGNhbiBuZXZlciBiZSBjYXVnaHQgYmVpbmcgdXNlZCBmcm9tIHRoZVxuICAgIHdyb25nIHBsYWNlLCBhbmQgYmVpbmcgY2F1Z2h0IGlzIHRoZSBvbmx5IHRoaW5nIHRoYXQgdHJpZ2dlcnMgYSByZXZvY2F0aW9uLlxuXG4gICAgTm8gdHdvIGtleXMgbWF5IGFsbG93IHRoZSBzYW1lIGFkZHJlc3NlcywgYmVjYXVzZSB0aGF0IGlzIG9uZSBwZXJzb24gaG9sZGluZyB0d28ga2V5czogcmV2b2tpbmdcbiAgICBvbmUgb2YgdGhlbSBsZWF2ZXMgdGhlIG90aGVyIHdvcmtpbmcsIHNvIHRoZSByZXZvY2F0aW9uIGFjaGlldmVzIG5vdGhpbmcuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZEtleVByb2JsZW1zKGtleXM6IHN0cmluZ1tdKSB7XG4gICAgbGV0IHByb2JsZW1zOiBzdHJpbmdbXSA9IFtdO1xuICAgIGxldCBieVJlc3RyaWN0aW9uID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZ1tdPigpO1xuICAgIGZvciAobGV0IGtleSBvZiBrZXlzKSB7XG4gICAgICAgIGxldCByZXN0cmljdGlvbiA9IGtleVJlc3RyaWN0aW9uTGlzdChrZXkpO1xuICAgICAgICBpZiAoIXJlc3RyaWN0aW9uKSB7XG4gICAgICAgICAgICBwcm9ibGVtcy5wdXNoKGBubyBmcm9tPSByZXN0cmljdGlvbiwgc28gaXQgY2FuIGJlIHVzZWQgZnJvbSBhbnl3aGVyZTpcXG4gICAgICAke3N1bW1hcml6ZUtleShrZXkpfWApO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gU29ydGVkLCBzbyB0aGUgc2FtZSBhZGRyZXNzZXMgd3JpdHRlbiBpbiBhIGRpZmZlcmVudCBvcmRlciBzdGlsbCBjb3VudCBhcyB0aGUgc2FtZS5cbiAgICAgICAgbGV0IGlkZW50aXR5ID0gWy4uLnJlc3RyaWN0aW9uXS5zb3J0KCkuam9pbihcIixcIik7XG4gICAgICAgIGJ5UmVzdHJpY3Rpb24uc2V0KGlkZW50aXR5LCBbLi4uKGJ5UmVzdHJpY3Rpb24uZ2V0KGlkZW50aXR5KSB8fCBbXSksIGtleV0pO1xuICAgIH1cbiAgICBmb3IgKGxldCBbcmVzdHJpY3Rpb24sIHNoYXJpbmddIG9mIGJ5UmVzdHJpY3Rpb24pIHtcbiAgICAgICAgaWYgKHNoYXJpbmcubGVuZ3RoIDwgMikge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgcHJvYmxlbXMucHVzaChcbiAgICAgICAgICAgIGAke3NoYXJpbmcubGVuZ3RofSBrZXlzIGFsbG93IGV4YWN0bHkgdGhlIHNhbWUgYWRkcmVzc2VzICgke3Jlc3RyaWN0aW9ufSksIHdoaWNoIGlzIG9uZWBcbiAgICAgICAgICAgICsgYCBwZXJzb24gaG9sZGluZyBtb3JlIHRoYW4gb25lIGtleTpcXG5gXG4gICAgICAgICAgICArIHNoYXJpbmcubWFwKGtleSA9PiBgICAgICAgJHtzdW1tYXJpemVLZXkoa2V5KX1gKS5qb2luKFwiXFxuXCIpXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBwcm9ibGVtcztcbn1cblxuLyoqIFJlYWRzIHRoZSBhdXRob3JpemVkIGtleXMgYSByZXBvIGNoZWNrb3V0IHdhbnRzIGFwcGxpZWQuIFByZWZlcnMgYSB0b3AgbGV2ZWwgYXV0aG9yaXplZF9rZXlzXG4gICAgZmlsZSBhbmQgb3RoZXJ3aXNlIGNvbmNhdGVuYXRlcyBldmVyeSAucHViIGF0IHRoZSB0b3AgbGV2ZWwuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZFJlcG9LZXlzKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICBsZXQgY29tYmluZWRQYXRoID0gcGF0aC5qb2luKHJlcG9QYXRoLCBcImF1dGhvcml6ZWRfa2V5c1wiKTtcbiAgICBsZXQgZW50cmllcyA9IGF3YWl0IGZzLnJlYWRkaXIocmVwb1BhdGgpO1xuICAgIGlmIChlbnRyaWVzLmluY2x1ZGVzKFwiYXV0aG9yaXplZF9rZXlzXCIpKSB7XG4gICAgICAgIHJldHVybiBub3JtYWxpemVLZXlzKGF3YWl0IGZzLnJlYWRGaWxlKGNvbWJpbmVkUGF0aCwgXCJ1dGY4XCIpKTtcbiAgICB9XG4gICAgbGV0IHB1YkZpbGVzID0gZW50cmllcy5maWx0ZXIobmFtZSA9PiBuYW1lLmVuZHNXaXRoKFwiLnB1YlwiKSkuc29ydCgpO1xuICAgIGlmICghcHViRmlsZXMubGVuZ3RoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYXV0aG9yaXplZF9rZXlzIG9yIGF0IGxlYXN0IG9uZSAucHViIGZpbGUgaW4gJHtyZXBvUGF0aH0sIGZvdW5kIG5laXRoZXJgKTtcbiAgICB9XG4gICAgbGV0IGtleXM6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChsZXQgbmFtZSBvZiBwdWJGaWxlcykge1xuICAgICAgICBrZXlzLnB1c2goLi4ubm9ybWFsaXplS2V5cyhhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4ocmVwb1BhdGgsIG5hbWUpLCBcInV0ZjhcIikpKTtcbiAgICB9XG4gICAgcmV0dXJuIGtleXM7XG59XG4iXX0=
145
+ /* _JS_SOURCE_HASH = "5dc60c0f88ff3878003bf57017d1342326afa7c6a9f511d5896e4113565a7823"; */
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
3
- //exports.deriveRevokeKey = exports.revokeRepoPath = exports.revokeKeyPath = exports.revokeRepoURL = exports.REVOKE_KEY_LABEL = void 0;
6
+ //exports.deriveRevokeKey = exports.revokeRepoPath = exports.findRevokeKey = exports.legacyRevokeKeyPath = exports.revokeKeyPath = exports.revokeRepoURL = exports.REVOKE_KEY_LABEL = void 0;
4
7
  const deriveKey_1 = require("../keys/deriveKey");
5
8
  const sshKeyFile_1 = require("../keys/sshKeyFile");
9
+ const path_1 = __importDefault(require("path"));
6
10
  const sources_1 = require("./sources");
7
11
  // A source's revocations live in a repo beside it. Derived rather than configured, so adding a
8
12
  // source brings its revoke repo with it and there is nothing extra to pass on the command line.
@@ -16,9 +20,18 @@ function revokeRepoURL(sourceURL) {
16
20
  }
17
21
  exports.revokeRepoURL = revokeRepoURL;
18
22
  function revokeKeyPath(sourceURL) {
19
- return `${sources_1.REPO_KEYS_DIR}/${(0, sources_1.sourceName)(sourceURL)}${REVOKED_SUFFIX}`;
23
+ return path_1.default.join((0, sources_1.keysDir)(), `${(0, sources_1.sourceName)(sourceURL)}${REVOKED_SUFFIX}`);
20
24
  }
21
25
  exports.revokeKeyPath = revokeKeyPath;
26
+ function legacyRevokeKeyPath(sourceURL) {
27
+ return `${sources_1.LEGACY_REPO_KEYS_DIR}/${(0, sources_1.sourceName)(sourceURL)}${REVOKED_SUFFIX}`;
28
+ }
29
+ exports.legacyRevokeKeyPath = legacyRevokeKeyPath;
30
+ /** Wherever this source's revoke key already is, or nothing if it has not been derived yet. */
31
+ async function findRevokeKey(sourceURL) {
32
+ return await (0, sources_1.findKey)({ current: revokeKeyPath(sourceURL), legacy: legacyRevokeKeyPath(sourceURL) });
33
+ }
34
+ exports.findRevokeKey = findRevokeKey;
22
35
  function revokeRepoPath(sourceURL) {
23
36
  return `${sources_1.REPOS_DIR}/${(0, sources_1.sourceName)(sourceURL)}${REVOKED_SUFFIX}`;
24
37
  }
@@ -40,5 +53,5 @@ function deriveRevokeKey(sourcePrivateKey) {
40
53
  };
41
54
  }
42
55
  exports.deriveRevokeKey = deriveRevokeKey;
43
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV2b2tlU291cmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicmV2b2tlU291cmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLGlEQUFxRDtBQUNyRCxtREFBcUY7QUFDckYsdUNBQWlFO0FBRWpFLCtGQUErRjtBQUMvRixnR0FBZ0c7QUFDaEcsTUFBTSxjQUFjLEdBQUcsVUFBVSxDQUFDO0FBQ2xDLCtGQUErRjtBQUMvRixrRUFBa0U7QUFDckQsUUFBQSxnQkFBZ0IsR0FBRyxpQkFBaUIsQ0FBQztBQUVsRCxTQUFnQixhQUFhLENBQUMsU0FBaUI7SUFDM0MsSUFBSSxVQUFVLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDakQsT0FBTyxHQUFHLFVBQVUsR0FBRyxjQUFjLE1BQU0sQ0FBQztBQUNoRCxDQUFDO0FBSEQsc0NBR0M7QUFFRCxTQUFnQixhQUFhLENBQUMsU0FBaUI7SUFDM0MsT0FBTyxHQUFHLHVCQUFhLElBQUksSUFBQSxvQkFBVSxFQUFDLFNBQVMsQ0FBQyxHQUFHLGNBQWMsRUFBRSxDQUFDO0FBQ3hFLENBQUM7QUFGRCxzQ0FFQztBQUVELFNBQWdCLGNBQWMsQ0FBQyxTQUFpQjtJQUM1QyxPQUFPLEdBQUcsbUJBQVMsSUFBSSxJQUFBLG9CQUFVLEVBQUMsU0FBUyxDQUFDLEdBQUcsY0FBYyxFQUFFLENBQUM7QUFDcEUsQ0FBQztBQUZELHdDQUVDO0FBRUQ7eUZBQ3lGO0FBQ3pGLFNBQWdCLGVBQWUsQ0FBQyxnQkFBd0I7SUFDcEQsSUFBSSxNQUFNLEdBQUcsSUFBQSxtQ0FBc0IsRUFBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3RELElBQUksT0FBTyxHQUFHLElBQUEsNEJBQWdCLEVBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsd0JBQWdCLEVBQUUsQ0FBQyxDQUFDO0lBQy9FLE9BQU87UUFDSCxTQUFTLEVBQUUsZUFBZSxNQUFNLENBQUMsTUFBTSxDQUFDO1lBQ3BDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDO1lBQ3RELE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxTQUFTO1NBQ2hELENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEVBQUU7UUFDdkIsY0FBYyxFQUFFLElBQUEsb0NBQXVCLEVBQUM7WUFDcEMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO1lBQ2xCLE9BQU8sRUFBRSxHQUFHLE1BQU0sQ0FBQyxPQUFPLElBQUksWUFBWSxJQUFJLHdCQUFnQixFQUFFO1NBQ25FLENBQUM7S0FDTCxDQUFDO0FBQ04sQ0FBQztBQWJELDBDQWFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZGVyaXZlRWQyNTUxOUtleSB9IGZyb20gXCIuLi9rZXlzL2Rlcml2ZUtleVwiO1xuaW1wb3J0IHsgZm9ybWF0T3BlblNTSFByaXZhdGVLZXksIHBhcnNlT3BlblNTSFByaXZhdGVLZXkgfSBmcm9tIFwiLi4va2V5cy9zc2hLZXlGaWxlXCI7XG5pbXBvcnQgeyBSRVBPX0tFWVNfRElSLCBSRVBPU19ESVIsIHNvdXJjZU5hbWUgfSBmcm9tIFwiLi9zb3VyY2VzXCI7XG5cbi8vIEEgc291cmNlJ3MgcmV2b2NhdGlvbnMgbGl2ZSBpbiBhIHJlcG8gYmVzaWRlIGl0LiBEZXJpdmVkIHJhdGhlciB0aGFuIGNvbmZpZ3VyZWQsIHNvIGFkZGluZyBhXG4vLyBzb3VyY2UgYnJpbmdzIGl0cyByZXZva2UgcmVwbyB3aXRoIGl0IGFuZCB0aGVyZSBpcyBub3RoaW5nIGV4dHJhIHRvIHBhc3Mgb24gdGhlIGNvbW1hbmQgbGluZS5cbmNvbnN0IFJFVk9LRURfU1VGRklYID0gXCJfcmV2b2tlZFwiO1xuLy8gR2l0SHViIHdpbGwgbm90IHRha2UgdGhlIHNhbWUgcHVibGljIGtleSBhcyBhIGRlcGxveSBrZXkgb24gdHdvIHJlcG9zLCBzbyB0aGUgcmV2b2tlIHJlcG8gaXNcbi8vIHJlYWNoZWQgd2l0aCBhIGtleSBkZXJpdmVkIGZyb20gdGhlIHNvdXJjZSdzLCB1bmRlciB0aGlzIGxhYmVsLlxuZXhwb3J0IGNvbnN0IFJFVk9LRV9LRVlfTEFCRUwgPSBcInJldm9rZWdpdGh1YmtleVwiO1xuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlUmVwb1VSTChzb3VyY2VVUkw6IHN0cmluZykge1xuICAgIGxldCB3aXRob3V0R2l0ID0gc291cmNlVVJMLnJlcGxhY2UoL1xcLmdpdCQvLCBcIlwiKTtcbiAgICByZXR1cm4gYCR7d2l0aG91dEdpdH0ke1JFVk9LRURfU1VGRklYfS5naXRgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlS2V5UGF0aChzb3VyY2VVUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPX0tFWVNfRElSfS8ke3NvdXJjZU5hbWUoc291cmNlVVJMKX0ke1JFVk9LRURfU1VGRklYfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXZva2VSZXBvUGF0aChzb3VyY2VVUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPU19ESVJ9LyR7c291cmNlTmFtZShzb3VyY2VVUkwpfSR7UkVWT0tFRF9TVUZGSVh9YDtcbn1cblxuLyoqIFRoZSByZXZva2UgcmVwbydzIGtleSwgd29ya2VkIG91dCBmcm9tIHRoZSBzb3VyY2UgcmVwbydzIGtleS4gQW55dGhpbmcgaG9sZGluZyB0aGUgc291cmNlIGtleVxuICAgIGNhbiBwcm9kdWNlIGl0LCBzbyBpdCBuZXZlciBoYXMgdG8gYmUgc3RvcmVkIGFueXdoZXJlIHNlcGFyYXRlbHkgb3IgaGFuZGVkIGFyb3VuZC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZXJpdmVSZXZva2VLZXkoc291cmNlUHJpdmF0ZUtleTogc3RyaW5nKSB7XG4gICAgbGV0IHNvdXJjZSA9IHBhcnNlT3BlblNTSFByaXZhdGVLZXkoc291cmNlUHJpdmF0ZUtleSk7XG4gICAgbGV0IGRlcml2ZWQgPSBkZXJpdmVFZDI1NTE5S2V5KHsgc2VlZDogc291cmNlLnNlZWQsIGxhYmVsOiBSRVZPS0VfS0VZX0xBQkVMIH0pO1xuICAgIHJldHVybiB7XG4gICAgICAgIHB1YmxpY0tleTogYHNzaC1lZDI1NTE5ICR7QnVmZmVyLmNvbmNhdChbXG4gICAgICAgICAgICBCdWZmZXIuZnJvbShbMCwgMCwgMCwgMTFdKSwgQnVmZmVyLmZyb20oXCJzc2gtZWQyNTUxOVwiKSxcbiAgICAgICAgICAgIEJ1ZmZlci5mcm9tKFswLCAwLCAwLCAzMl0pLCBkZXJpdmVkLnB1YmxpY0tleSxcbiAgICAgICAgXSkudG9TdHJpbmcoXCJiYXNlNjRcIil9YCxcbiAgICAgICAgcHJpdmF0ZUtleUZpbGU6IGZvcm1hdE9wZW5TU0hQcml2YXRlS2V5KHtcbiAgICAgICAgICAgIHNlZWQ6IGRlcml2ZWQuc2VlZCxcbiAgICAgICAgICAgIGNvbW1lbnQ6IGAke3NvdXJjZS5jb21tZW50IHx8IFwicG9ydHNlY3VyZVwifSAke1JFVk9LRV9LRVlfTEFCRUx9YCxcbiAgICAgICAgfSksXG4gICAgfTtcbn1cbiJdfQ==
44
- /* _JS_SOURCE_HASH = "9f16cd6aea6e1f1a646eda715ac4cac70dc67579ce23c1e473bd371b7985b229"; */
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV2b2tlU291cmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicmV2b2tlU291cmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLGlEQUFxRDtBQUNyRCxtREFBcUY7QUFDckYsZ0RBQXdCO0FBQ3hCLHVDQUEwRjtBQUUxRiwrRkFBK0Y7QUFDL0YsZ0dBQWdHO0FBQ2hHLE1BQU0sY0FBYyxHQUFHLFVBQVUsQ0FBQztBQUNsQywrRkFBK0Y7QUFDL0Ysa0VBQWtFO0FBQ3JELFFBQUEsZ0JBQWdCLEdBQUcsaUJBQWlCLENBQUM7QUFFbEQsU0FBZ0IsYUFBYSxDQUFDLFNBQWlCO0lBQzNDLElBQUksVUFBVSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ2pELE9BQU8sR0FBRyxVQUFVLEdBQUcsY0FBYyxNQUFNLENBQUM7QUFDaEQsQ0FBQztBQUhELHNDQUdDO0FBRUQsU0FBZ0IsYUFBYSxDQUFDLFNBQWlCO0lBQzNDLE9BQU8sY0FBSSxDQUFDLElBQUksQ0FBQyxJQUFBLGlCQUFPLEdBQUUsRUFBRSxHQUFHLElBQUEsb0JBQVUsRUFBQyxTQUFTLENBQUMsR0FBRyxjQUFjLEVBQUUsQ0FBQyxDQUFDO0FBQzdFLENBQUM7QUFGRCxzQ0FFQztBQUVELFNBQWdCLG1CQUFtQixDQUFDLFNBQWlCO0lBQ2pELE9BQU8sR0FBRyw4QkFBb0IsSUFBSSxJQUFBLG9CQUFVLEVBQUMsU0FBUyxDQUFDLEdBQUcsY0FBYyxFQUFFLENBQUM7QUFDL0UsQ0FBQztBQUZELGtEQUVDO0FBRUQsK0ZBQStGO0FBQ3hGLEtBQUssVUFBVSxhQUFhLENBQUMsU0FBaUI7SUFDakQsT0FBTyxNQUFNLElBQUEsaUJBQU8sRUFBQyxFQUFFLE9BQU8sRUFBRSxhQUFhLENBQUMsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUN4RyxDQUFDO0FBRkQsc0NBRUM7QUFFRCxTQUFnQixjQUFjLENBQUMsU0FBaUI7SUFDNUMsT0FBTyxHQUFHLG1CQUFTLElBQUksSUFBQSxvQkFBVSxFQUFDLFNBQVMsQ0FBQyxHQUFHLGNBQWMsRUFBRSxDQUFDO0FBQ3BFLENBQUM7QUFGRCx3Q0FFQztBQUVEO3lGQUN5RjtBQUN6RixTQUFnQixlQUFlLENBQUMsZ0JBQXdCO0lBQ3BELElBQUksTUFBTSxHQUFHLElBQUEsbUNBQXNCLEVBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUN0RCxJQUFJLE9BQU8sR0FBRyxJQUFBLDRCQUFnQixFQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLHdCQUFnQixFQUFFLENBQUMsQ0FBQztJQUMvRSxPQUFPO1FBQ0gsU0FBUyxFQUFFLGVBQWUsTUFBTSxDQUFDLE1BQU0sQ0FBQztZQUNwQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQztZQUN0RCxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsU0FBUztTQUNoRCxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFO1FBQ3ZCLGNBQWMsRUFBRSxJQUFBLG9DQUF1QixFQUFDO1lBQ3BDLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtZQUNsQixPQUFPLEVBQUUsR0FBRyxNQUFNLENBQUMsT0FBTyxJQUFJLFlBQVksSUFBSSx3QkFBZ0IsRUFBRTtTQUNuRSxDQUFDO0tBQ0wsQ0FBQztBQUNOLENBQUM7QUFiRCwwQ0FhQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRlcml2ZUVkMjU1MTlLZXkgfSBmcm9tIFwiLi4va2V5cy9kZXJpdmVLZXlcIjtcbmltcG9ydCB7IGZvcm1hdE9wZW5TU0hQcml2YXRlS2V5LCBwYXJzZU9wZW5TU0hQcml2YXRlS2V5IH0gZnJvbSBcIi4uL2tleXMvc3NoS2V5RmlsZVwiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IGZpbmRLZXksIGtleXNEaXIsIExFR0FDWV9SRVBPX0tFWVNfRElSLCBSRVBPU19ESVIsIHNvdXJjZU5hbWUgfSBmcm9tIFwiLi9zb3VyY2VzXCI7XG5cbi8vIEEgc291cmNlJ3MgcmV2b2NhdGlvbnMgbGl2ZSBpbiBhIHJlcG8gYmVzaWRlIGl0LiBEZXJpdmVkIHJhdGhlciB0aGFuIGNvbmZpZ3VyZWQsIHNvIGFkZGluZyBhXG4vLyBzb3VyY2UgYnJpbmdzIGl0cyByZXZva2UgcmVwbyB3aXRoIGl0IGFuZCB0aGVyZSBpcyBub3RoaW5nIGV4dHJhIHRvIHBhc3Mgb24gdGhlIGNvbW1hbmQgbGluZS5cbmNvbnN0IFJFVk9LRURfU1VGRklYID0gXCJfcmV2b2tlZFwiO1xuLy8gR2l0SHViIHdpbGwgbm90IHRha2UgdGhlIHNhbWUgcHVibGljIGtleSBhcyBhIGRlcGxveSBrZXkgb24gdHdvIHJlcG9zLCBzbyB0aGUgcmV2b2tlIHJlcG8gaXNcbi8vIHJlYWNoZWQgd2l0aCBhIGtleSBkZXJpdmVkIGZyb20gdGhlIHNvdXJjZSdzLCB1bmRlciB0aGlzIGxhYmVsLlxuZXhwb3J0IGNvbnN0IFJFVk9LRV9LRVlfTEFCRUwgPSBcInJldm9rZWdpdGh1YmtleVwiO1xuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlUmVwb1VSTChzb3VyY2VVUkw6IHN0cmluZykge1xuICAgIGxldCB3aXRob3V0R2l0ID0gc291cmNlVVJMLnJlcGxhY2UoL1xcLmdpdCQvLCBcIlwiKTtcbiAgICByZXR1cm4gYCR7d2l0aG91dEdpdH0ke1JFVk9LRURfU1VGRklYfS5naXRgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlS2V5UGF0aChzb3VyY2VVUkw6IHN0cmluZykge1xuICAgIHJldHVybiBwYXRoLmpvaW4oa2V5c0RpcigpLCBgJHtzb3VyY2VOYW1lKHNvdXJjZVVSTCl9JHtSRVZPS0VEX1NVRkZJWH1gKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxlZ2FjeVJldm9rZUtleVBhdGgoc291cmNlVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYCR7TEVHQUNZX1JFUE9fS0VZU19ESVJ9LyR7c291cmNlTmFtZShzb3VyY2VVUkwpfSR7UkVWT0tFRF9TVUZGSVh9YDtcbn1cblxuLyoqIFdoZXJldmVyIHRoaXMgc291cmNlJ3MgcmV2b2tlIGtleSBhbHJlYWR5IGlzLCBvciBub3RoaW5nIGlmIGl0IGhhcyBub3QgYmVlbiBkZXJpdmVkIHlldC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmaW5kUmV2b2tlS2V5KHNvdXJjZVVSTDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGF3YWl0IGZpbmRLZXkoeyBjdXJyZW50OiByZXZva2VLZXlQYXRoKHNvdXJjZVVSTCksIGxlZ2FjeTogbGVnYWN5UmV2b2tlS2V5UGF0aChzb3VyY2VVUkwpIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlUmVwb1BhdGgoc291cmNlVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYCR7UkVQT1NfRElSfS8ke3NvdXJjZU5hbWUoc291cmNlVVJMKX0ke1JFVk9LRURfU1VGRklYfWA7XG59XG5cbi8qKiBUaGUgcmV2b2tlIHJlcG8ncyBrZXksIHdvcmtlZCBvdXQgZnJvbSB0aGUgc291cmNlIHJlcG8ncyBrZXkuIEFueXRoaW5nIGhvbGRpbmcgdGhlIHNvdXJjZSBrZXlcbiAgICBjYW4gcHJvZHVjZSBpdCwgc28gaXQgbmV2ZXIgaGFzIHRvIGJlIHN0b3JlZCBhbnl3aGVyZSBzZXBhcmF0ZWx5IG9yIGhhbmRlZCBhcm91bmQuICovXG5leHBvcnQgZnVuY3Rpb24gZGVyaXZlUmV2b2tlS2V5KHNvdXJjZVByaXZhdGVLZXk6IHN0cmluZykge1xuICAgIGxldCBzb3VyY2UgPSBwYXJzZU9wZW5TU0hQcml2YXRlS2V5KHNvdXJjZVByaXZhdGVLZXkpO1xuICAgIGxldCBkZXJpdmVkID0gZGVyaXZlRWQyNTUxOUtleSh7IHNlZWQ6IHNvdXJjZS5zZWVkLCBsYWJlbDogUkVWT0tFX0tFWV9MQUJFTCB9KTtcbiAgICByZXR1cm4ge1xuICAgICAgICBwdWJsaWNLZXk6IGBzc2gtZWQyNTUxOSAke0J1ZmZlci5jb25jYXQoW1xuICAgICAgICAgICAgQnVmZmVyLmZyb20oWzAsIDAsIDAsIDExXSksIEJ1ZmZlci5mcm9tKFwic3NoLWVkMjU1MTlcIiksXG4gICAgICAgICAgICBCdWZmZXIuZnJvbShbMCwgMCwgMCwgMzJdKSwgZGVyaXZlZC5wdWJsaWNLZXksXG4gICAgICAgIF0pLnRvU3RyaW5nKFwiYmFzZTY0XCIpfWAsXG4gICAgICAgIHByaXZhdGVLZXlGaWxlOiBmb3JtYXRPcGVuU1NIUHJpdmF0ZUtleSh7XG4gICAgICAgICAgICBzZWVkOiBkZXJpdmVkLnNlZWQsXG4gICAgICAgICAgICBjb21tZW50OiBgJHtzb3VyY2UuY29tbWVudCB8fCBcInBvcnRzZWN1cmVcIn0gJHtSRVZPS0VfS0VZX0xBQkVMfWAsXG4gICAgICAgIH0pLFxuICAgIH07XG59XG4iXX0=
57
+ /* _JS_SOURCE_HASH = "fa19b1fd43388ad835ac95f96f64acb37f8ebbc316741e6c48fc273ac174e1e7"; */