sliftutils 1.7.125 → 1.7.126

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 (41) hide show
  1. package/bin/derivekey.js +10 -0
  2. package/bin/portsecuredaemon.js +13 -0
  3. package/bin/unrevoke.js +9 -0
  4. package/package.json +8 -3
  5. package/security/README.md +66 -2
  6. package/security/authorizedKeys/authorizedKeys.ts +23 -0
  7. package/security/authorizedKeys/daemon/authLog.ts +117 -0
  8. package/security/authorizedKeys/daemon/daemon.ts +308 -0
  9. package/security/authorizedKeys/daemon/git.ts +119 -0
  10. package/security/authorizedKeys/daemon/notify.ts +25 -0
  11. package/security/authorizedKeys/daemon/paths.ts +26 -0
  12. package/security/authorizedKeys/daemon/portsecure.service +3 -2
  13. package/security/authorizedKeys/daemon/revocation.ts +306 -0
  14. package/security/authorizedKeys/daemon/rootKeys.ts +135 -0
  15. package/security/authorizedKeys/daemon/sshdConfig.ts +85 -0
  16. package/security/authorizedKeys/daemon/state.ts +108 -0
  17. package/security/authorizedKeys/daemon/trust.ts +291 -0
  18. package/security/authorizedKeys/daemon/userKeys.ts +76 -0
  19. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +73 -0
  20. package/security/authorizedKeys/dist/revokeSource.ts.cache +44 -0
  21. package/security/authorizedKeys/dist/secureSSH.ts.cache +552 -0
  22. package/security/authorizedKeys/dist/sources.ts.cache +24 -0
  23. package/security/authorizedKeys/dist/unrevoke.ts.cache +145 -0
  24. package/security/authorizedKeys/revokeSource.ts +40 -0
  25. package/security/authorizedKeys/secureSSH.ts +201 -15
  26. package/security/authorizedKeys/unrevoke.ts +149 -0
  27. package/security/helpers/dist/paths.ts.cache +28 -0
  28. package/security/helpers/dist/remoteSSH.ts.cache +90 -0
  29. package/security/helpers/dist/spawn.ts.cache +34 -0
  30. package/security/helpers/remoteSSH.ts +3 -2
  31. package/security/helpers/spawn.ts +5 -1
  32. package/security/keys/deriveKey.ts +72 -0
  33. package/security/keys/dist/deriveKey.ts.cache +72 -0
  34. package/security/keys/dist/sshKeyFile.ts.cache +153 -0
  35. package/security/keys/sshKeyFile.ts +156 -0
  36. package/security/notifications/dist/discord.ts.cache +180 -0
  37. package/security/signedFiles/dist/manifest.ts.cache +68 -0
  38. package/security/signedFiles/dist/signFiles.ts.cache +146 -0
  39. package/security/signedFiles/manifest.ts +19 -6
  40. package/security/signedFiles/signFiles.ts +78 -43
  41. package/security/authorizedKeys/daemon/portsecureDaemon.js +0 -1032
@@ -0,0 +1,153 @@
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.formatPublicKeyLine = exports.formatOpenSSHPrivateKey = exports.parseOpenSSHPrivateKey = exports.publicKeyFromSeed = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ // OpenSSH keeps private keys in its own container rather than anything node can read, so the few
9
+ // bytes of it we need are handled here. Only unencrypted ed25519 keys are supported, which is what
10
+ // a key used by automation is.
11
+ const MAGIC = "openssh-key-v1\0";
12
+ const KEY_TYPE = "ssh-ed25519";
13
+ const NONE = "none";
14
+ const SEED_LENGTH = 32;
15
+ const PUBLIC_LENGTH = 32;
16
+ // The one block size that matters here, since nothing is encrypted.
17
+ const PADDING_BLOCK = 8;
18
+ const PEM_LINE_LENGTH = 70;
19
+ const PRIVATE_HEADER = "-----BEGIN OPENSSH PRIVATE KEY-----";
20
+ const PRIVATE_FOOTER = "-----END OPENSSH PRIVATE KEY-----";
21
+ // The prefix that turns a bare 32 byte seed into a pkcs8 ed25519 key node will accept.
22
+ const PKCS8_ED25519_PREFIX = Buffer.from("302e020100300506032b657004220420", "hex");
23
+ class ByteReader {
24
+ constructor(buffer) {
25
+ this.buffer = buffer;
26
+ this.offset = 0;
27
+ }
28
+ readBytes(length) {
29
+ let value = this.buffer.subarray(this.offset, this.offset + length);
30
+ if (value.length !== length) {
31
+ throw new Error(`Expected ${length} bytes at offset ${this.offset}, was ${value.length}`);
32
+ }
33
+ this.offset += length;
34
+ return value;
35
+ }
36
+ readUInt32() {
37
+ let value = this.buffer.readUInt32BE(this.offset);
38
+ this.offset += 4;
39
+ return value;
40
+ }
41
+ readString() {
42
+ return this.readBytes(this.readUInt32());
43
+ }
44
+ }
45
+ function writeString(value) {
46
+ let contents = Buffer.isBuffer(value) && value || Buffer.from(value, "utf8");
47
+ let length = Buffer.alloc(4);
48
+ length.writeUInt32BE(contents.length);
49
+ return Buffer.concat([length, contents]);
50
+ }
51
+ function writeUInt32(value) {
52
+ let bytes = Buffer.alloc(4);
53
+ bytes.writeUInt32BE(value);
54
+ return bytes;
55
+ }
56
+ /** The public key of a seed, worked out by node rather than by reimplementing the curve. */
57
+ function publicKeyFromSeed(seed) {
58
+ if (seed.length !== SEED_LENGTH) {
59
+ throw new Error(`Expected a ${SEED_LENGTH} byte seed, was ${seed.length}`);
60
+ }
61
+ let privateKey = crypto_1.default.createPrivateKey({
62
+ key: Buffer.concat([PKCS8_ED25519_PREFIX, seed]),
63
+ format: "der",
64
+ type: "pkcs8",
65
+ });
66
+ // The spki wrapper is a fixed 12 byte header followed by the raw key.
67
+ return crypto_1.default.createPublicKey(privateKey).export({ format: "der", type: "spki" }).subarray(12);
68
+ }
69
+ exports.publicKeyFromSeed = publicKeyFromSeed;
70
+ function parseOpenSSHPrivateKey(contents) {
71
+ let body = contents.split("\n").filter(line => line.trim() && !line.startsWith("-----")).join("");
72
+ if (!contents.includes(PRIVATE_HEADER)) {
73
+ throw new Error(`Expected an OpenSSH private key starting with ${PRIVATE_HEADER}`);
74
+ }
75
+ let reader = new ByteReader(Buffer.from(body, "base64"));
76
+ if (reader.readBytes(MAGIC.length).toString() !== MAGIC) {
77
+ throw new Error(`Expected the key to start with ${JSON.stringify(MAGIC)}`);
78
+ }
79
+ let cipherName = reader.readString().toString();
80
+ let kdfName = reader.readString().toString();
81
+ reader.readString();
82
+ if (cipherName !== NONE || kdfName !== NONE) {
83
+ throw new Error(`Expected an unencrypted key, was encrypted with ${cipherName}/${kdfName}.`
84
+ + ` Remove the passphrase with "ssh-keygen -p" first.`);
85
+ }
86
+ let keyCount = reader.readUInt32();
87
+ if (keyCount !== 1) {
88
+ throw new Error(`Expected the key file to hold one key, held ${keyCount}`);
89
+ }
90
+ reader.readString();
91
+ let privateSection = new ByteReader(reader.readString());
92
+ let checkOne = privateSection.readUInt32();
93
+ let checkTwo = privateSection.readUInt32();
94
+ if (checkOne !== checkTwo) {
95
+ throw new Error(`Expected the key's two check values to match, were ${checkOne} and ${checkTwo}`);
96
+ }
97
+ let keyType = privateSection.readString().toString();
98
+ if (keyType !== KEY_TYPE) {
99
+ throw new Error(`Expected an ${KEY_TYPE} key, was ${keyType}`);
100
+ }
101
+ privateSection.readString();
102
+ // OpenSSH stores the seed and the public key together in the private field.
103
+ let privateValue = privateSection.readString();
104
+ if (privateValue.length !== SEED_LENGTH + PUBLIC_LENGTH) {
105
+ throw new Error(`Expected a ${SEED_LENGTH + PUBLIC_LENGTH} byte private value, was ${privateValue.length}`);
106
+ }
107
+ return {
108
+ seed: privateValue.subarray(0, SEED_LENGTH),
109
+ publicKey: privateValue.subarray(SEED_LENGTH),
110
+ comment: privateSection.readString().toString(),
111
+ };
112
+ }
113
+ exports.parseOpenSSHPrivateKey = parseOpenSSHPrivateKey;
114
+ function formatOpenSSHPrivateKey(config) {
115
+ let { seed, comment } = config;
116
+ let publicKey = publicKeyFromSeed(seed);
117
+ let publicBlob = Buffer.concat([writeString(KEY_TYPE), writeString(publicKey)]);
118
+ // The check value only proves a passphrase decrypted correctly, and nothing here is encrypted.
119
+ // Taking it from the seed keeps the same input producing the same file, so deriving a key twice
120
+ // is something you can compare rather than something that looks different every time.
121
+ let check = crypto_1.default.createHash("sha256").update(seed).digest().readUInt32BE(0);
122
+ let privateSection = Buffer.concat([
123
+ writeUInt32(check),
124
+ writeUInt32(check),
125
+ writeString(KEY_TYPE),
126
+ writeString(publicKey),
127
+ writeString(Buffer.concat([seed, publicKey])),
128
+ writeString(comment),
129
+ ]);
130
+ let paddingLength = (PADDING_BLOCK - privateSection.length % PADDING_BLOCK) % PADDING_BLOCK;
131
+ let padding = Buffer.from(Array.from({ length: paddingLength }, (unused, index) => index + 1));
132
+ let contents = Buffer.concat([
133
+ Buffer.from(MAGIC),
134
+ writeString(NONE),
135
+ writeString(NONE),
136
+ writeString(""),
137
+ writeUInt32(1),
138
+ writeString(publicBlob),
139
+ writeString(Buffer.concat([privateSection, padding])),
140
+ ]);
141
+ let encoded = contents.toString("base64").match(new RegExp(`.{1,${PEM_LINE_LENGTH}}`, "g")) || [];
142
+ return `${PRIVATE_HEADER}\n${encoded.join("\n")}\n${PRIVATE_FOOTER}\n`;
143
+ }
144
+ exports.formatOpenSSHPrivateKey = formatOpenSSHPrivateKey;
145
+ /** The single line form, as it appears in authorized_keys and in a .pub file. */
146
+ function formatPublicKeyLine(config) {
147
+ let { publicKey, comment } = config;
148
+ let blob = Buffer.concat([writeString(KEY_TYPE), writeString(publicKey)]);
149
+ return `${KEY_TYPE} ${blob.toString("base64")}${comment && ` ${comment}` || ""}\n`;
150
+ }
151
+ exports.formatPublicKeyLine = formatPublicKeyLine;
152
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NoS2V5RmlsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNzaEtleUZpbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsb0RBQTRCO0FBRTVCLGlHQUFpRztBQUNqRyxtR0FBbUc7QUFDbkcsK0JBQStCO0FBRS9CLE1BQU0sS0FBSyxHQUFHLGtCQUFrQixDQUFDO0FBQ2pDLE1BQU0sUUFBUSxHQUFHLGFBQWEsQ0FBQztBQUMvQixNQUFNLElBQUksR0FBRyxNQUFNLENBQUM7QUFDcEIsTUFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDO0FBQ3ZCLE1BQU0sYUFBYSxHQUFHLEVBQUUsQ0FBQztBQUN6QixvRUFBb0U7QUFDcEUsTUFBTSxhQUFhLEdBQUcsQ0FBQyxDQUFDO0FBQ3hCLE1BQU0sZUFBZSxHQUFHLEVBQUUsQ0FBQztBQUMzQixNQUFNLGNBQWMsR0FBRyxxQ0FBcUMsQ0FBQztBQUM3RCxNQUFNLGNBQWMsR0FBRyxtQ0FBbUMsQ0FBQztBQUMzRCx1RkFBdUY7QUFDdkYsTUFBTSxvQkFBb0IsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLGtDQUFrQyxFQUFFLEtBQUssQ0FBQyxDQUFDO0FBRXBGLE1BQU0sVUFBVTtJQUVaLFlBQW9CLE1BQWM7UUFBZCxXQUFNLEdBQU4sTUFBTSxDQUFRO1FBRDFCLFdBQU0sR0FBRyxDQUFDLENBQUM7SUFDbUIsQ0FBQztJQUV2QyxTQUFTLENBQUMsTUFBYztRQUNwQixJQUFJLEtBQUssR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLENBQUM7UUFDcEUsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQzFCLE1BQU0sSUFBSSxLQUFLLENBQUMsWUFBWSxNQUFNLG9CQUFvQixJQUFJLENBQUMsTUFBTSxTQUFTLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFD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ZUFBZSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDbEcsT0FBTyxHQUFHLGNBQWMsS0FBSyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLGNBQWMsSUFBSSxDQUFDO0FBQzNFLENBQUM7QUEvQkQsMERBK0JDO0FBRUQsaUZBQWlGO0FBQ2pGLFNBQWdCLG1CQUFtQixDQUFDLE1BQThDO0lBQzlFLElBQUksRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLEdBQUcsTUFBTSxDQUFDO0lBQ3BDLElBQUksSUFBSSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLEVBQUUsV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRSxPQUFPLEdBQUcsUUFBUSxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEdBQUcsT0FBTyxJQUFJLElBQUksT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUM7QUFDdkYsQ0FBQztBQUpELGtEQUlDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNyeXB0byBmcm9tIFwiY3J5cHRvXCI7XG5cbi8vIE9wZW5TU0gga2VlcHMgcHJpdmF0ZSBrZXlzIGluIGl0cyBvd24gY29udGFpbmVyIHJhdGhlciB0aGFuIGFueXRoaW5nIG5vZGUgY2FuIHJlYWQsIHNvIHRoZSBmZXdcbi8vIGJ5dGVzIG9mIGl0IHdlIG5lZWQgYXJlIGhhbmRsZWQgaGVyZS4gT25seSB1bmVuY3J5cHRlZCBlZDI1NTE5IGtleXMgYXJlIHN1cHBvcnRlZCwgd2hpY2ggaXMgd2hhdFxuLy8gYSBrZXkgdXNlZCBieSBhdXRvbWF0aW9uIGlzLlxuXG5jb25zdCBNQUdJQyA9IFwib3BlbnNzaC1rZXktdjFcXDBcIjtcbmNvbnN0IEtFWV9UWVBFID0gXCJzc2gtZWQyNTUxOVwiO1xuY29uc3QgTk9ORSA9IFwibm9uZVwiO1xuY29uc3QgU0VFRF9MRU5HVEggPSAzMjtcbmNvbnN0IFBVQkxJQ19MRU5HVEggPSAzMjtcbi8vIFRoZSBvbmUgYmxvY2sgc2l6ZSB0aGF0IG1hdHRlcnMgaGVyZSwgc2luY2Ugbm90aGluZyBpcyBlbmNyeXB0ZWQuXG5jb25zdCBQQURESU5HX0JMT0NLID0gODtcbmNvbnN0IFBFTV9MSU5FX0xFTkdUSCA9IDcwO1xuY29uc3QgUFJJVkFURV9IRUFERVIgPSBcIi0tLS0tQkVHSU4gT1BFTlNTSCBQUklWQVRFIEtFWS0tLS0tXCI7XG5jb25zdCBQUklWQVRFX0ZPT1RFUiA9IFwiLS0tLS1FTkQgT1BFTlNTSCBQUklWQVRFIEtFWS0tLS0tXCI7XG4vLyBUaGUgcHJlZml4IHRoYXQgdHVybnMgYSBiYXJlIDMyIGJ5dGUgc2VlZCBpbnRvIGEgcGtjczggZWQyNTUxOSBrZXkgbm9kZSB3aWxsIGFjY2VwdC5cbmNvbnN0IFBLQ1M4X0VEMjU1MTlfUFJFRklYID0gQnVmZmVyLmZyb20oXCIzMDJlMDIwMTAwMzAwNTA2MDMyYjY1NzAwNDIyMDQyMFwiLCBcImhleFwiKTtcblxuY2xhc3MgQnl0ZVJlYWRlciB7XG4gICAgcHJpdmF0ZSBvZmZzZXQgPSAwO1xuICAgIGNvbnN0cnVjdG9yKHByaXZhdGUgYnVmZmVyOiBCdWZmZXIpIHsgfVxuXG4gICAgcmVhZEJ5dGVzKGxlbmd0aDogbnVtYmVyKSB7XG4gICAgICAgIGxldCB2YWx1ZSA9IHRoaXMuYnVmZmVyLnN1YmFycmF5KHRoaXMub2Zmc2V0LCB0aGlzLm9mZnNldCArIGxlbmd0aCk7XG4gICAgICAgIGlmICh2YWx1ZS5sZW5ndGggIT09IGxlbmd0aCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCAke2xlbmd0aH0gYnl0ZXMgYXQgb2Zmc2V0ICR7dGhpcy5vZmZzZXR9LCB3YXMgJHt2YWx1ZS5sZW5ndGh9YCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5vZmZzZXQgKz0gbGVuZ3RoO1xuICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgfVxuXG4gICAgcmVhZFVJbnQzMigpIHtcbiAgICAgICAgbGV0IHZhbHVlID0gdGhpcy5idWZmZXIucmVhZFVJbnQzMkJFKHRoaXMub2Zmc2V0KTtcbiAgICAgICAgdGhpcy5vZmZzZXQgKz0gNDtcbiAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH1cblxuICAgIHJlYWRTdHJpbmcoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJlYWRCeXRlcyh0aGlzLnJlYWRVSW50MzIoKSk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiB3cml0ZVN0cmluZyh2YWx1ZTogQnVmZmVyIHwgc3RyaW5nKSB7XG4gICAgbGV0IGNvbnRlbnRzID0gQnVmZmVyLmlzQnVmZmVyKHZhbHVlKSAmJiB2YWx1ZSB8fCBCdWZmZXIuZnJvbSh2YWx1ZSwgXCJ1dGY4XCIpO1xuICAgIGxldCBsZW5ndGggPSBCdWZmZXIuYWxsb2MoNCk7XG4gICAgbGVuZ3RoLndyaXRlVUludDMyQkUoY29udGVudHMubGVuZ3RoKTtcbiAgICByZXR1cm4gQnVmZmVyLmNvbmNhdChbbGVuZ3RoLCBjb250ZW50c10pO1xufVxuXG5mdW5jdGlvbiB3cml0ZVVJbnQzMih2YWx1ZTogbnVtYmVyKSB7XG4gICAgbGV0IGJ5dGVzID0gQnVmZmVyLmFsbG9jKDQpO1xuICAgIGJ5dGVzLndyaXRlVUludDMyQkUodmFsdWUpO1xuICAgIHJldHVybiBieXRlcztcbn1cblxuLyoqIFRoZSBwdWJsaWMga2V5IG9mIGEgc2VlZCwgd29ya2VkIG91dCBieSBub2RlIHJhdGhlciB0aGFuIGJ5IHJlaW1wbGVtZW50aW5nIHRoZSBjdXJ2ZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwdWJsaWNLZXlGcm9tU2VlZChzZWVkOiBCdWZmZXIpIHtcbiAgICBpZiAoc2VlZC5sZW5ndGggIT09IFNFRURfTEVOR1RIKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSAke1NFRURfTEVOR1RIfSBieXRlIHNlZWQsIHdhcyAke3NlZWQubGVuZ3RofWApO1xuICAgIH1cbiAgICBsZXQgcHJpdmF0ZUtleSA9IGNyeXB0by5jcmVhdGVQcml2YXRlS2V5KHtcbiAgICAgICAga2V5OiBCdWZmZXIuY29uY2F0KFtQS0NTOF9FRDI1NTE5X1BSRUZJWCwgc2VlZF0pLFxuICAgICAgICBmb3JtYXQ6IFwiZGVyXCIsXG4gICAgICAgIHR5cGU6IFwicGtjczhcIixcbiAgICB9KTtcbiAgICAvLyBUaGUgc3BraSB3cmFwcGVyIGlzIGEgZml4ZWQgMTIgYnl0ZSBoZWFkZXIgZm9sbG93ZWQgYnkgdGhlIHJhdyBrZXkuXG4gICAgcmV0dXJuIGNyeXB0by5jcmVhdGVQdWJsaWNLZXkocHJpdmF0ZUtleSkuZXhwb3J0KHsgZm9ybWF0OiBcImRlclwiLCB0eXBlOiBcInNwa2lcIiB9KS5zdWJhcnJheSgxMik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZU9wZW5TU0hQcml2YXRlS2V5KGNvbnRlbnRzOiBzdHJpbmcpIHtcbiAgICBsZXQgYm9keSA9IGNvbnRlbnRzLnNwbGl0KFwiXFxuXCIpLmZpbHRlcihsaW5lID0+IGxpbmUudHJpbSgpICYmICFsaW5lLnN0YXJ0c1dpdGgoXCItLS0tLVwiKSkuam9pbihcIlwiKTtcbiAgICBpZiAoIWNvbnRlbnRzLmluY2x1ZGVzKFBSSVZBVEVfSEVBREVSKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGFuIE9wZW5TU0ggcHJpdmF0ZSBrZXkgc3RhcnRpbmcgd2l0aCAke1BSSVZBVEVfSEVBREVSfWApO1xuICAgIH1cbiAgICBsZXQgcmVhZGVyID0gbmV3IEJ5dGVSZWFkZXIoQnVmZmVyLmZyb20oYm9keSwgXCJiYXNlNjRcIikpO1xuICAgIGlmIChyZWFkZXIucmVhZEJ5dGVzKE1BR0lDLmxlbmd0aCkudG9TdHJpbmcoKSAhPT0gTUFHSUMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCB0aGUga2V5IHRvIHN0YXJ0IHdpdGggJHtKU09OLnN0cmluZ2lmeShNQUdJQyl9YCk7XG4gICAgfVxuICAgIGxldCBjaXBoZXJOYW1lID0gcmVhZGVyLnJlYWRTdHJpbmcoKS50b1N0cmluZygpO1xuICAgIGxldCBrZGZOYW1lID0gcmVhZGVyLnJlYWRTdHJpbmcoKS50b1N0cmluZygpO1xuICAgIHJlYWRlci5yZWFkU3RyaW5nKCk7XG4gICAgaWYgKGNpcGhlck5hbWUgIT09IE5PTkUgfHwga2RmTmFtZSAhPT0gTk9ORSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgYW4gdW5lbmNyeXB0ZWQga2V5LCB3YXMgZW5jcnlwdGVkIHdpdGggJHtjaXBoZXJOYW1lfS8ke2tkZk5hbWV9LmBcbiAgICAgICAgICAgICsgYCBSZW1vdmUgdGhlIHBhc3NwaHJhc2Ugd2l0aCBcInNzaC1rZXlnZW4gLXBcIiBmaXJzdC5gXG4gICAgICAgICk7XG4gICAgfVxuICAgIGxldCBrZXlDb3VudCA9IHJlYWRlci5yZWFkVUludDMyKCk7XG4gICAgaWYgKGtleUNvdW50ICE9PSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgdGhlIGtleSBmaWxlIHRvIGhvbGQgb25lIGtleSwgaGVsZCAke2tleUNvdW50fWApO1xuICAgIH1cbiAgICByZWFkZXIucmVhZFN0cmluZygpO1xuXG4gICAgbGV0IHByaXZhdGVTZWN0aW9uID0gbmV3IEJ5dGVSZWFkZXIocmVhZGVyLnJlYWRTdHJpbmcoKSk7XG4gICAgbGV0IGNoZWNrT25lID0gcHJpdmF0ZVNlY3Rpb24ucmVhZFVJbnQzMigpO1xuICAgIGxldCBjaGVja1R3byA9IHByaXZhdGVTZWN0aW9uLnJlYWRVSW50MzIoKTtcbiAgICBpZiAoY2hlY2tPbmUgIT09IGNoZWNrVHdvKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgdGhlIGtleSdzIHR3byBjaGVjayB2YWx1ZXMgdG8gbWF0Y2gsIHdlcmUgJHtjaGVja09uZX0gYW5kICR7Y2hlY2tUd299YCk7XG4gICAgfVxuICAgIGxldCBrZXlUeXBlID0gcHJpdmF0ZVNlY3Rpb24ucmVhZFN0cmluZygpLnRvU3RyaW5nKCk7XG4gICAgaWYgKGtleVR5cGUgIT09IEtFWV9UWVBFKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYW4gJHtLRVlfVFlQRX0ga2V5LCB3YXMgJHtrZXlUeXBlfWApO1xuICAgIH1cbiAgICBwcml2YXRlU2VjdGlvbi5yZWFkU3RyaW5nKCk7XG4gICAgLy8gT3BlblNTSCBzdG9yZXMgdGhlIHNlZWQgYW5kIHRoZSBwdWJsaWMga2V5IHRvZ2V0aGVyIGluIHRoZSBwcml2YXRlIGZpZWxkLlxuICAgIGxldCBwcml2YXRlVmFsdWUgPSBwcml2YXRlU2VjdGlvbi5yZWFkU3RyaW5nKCk7XG4gICAgaWYgKHByaXZhdGVWYWx1ZS5sZW5ndGggIT09IFNFRURfTEVOR1RIICsgUFVCTElDX0xFTkdUSCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGEgJHtTRUVEX0xFTkdUSCArIFBVQkxJQ19MRU5HVEh9IGJ5dGUgcHJpdmF0ZSB2YWx1ZSwgd2FzICR7cHJpdmF0ZVZhbHVlLmxlbmd0aH1gKTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgICAgc2VlZDogcHJpdmF0ZVZhbHVlLnN1YmFycmF5KDAsIFNFRURfTEVOR1RIKSxcbiAgICAgICAgcHVibGljS2V5OiBwcml2YXRlVmFsdWUuc3ViYXJyYXkoU0VFRF9MRU5HVEgpLFxuICAgICAgICBjb21tZW50OiBwcml2YXRlU2VjdGlvbi5yZWFkU3RyaW5nKCkudG9TdHJpbmcoKSxcbiAgICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0T3BlblNTSFByaXZhdGVLZXkoY29uZmlnOiB7IHNlZWQ6IEJ1ZmZlcjsgY29tbWVudDogc3RyaW5nIH0pIHtcbiAgICBsZXQgeyBzZWVkLCBjb21tZW50IH0gPSBjb25maWc7XG4gICAgbGV0IHB1YmxpY0tleSA9IHB1YmxpY0tleUZyb21TZWVkKHNlZWQpO1xuICAgIGxldCBwdWJsaWNCbG9iID0gQnVmZmVyLmNvbmNhdChbd3JpdGVTdHJpbmcoS0VZX1RZUEUpLCB3cml0ZVN0cmluZyhwdWJsaWNLZXkpXSk7XG5cbiAgICAvLyBUaGUgY2hlY2sgdmFsdWUgb25seSBwcm92ZXMgYSBwYXNzcGhyYXNlIGRlY3J5cHRlZCBjb3JyZWN0bHksIGFuZCBub3RoaW5nIGhlcmUgaXMgZW5jcnlwdGVkLlxuICAgIC8vIFRha2luZyBpdCBmcm9tIHRoZSBzZWVkIGtlZXBzIHRoZSBzYW1lIGlucHV0IHByb2R1Y2luZyB0aGUgc2FtZSBmaWxlLCBzbyBkZXJpdmluZyBhIGtleSB0d2ljZVxuICAgIC8vIGlzIHNvbWV0aGluZyB5b3UgY2FuIGNvbXBhcmUgcmF0aGVyIHRoYW4gc29tZXRoaW5nIHRoYXQgbG9va3MgZGlmZmVyZW50IGV2ZXJ5IHRpbWUuXG4gICAgbGV0IGNoZWNrID0gY3J5cHRvLmNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKHNlZWQpLmRpZ2VzdCgpLnJlYWRVSW50MzJCRSgwKTtcbiAgICBsZXQgcHJpdmF0ZVNlY3Rpb24gPSBCdWZmZXIuY29uY2F0KFtcbiAgICAgICAgd3JpdGVVSW50MzIoY2hlY2spLFxuICAgICAgICB3cml0ZVVJbnQzMihjaGVjayksXG4gICAgICAgIHdyaXRlU3RyaW5nKEtFWV9UWVBFKSxcbiAgICAgICAgd3JpdGVTdHJpbmcocHVibGljS2V5KSxcbiAgICAgICAgd3JpdGVTdHJpbmcoQnVmZmVyLmNvbmNhdChbc2VlZCwgcHVibGljS2V5XSkpLFxuICAgICAgICB3cml0ZVN0cmluZyhjb21tZW50KSxcbiAgICBdKTtcbiAgICBsZXQgcGFkZGluZ0xlbmd0aCA9IChQQURESU5HX0JMT0NLIC0gcHJpdmF0ZVNlY3Rpb24ubGVuZ3RoICUgUEFERElOR19CTE9DSykgJSBQQURESU5HX0JMT0NLO1xuICAgIGxldCBwYWRkaW5nID0gQnVmZmVyLmZyb20oQXJyYXkuZnJvbSh7IGxlbmd0aDogcGFkZGluZ0xlbmd0aCB9LCAodW51c2VkLCBpbmRleCkgPT4gaW5kZXggKyAxKSk7XG5cbiAgICBsZXQgY29udGVudHMgPSBCdWZmZXIuY29uY2F0KFtcbiAgICAgICAgQnVmZmVyLmZyb20oTUFHSUMpLFxuICAgICAgICB3cml0ZVN0cmluZyhOT05FKSxcbiAgICAgICAgd3JpdGVTdHJpbmcoTk9ORSksXG4gICAgICAgIHdyaXRlU3RyaW5nKFwiXCIpLFxuICAgICAgICB3cml0ZVVJbnQzMigxKSxcbiAgICAgICAgd3JpdGVTdHJpbmcocHVibGljQmxvYiksXG4gICAgICAgIHdyaXRlU3RyaW5nKEJ1ZmZlci5jb25jYXQoW3ByaXZhdGVTZWN0aW9uLCBwYWRkaW5nXSkpLFxuICAgIF0pO1xuICAgIGxldCBlbmNvZGVkID0gY29udGVudHMudG9TdHJpbmcoXCJiYXNlNjRcIikubWF0Y2gobmV3IFJlZ0V4cChgLnsxLCR7UEVNX0xJTkVfTEVOR1RIfX1gLCBcImdcIikpIHx8IFtdO1xuICAgIHJldHVybiBgJHtQUklWQVRFX0hFQURFUn1cXG4ke2VuY29kZWQuam9pbihcIlxcblwiKX1cXG4ke1BSSVZBVEVfRk9PVEVSfVxcbmA7XG59XG5cbi8qKiBUaGUgc2luZ2xlIGxpbmUgZm9ybSwgYXMgaXQgYXBwZWFycyBpbiBhdXRob3JpemVkX2tleXMgYW5kIGluIGEgLnB1YiBmaWxlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFB1YmxpY0tleUxpbmUoY29uZmlnOiB7IHB1YmxpY0tleTogQnVmZmVyOyBjb21tZW50OiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHB1YmxpY0tleSwgY29tbWVudCB9ID0gY29uZmlnO1xuICAgIGxldCBibG9iID0gQnVmZmVyLmNvbmNhdChbd3JpdGVTdHJpbmcoS0VZX1RZUEUpLCB3cml0ZVN0cmluZyhwdWJsaWNLZXkpXSk7XG4gICAgcmV0dXJuIGAke0tFWV9UWVBFfSAke2Jsb2IudG9TdHJpbmcoXCJiYXNlNjRcIil9JHtjb21tZW50ICYmIGAgJHtjb21tZW50fWAgfHwgXCJcIn1cXG5gO1xufVxuIl19
153
+ /* _JS_SOURCE_HASH = "57e4d1fd38c49a01bb35cf57a53a29f28eb2053b81167b87b4c17bab13251fae"; */
@@ -0,0 +1,156 @@
1
+ import crypto from "crypto";
2
+
3
+ // OpenSSH keeps private keys in its own container rather than anything node can read, so the few
4
+ // bytes of it we need are handled here. Only unencrypted ed25519 keys are supported, which is what
5
+ // a key used by automation is.
6
+
7
+ const MAGIC = "openssh-key-v1\0";
8
+ const KEY_TYPE = "ssh-ed25519";
9
+ const NONE = "none";
10
+ const SEED_LENGTH = 32;
11
+ const PUBLIC_LENGTH = 32;
12
+ // The one block size that matters here, since nothing is encrypted.
13
+ const PADDING_BLOCK = 8;
14
+ const PEM_LINE_LENGTH = 70;
15
+ const PRIVATE_HEADER = "-----BEGIN OPENSSH PRIVATE KEY-----";
16
+ const PRIVATE_FOOTER = "-----END OPENSSH PRIVATE KEY-----";
17
+ // The prefix that turns a bare 32 byte seed into a pkcs8 ed25519 key node will accept.
18
+ const PKCS8_ED25519_PREFIX = Buffer.from("302e020100300506032b657004220420", "hex");
19
+
20
+ class ByteReader {
21
+ private offset = 0;
22
+ constructor(private buffer: Buffer) { }
23
+
24
+ readBytes(length: number) {
25
+ let value = this.buffer.subarray(this.offset, this.offset + length);
26
+ if (value.length !== length) {
27
+ throw new Error(`Expected ${length} bytes at offset ${this.offset}, was ${value.length}`);
28
+ }
29
+ this.offset += length;
30
+ return value;
31
+ }
32
+
33
+ readUInt32() {
34
+ let value = this.buffer.readUInt32BE(this.offset);
35
+ this.offset += 4;
36
+ return value;
37
+ }
38
+
39
+ readString() {
40
+ return this.readBytes(this.readUInt32());
41
+ }
42
+ }
43
+
44
+ function writeString(value: Buffer | string) {
45
+ let contents = Buffer.isBuffer(value) && value || Buffer.from(value, "utf8");
46
+ let length = Buffer.alloc(4);
47
+ length.writeUInt32BE(contents.length);
48
+ return Buffer.concat([length, contents]);
49
+ }
50
+
51
+ function writeUInt32(value: number) {
52
+ let bytes = Buffer.alloc(4);
53
+ bytes.writeUInt32BE(value);
54
+ return bytes;
55
+ }
56
+
57
+ /** The public key of a seed, worked out by node rather than by reimplementing the curve. */
58
+ export function publicKeyFromSeed(seed: Buffer) {
59
+ if (seed.length !== SEED_LENGTH) {
60
+ throw new Error(`Expected a ${SEED_LENGTH} byte seed, was ${seed.length}`);
61
+ }
62
+ let privateKey = crypto.createPrivateKey({
63
+ key: Buffer.concat([PKCS8_ED25519_PREFIX, seed]),
64
+ format: "der",
65
+ type: "pkcs8",
66
+ });
67
+ // The spki wrapper is a fixed 12 byte header followed by the raw key.
68
+ return crypto.createPublicKey(privateKey).export({ format: "der", type: "spki" }).subarray(12);
69
+ }
70
+
71
+ export function parseOpenSSHPrivateKey(contents: string) {
72
+ let body = contents.split("\n").filter(line => line.trim() && !line.startsWith("-----")).join("");
73
+ if (!contents.includes(PRIVATE_HEADER)) {
74
+ throw new Error(`Expected an OpenSSH private key starting with ${PRIVATE_HEADER}`);
75
+ }
76
+ let reader = new ByteReader(Buffer.from(body, "base64"));
77
+ if (reader.readBytes(MAGIC.length).toString() !== MAGIC) {
78
+ throw new Error(`Expected the key to start with ${JSON.stringify(MAGIC)}`);
79
+ }
80
+ let cipherName = reader.readString().toString();
81
+ let kdfName = reader.readString().toString();
82
+ reader.readString();
83
+ if (cipherName !== NONE || kdfName !== NONE) {
84
+ throw new Error(
85
+ `Expected an unencrypted key, was encrypted with ${cipherName}/${kdfName}.`
86
+ + ` Remove the passphrase with "ssh-keygen -p" first.`
87
+ );
88
+ }
89
+ let keyCount = reader.readUInt32();
90
+ if (keyCount !== 1) {
91
+ throw new Error(`Expected the key file to hold one key, held ${keyCount}`);
92
+ }
93
+ reader.readString();
94
+
95
+ let privateSection = new ByteReader(reader.readString());
96
+ let checkOne = privateSection.readUInt32();
97
+ let checkTwo = privateSection.readUInt32();
98
+ if (checkOne !== checkTwo) {
99
+ throw new Error(`Expected the key's two check values to match, were ${checkOne} and ${checkTwo}`);
100
+ }
101
+ let keyType = privateSection.readString().toString();
102
+ if (keyType !== KEY_TYPE) {
103
+ throw new Error(`Expected an ${KEY_TYPE} key, was ${keyType}`);
104
+ }
105
+ privateSection.readString();
106
+ // OpenSSH stores the seed and the public key together in the private field.
107
+ let privateValue = privateSection.readString();
108
+ if (privateValue.length !== SEED_LENGTH + PUBLIC_LENGTH) {
109
+ throw new Error(`Expected a ${SEED_LENGTH + PUBLIC_LENGTH} byte private value, was ${privateValue.length}`);
110
+ }
111
+ return {
112
+ seed: privateValue.subarray(0, SEED_LENGTH),
113
+ publicKey: privateValue.subarray(SEED_LENGTH),
114
+ comment: privateSection.readString().toString(),
115
+ };
116
+ }
117
+
118
+ export function formatOpenSSHPrivateKey(config: { seed: Buffer; comment: string }) {
119
+ let { seed, comment } = config;
120
+ let publicKey = publicKeyFromSeed(seed);
121
+ let publicBlob = Buffer.concat([writeString(KEY_TYPE), writeString(publicKey)]);
122
+
123
+ // The check value only proves a passphrase decrypted correctly, and nothing here is encrypted.
124
+ // Taking it from the seed keeps the same input producing the same file, so deriving a key twice
125
+ // is something you can compare rather than something that looks different every time.
126
+ let check = crypto.createHash("sha256").update(seed).digest().readUInt32BE(0);
127
+ let privateSection = Buffer.concat([
128
+ writeUInt32(check),
129
+ writeUInt32(check),
130
+ writeString(KEY_TYPE),
131
+ writeString(publicKey),
132
+ writeString(Buffer.concat([seed, publicKey])),
133
+ writeString(comment),
134
+ ]);
135
+ let paddingLength = (PADDING_BLOCK - privateSection.length % PADDING_BLOCK) % PADDING_BLOCK;
136
+ let padding = Buffer.from(Array.from({ length: paddingLength }, (unused, index) => index + 1));
137
+
138
+ let contents = Buffer.concat([
139
+ Buffer.from(MAGIC),
140
+ writeString(NONE),
141
+ writeString(NONE),
142
+ writeString(""),
143
+ writeUInt32(1),
144
+ writeString(publicBlob),
145
+ writeString(Buffer.concat([privateSection, padding])),
146
+ ]);
147
+ let encoded = contents.toString("base64").match(new RegExp(`.{1,${PEM_LINE_LENGTH}}`, "g")) || [];
148
+ return `${PRIVATE_HEADER}\n${encoded.join("\n")}\n${PRIVATE_FOOTER}\n`;
149
+ }
150
+
151
+ /** The single line form, as it appears in authorized_keys and in a .pub file. */
152
+ export function formatPublicKeyLine(config: { publicKey: Buffer; comment: string }) {
153
+ let { publicKey, comment } = config;
154
+ let blob = Buffer.concat([writeString(KEY_TYPE), writeString(publicKey)]);
155
+ return `${KEY_TYPE} ${blob.toString("base64")}${comment && ` ${comment}` || ""}\n`;
156
+ }
@@ -0,0 +1,180 @@
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.stopDiscordNotifications = exports.sendDiscordNotification = exports.configureDiscordNotifications = exports.sendToWebhookURL = exports.formatWebhookFile = exports.redactWebhookURL = exports.parseWebhookFile = exports.DEFAULT_WEBHOOK_FILE_PATH = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ // PORTED CODE: security/authorizedKeys/daemon/portsecureDaemon.js embeds a hand port of this file in plain JS, so the
9
+ // daemon can run with no dependencies. The two are expected to behave identically - if you change
10
+ // one, make the matching change in the other.
11
+ exports.DEFAULT_WEBHOOK_FILE_PATH = "/etc/portsecure/discord-webhook";
12
+ const DEFAULT_CHECK_INTERVAL = 5 * 60 * 1000;
13
+ const VALID_WEBHOOK_PREFIXES = [
14
+ "https://discord.com/api/webhooks/",
15
+ "https://discordapp.com/api/webhooks/",
16
+ "https://canary.discord.com/api/webhooks/",
17
+ ];
18
+ const DISCORD_MESSAGE_LIMIT = 2000;
19
+ const MAX_ERROR_BODY_LENGTH = 500;
20
+ const MAX_SEND_ATTEMPTS = 3;
21
+ const REDACTED_TOKEN_VISIBLE = 8;
22
+ const DEFAULT_RATE_LIMIT_WAIT = 2 * 1000;
23
+ let notificationState;
24
+ // Discord webhooks are rate limited (roughly 5 requests per 2 seconds), so every send
25
+ // goes through one chain instead of racing.
26
+ let sendChain = Promise.resolve();
27
+ /** Pulls the webhook URL out of webhook file contents. `sourceName` only appears in errors, so
28
+ callers can name a remote path. */
29
+ function parseWebhookFile(config) {
30
+ let { contents, sourceName } = config;
31
+ let lines = contents.split("\n").map(line => line.trim()).filter(line => line && !line.startsWith("#"));
32
+ let webhookURL = lines[0];
33
+ if (!webhookURL) {
34
+ throw new Error(`Expected a Discord webhook URL in ${sourceName}, the file has no usable lines`);
35
+ }
36
+ if (!VALID_WEBHOOK_PREFIXES.some(prefix => webhookURL.startsWith(prefix))) {
37
+ throw new Error(`Expected a Discord webhook URL starting with one of ${VALID_WEBHOOK_PREFIXES.join(", ")}, `
38
+ + `was ${webhookURL.slice(0, MAX_ERROR_BODY_LENGTH)} (in ${sourceName})`);
39
+ }
40
+ return webhookURL;
41
+ }
42
+ exports.parseWebhookFile = parseWebhookFile;
43
+ /** Keeps the id and both ends of the token, so a reader can confirm which webhook replaced theirs
44
+ without receiving one they could post to. */
45
+ function redactWebhookURL(webhookURL) {
46
+ let separatorIndex = webhookURL.lastIndexOf("/");
47
+ let base = webhookURL.slice(0, separatorIndex + 1);
48
+ let token = webhookURL.slice(separatorIndex + 1);
49
+ if (token.length <= REDACTED_TOKEN_VISIBLE * 2) {
50
+ // Too short to show both ends without giving away the whole token.
51
+ return `${base}${token.slice(0, REDACTED_TOKEN_VISIBLE)}...`;
52
+ }
53
+ return `${base}${token.slice(0, REDACTED_TOKEN_VISIBLE)}...${token.slice(-REDACTED_TOKEN_VISIBLE)}`;
54
+ }
55
+ exports.redactWebhookURL = redactWebhookURL;
56
+ function formatWebhookFile(webhookURL) {
57
+ return `# portsecure Discord webhook. First non-comment line is used.\n${webhookURL}\n`;
58
+ }
59
+ exports.formatWebhookFile = formatWebhookFile;
60
+ async function readWebhookFile(filePath) {
61
+ let contents;
62
+ try {
63
+ contents = await promises_1.default.readFile(filePath, "utf8");
64
+ }
65
+ catch (e) {
66
+ throw new Error(`Expected a readable Discord webhook file at ${filePath}, ${e}`);
67
+ }
68
+ return parseWebhookFile({ contents, sourceName: filePath });
69
+ }
70
+ async function postToWebhook(webhookURL, message) {
71
+ let content = message;
72
+ if (content.length > DISCORD_MESSAGE_LIMIT) {
73
+ content = content.slice(0, DISCORD_MESSAGE_LIMIT - 3) + "...";
74
+ }
75
+ for (let attempt = 1; attempt <= MAX_SEND_ATTEMPTS; attempt++) {
76
+ let response = await fetch(webhookURL, {
77
+ method: "POST",
78
+ headers: { "Content-Type": "application/json" },
79
+ body: JSON.stringify({ content }),
80
+ });
81
+ if (response.ok) {
82
+ return;
83
+ }
84
+ let body = (await response.text()).slice(0, MAX_ERROR_BODY_LENGTH);
85
+ if (response.status === 429 && attempt < MAX_SEND_ATTEMPTS) {
86
+ let retryAfter = Number(response.headers.get("retry-after"));
87
+ let waitTime = retryAfter && retryAfter * 1000 || DEFAULT_RATE_LIMIT_WAIT;
88
+ await new Promise(resolve => setTimeout(resolve, waitTime));
89
+ continue;
90
+ }
91
+ throw new Error(`Expected a 2xx response from the Discord webhook, was ${response.status} ${response.statusText}, body ${body}`);
92
+ }
93
+ }
94
+ function queueSend(webhookURL, message) {
95
+ let send = () => postToWebhook(webhookURL, message);
96
+ let result = sendChain.then(send, send);
97
+ sendChain = result.catch(() => undefined);
98
+ return result;
99
+ }
100
+ /** Sends to an explicit webhook URL, for tooling that acts on a webhook before (or instead of)
101
+ configureDiscordNotifications - setup, migrations, connectivity checks. */
102
+ function sendToWebhookURL(config) {
103
+ return queueSend(config.webhookURL, config.message);
104
+ }
105
+ exports.sendToWebhookURL = sendToWebhookURL;
106
+ async function checkWebhookFileChanged() {
107
+ let state = notificationState;
108
+ if (!state) {
109
+ return;
110
+ }
111
+ let newWebhookURL;
112
+ try {
113
+ newWebhookURL = await readWebhookFile(state.filePath);
114
+ }
115
+ catch (e) {
116
+ console.error(`portsecure: Discord webhook file is no longer readable, still using the loaded webhook. ${e}`);
117
+ return;
118
+ }
119
+ if (newWebhookURL === state.webhookURL) {
120
+ return;
121
+ }
122
+ // Warn the old channel first, with the new webhook redacted, so a stolen old webhook does not
123
+ // hand the attacker a usable new one.
124
+ try {
125
+ await queueSend(state.webhookURL, `**portsecure**: the Discord webhook in \`${state.filePath}\` changed to`
126
+ + ` \`${redactWebhookURL(newWebhookURL)}\`.`
127
+ + ` Notifications are moving to the new webhook and this channel will stop receiving them.`);
128
+ }
129
+ catch (e) {
130
+ console.error(`portsecure: failed to warn the old Discord webhook about the change. ${e}`);
131
+ }
132
+ state.webhookURL = newWebhookURL;
133
+ }
134
+ /** Must be called once on startup, before any notification is sent. Aborts the process if the
135
+ webhook file is missing or invalid, then re-checks the file on an interval and warns the old
136
+ webhook whenever it changes. */
137
+ async function configureDiscordNotifications(config) {
138
+ if (notificationState) {
139
+ throw new Error(`Expected configureDiscordNotifications to be called once, was called again (already using ${notificationState.filePath})`);
140
+ }
141
+ let filePath = (config === null || config === void 0 ? void 0 : config.filePath) || exports.DEFAULT_WEBHOOK_FILE_PATH;
142
+ let checkInterval = (config === null || config === void 0 ? void 0 : config.checkInterval) || DEFAULT_CHECK_INTERVAL;
143
+ let webhookURL;
144
+ try {
145
+ webhookURL = await readWebhookFile(filePath);
146
+ }
147
+ catch (e) {
148
+ console.error(`portsecure: refusing to start without a valid Discord webhook file.\n${e}`);
149
+ process.exit(1);
150
+ }
151
+ let checkTimer = setInterval(() => {
152
+ checkWebhookFileChanged().catch(e => console.error(`portsecure: Discord webhook file check failed. ${e}`));
153
+ }, checkInterval);
154
+ // The check should never be the reason the process stays alive.
155
+ checkTimer.unref();
156
+ notificationState = { filePath, webhookURL, checkTimer };
157
+ return { filePath, checkInterval };
158
+ }
159
+ exports.configureDiscordNotifications = configureDiscordNotifications;
160
+ // DO NOT add new calls to this. Every message goes to a real Discord server someone reads, so a
161
+ // notification is only ever added when the user explicitly asks for that specific case. Startup,
162
+ // success, errors, retries and recoveries all belong in a log instead.
163
+ async function sendDiscordNotification(message) {
164
+ let state = notificationState;
165
+ if (!state) {
166
+ throw new Error(`Expected configureDiscordNotifications to be called before sending, was called with message ${message.slice(0, MAX_ERROR_BODY_LENGTH)}`);
167
+ }
168
+ await queueSend(state.webhookURL, message);
169
+ }
170
+ exports.sendDiscordNotification = sendDiscordNotification;
171
+ function stopDiscordNotifications() {
172
+ if (!notificationState) {
173
+ return;
174
+ }
175
+ clearInterval(notificationState.checkTimer);
176
+ notificationState = undefined;
177
+ }
178
+ exports.stopDiscordNotifications = stopDiscordNotifications;
179
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY29yZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImRpc2NvcmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMkRBQTZCO0FBRTdCLHNIQUFzSDtBQUN0SCxrR0FBa0c7QUFDbEcsOENBQThDO0FBRWpDLFFBQUEseUJBQXlCLEdBQUcsaUNBQWlDLENBQUM7QUFDM0UsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUM3QyxNQUFNLHNCQUFzQixHQUFHO0lBQzNCLG1DQUFtQztJQUNuQyxzQ0FBc0M7SUFDdEMsMENBQTBDO0NBQzdDLENBQUM7QUFDRixNQUFNLHFCQUFxQixHQUFHLElBQUksQ0FBQztBQUNuQyxNQUFNLHFCQUFxQixHQUFHLEdBQUcsQ0FBQztBQUNsQyxNQUFNLGlCQUFpQixHQUFHLENBQUMsQ0FBQztBQUM1QixNQUFNLHNCQUFzQixHQUFHLENBQUMsQ0FBQztBQUNqQyxNQUFNLHVCQUF1QixHQUFHLENBQUMsR0FBRyxJQUFJLENBQUM7QUFFekMsSUFBSSxpQkFJUyxDQUFDO0FBRWQsc0ZBQXNGO0FBQ3RGLDRDQUE0QztBQUM1QyxJQUFJLFNBQVMsR0FBcUIsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBRXBEO3NDQUNzQztBQUN0QyxTQUFnQixnQkFBZ0IsQ0FBQyxNQUFnRDtJQUM3RSxJQUFJLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sQ0FBQztJQUN0QyxJQUFJLEtBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUN4RyxJQUFJLFVBQVUsR0FBRyxL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QUMsS0FBSyxFQUFFLENBQUM7UUFDVCxNQUFNLElBQUksS0FBSyxDQUFDLCtGQUErRixPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxxQkFBcUIsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM5SixDQUFDO0lBQ0QsTUFBTSxTQUFTLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUMvQyxDQUFDO0FBTkQsMERBTUM7QUFFRCxTQUFnQix3QkFBd0I7SUFDcEMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDckIsT0FBTztJQUNYLENBQUM7SUFDRCxhQUFhLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDNUMsaUJBQWlCLEdBQUcsU0FBUyxDQUFDO0FBQ2xDLENBQUM7QUFORCw0REFNQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcblxuLy8gUE9SVEVEIENPREU6IHNlY3VyaXR5L2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9wb3J0c2VjdXJlRGFlbW9uLmpzIGVtYmVkcyBhIGhhbmQgcG9ydCBvZiB0aGlzIGZpbGUgaW4gcGxhaW4gSlMsIHNvIHRoZVxuLy8gZGFlbW9uIGNhbiBydW4gd2l0aCBubyBkZXBlbmRlbmNpZXMuIFRoZSB0d28gYXJlIGV4cGVjdGVkIHRvIGJlaGF2ZSBpZGVudGljYWxseSAtIGlmIHlvdSBjaGFuZ2Vcbi8vIG9uZSwgbWFrZSB0aGUgbWF0Y2hpbmcgY2hhbmdlIGluIHRoZSBvdGhlci5cblxuZXhwb3J0IGNvbnN0IERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEggPSBcIi9ldGMvcG9ydHNlY3VyZS9kaXNjb3JkLXdlYmhvb2tcIjtcbmNvbnN0IERFRkFVTFRfQ0hFQ0tfSU5URVJWQUwgPSA1ICogNjAgKiAxMDAwO1xuY29uc3QgVkFMSURfV0VCSE9PS19QUkVGSVhFUyA9IFtcbiAgICBcImh0dHBzOi8vZGlzY29yZC5jb20vYXBpL3dlYmhvb2tzL1wiLFxuICAgIFwiaHR0cHM6Ly9kaXNjb3JkYXBwLmNvbS9hcGkvd2ViaG9va3MvXCIsXG4gICAgXCJodHRwczovL2NhbmFyeS5kaXNjb3JkLmNvbS9hcGkvd2ViaG9va3MvXCIsXG5dO1xuY29uc3QgRElTQ09SRF9NRVNTQUdFX0xJTUlUID0gMjAwMDtcbmNvbnN0IE1BWF9FUlJPUl9CT0RZX0xFTkdUSCA9IDUwMDtcbmNvbnN0IE1BWF9TRU5EX0FUVEVNUFRTID0gMztcbmNvbnN0IFJFREFDVEVEX1RPS0VOX1ZJU0lCTEUgPSA4O1xuY29uc3QgREVGQVVMVF9SQVRFX0xJTUlUX1dBSVQgPSAyICogMTAwMDtcblxubGV0IG5vdGlmaWNhdGlvblN0YXRlOiB7XG4gICAgZmlsZVBhdGg6IHN0cmluZztcbiAgICB3ZWJob29rVVJMOiBzdHJpbmc7XG4gICAgY2hlY2tUaW1lcjogTm9kZUpTLlRpbWVvdXQ7XG59IHwgdW5kZWZpbmVkO1xuXG4vLyBEaXNjb3JkIHdlYmhvb2tzIGFyZSByYXRlIGxpbWl0ZWQgKHJvdWdobHkgNSByZXF1ZXN0cyBwZXIgMiBzZWNvbmRzKSwgc28gZXZlcnkgc2VuZFxuLy8gZ29lcyB0aHJvdWdoIG9uZSBjaGFpbiBpbnN0ZWFkIG9mIHJhY2luZy5cbmxldCBzZW5kQ2hhaW46IFByb21pc2U8dW5rbm93bj4gPSBQcm9taXNlLnJlc29sdmUoKTtcblxuLyoqIFB1bGxzIHRoZSB3ZWJob29rIFVSTCBvdXQgb2Ygd2ViaG9vayBmaWxlIGNvbnRlbnRzLiBgc291cmNlTmFtZWAgb25seSBhcHBlYXJzIGluIGVycm9ycywgc29cbiAgICBjYWxsZXJzIGNhbiBuYW1lIGEgcmVtb3RlIHBhdGguICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VXZWJob29rRmlsZShjb25maWc6IHsgY29udGVudHM6IHN0cmluZzsgc291cmNlTmFtZTogc3RyaW5nIH0pIHtcbiAgICBsZXQgeyBjb250ZW50cywgc291cmNlTmFtZSB9ID0gY29uZmlnO1xuICAgIGxldCBsaW5lcyA9IGNvbnRlbnRzLnNwbGl0KFwiXFxuXCIpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKS5maWx0ZXIobGluZSA9PiBsaW5lICYmICFsaW5lLnN0YXJ0c1dpdGgoXCIjXCIpKTtcbiAgICBsZXQgd2ViaG9va1VSTCA9IGxpbmVzWzBdO1xuICAgIGlmICghd2ViaG9va1VSTCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGEgRGlzY29yZCB3ZWJob29rIFVSTCBpbiAke3NvdXJjZU5hbWV9LCB0aGUgZmlsZSBoYXMgbm8gdXNhYmxlIGxpbmVzYCk7XG4gICAgfVxuICAgIGlmICghVkFMSURfV0VCSE9PS19QUkVGSVhFUy5zb21lKHByZWZpeCA9PiB3ZWJob29rVVJMLnN0YXJ0c1dpdGgocHJlZml4KSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkIGEgRGlzY29yZCB3ZWJob29rIFVSTCBzdGFydGluZyB3aXRoIG9uZSBvZiAke1ZBTElEX1dFQkhPT0tfUFJFRklYRVMuam9pbihcIiwgXCIpfSwgYFxuICAgICAgICAgICAgKyBgd2FzICR7d2ViaG9va1VSTC5zbGljZSgwLCBNQVhfRVJST1JfQk9EWV9MRU5HVEgpfSAoaW4gJHtzb3VyY2VOYW1lfSlgXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB3ZWJob29rVVJMO1xufVxuXG4vKiogS2VlcHMgdGhlIGlkIGFuZCBib3RoIGVuZHMgb2YgdGhlIHRva2VuLCBzbyBhIHJlYWRlciBjYW4gY29uZmlybSB3aGljaCB3ZWJob29rIHJlcGxhY2VkIHRoZWlyc1xuICAgIHdpdGhvdXQgcmVjZWl2aW5nIG9uZSB0aGV5IGNvdWxkIHBvc3QgdG8uICovXG5leHBvcnQgZnVuY3Rpb24gcmVkYWN0V2ViaG9va1VSTCh3ZWJob29rVVJMOiBzdHJpbmcpIHtcbiAgICBsZXQgc2VwYXJhdG9ySW5kZXggPSB3ZWJob29rVVJMLmxhc3RJbmRleE9mKFwiL1wiKTtcbiAgICBsZXQgYmFzZSA9IHdlYmhvb2tVUkwuc2xpY2UoMCwgc2VwYXJhdG9ySW5kZXggKyAxKTtcbiAgICBsZXQgdG9rZW4gPSB3ZWJob29rVVJMLnNsaWNlKHNlcGFyYXRvckluZGV4ICsgMSk7XG4gICAgaWYgKHRva2VuLmxlbmd0aCA8PSBSRURBQ1RFRF9UT0tFTl9WSVNJQkxFICogMikge1xuICAgICAgICAvLyBUb28gc2hvcnQgdG8gc2hvdyBib3RoIGVuZHMgd2l0aG91dCBnaXZpbmcgYXdheSB0aGUgd2hvbGUgdG9rZW4uXG4gICAgICAgIHJldHVybiBgJHtiYXNlfSR7dG9rZW4uc2xpY2UoMCwgUkVEQUNURURfVE9LRU5fVklTSUJMRSl9Li4uYDtcbiAgICB9XG4gICAgcmV0dXJuIGAke2Jhc2V9JHt0b2tlbi5zbGljZSgwLCBSRURBQ1RFRF9UT0tFTl9WSVNJQkxFKX0uLi4ke3Rva2VuLnNsaWNlKC1SRURBQ1RFRF9UT0tFTl9WSVNJQkxFKX1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0V2ViaG9va0ZpbGUod2ViaG9va1VSTDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGAjIHBvcnRzZWN1cmUgRGlzY29yZCB3ZWJob29rLiBGaXJzdCBub24tY29tbWVudCBsaW5lIGlzIHVzZWQuXFxuJHt3ZWJob29rVVJMfVxcbmA7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRXZWJob29rRmlsZShmaWxlUGF0aDogc3RyaW5nKSB7XG4gICAgbGV0IGNvbnRlbnRzO1xuICAgIHRyeSB7XG4gICAgICAgIGNvbnRlbnRzID0gYXdhaXQgZnMucmVhZEZpbGUoZmlsZVBhdGgsIFwidXRmOFwiKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSByZWFkYWJsZSBEaXNjb3JkIHdlYmhvb2sgZmlsZSBhdCAke2ZpbGVQYXRofSwgJHtlfWApO1xuICAgIH1cbiAgICByZXR1cm4gcGFyc2VXZWJob29rRmlsZSh7IGNvbnRlbnRzLCBzb3VyY2VOYW1lOiBmaWxlUGF0aCB9KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcG9zdFRvV2ViaG9vayh3ZWJob29rVVJMOiBzdHJpbmcsIG1lc3NhZ2U6IHN0cmluZykge1xuICAgIGxldCBjb250ZW50ID0gbWVzc2FnZTtcbiAgICBpZiAoY29udGVudC5sZW5ndGggPiBESVNDT1JEX01FU1NBR0VfTElNSVQpIHtcbiAgICAgICAgY29udGVudCA9IGNvbnRlbnQuc2xpY2UoMCwgRElTQ09SRF9NRVNTQUdFX0xJTUlUIC0gMykgKyBcIi4uLlwiO1xuICAgIH1cbiAgICBmb3IgKGxldCBhdHRlbXB0ID0gMTsgYXR0ZW1wdCA8PSBNQVhfU0VORF9BVFRFTVBUUzsgYXR0ZW1wdCsrKSB7XG4gICAgICAgIGxldCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHdlYmhvb2tVUkwsIHtcbiAgICAgICAgICAgIG1ldGhvZDogXCJQT1NUXCIsXG4gICAgICAgICAgICBoZWFkZXJzOiB7IFwiQ29udGVudC1UeXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiIH0sXG4gICAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGNvbnRlbnQgfSksXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAocmVzcG9uc2Uub2spIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBsZXQgYm9keSA9IChhd2FpdCByZXNwb25zZS50ZXh0KCkpLnNsaWNlKDAsIE1BWF9FUlJPUl9CT0RZX0xFTkdUSCk7XG4gICAgICAgIGlmIChyZXNwb25zZS5zdGF0dXMgPT09IDQyOSAmJiBhdHRlbXB0IDwgTUFYX1NFTkRfQVRURU1QVFMpIHtcbiAgICAgICAgICAgIGxldCByZXRyeUFmdGVyID0gTnVtYmVyKHJlc3BvbnNlLmhlYWRlcnMuZ2V0KFwicmV0cnktYWZ0ZXJcIikpO1xuICAgICAgICAgICAgbGV0IHdhaXRUaW1lID0gcmV0cnlBZnRlciAmJiByZXRyeUFmdGVyICogMTAwMCB8fCBERUZBVUxUX1JBVEVfTElNSVRfV0FJVDtcbiAgICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCB3YWl0VGltZSkpO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkIGEgMnh4IHJlc3BvbnNlIGZyb20gdGhlIERpc2NvcmQgd2ViaG9vaywgd2FzICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9LCBib2R5ICR7Ym9keX1gXG4gICAgICAgICk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBxdWV1ZVNlbmQod2ViaG9va1VSTDogc3RyaW5nLCBtZXNzYWdlOiBzdHJpbmcpIHtcbiAgICBsZXQgc2VuZCA9ICgpID0+IHBvc3RUb1dlYmhvb2sod2ViaG9va1VSTCwgbWVzc2FnZSk7XG4gICAgbGV0IHJlc3VsdCA9IHNlbmRDaGFpbi50aGVuKHNlbmQsIHNlbmQpO1xuICAgIHNlbmRDaGFpbiA9IHJlc3VsdC5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuICAgIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKiBTZW5kcyB0byBhbiBleHBsaWNpdCB3ZWJob29rIFVSTCwgZm9yIHRvb2xpbmcgdGhhdCBhY3RzIG9uIGEgd2ViaG9vayBiZWZvcmUgKG9yIGluc3RlYWQgb2YpXG4gICAgY29uZmlndXJlRGlzY29yZE5vdGlmaWNhdGlvbnMgLSBzZXR1cCwgbWlncmF0aW9ucywgY29ubmVjdGl2aXR5IGNoZWNrcy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZW5kVG9XZWJob29rVVJMKGNvbmZpZzogeyB3ZWJob29rVVJMOiBzdHJpbmc7IG1lc3NhZ2U6IHN0cmluZyB9KSB7XG4gICAgcmV0dXJuIHF1ZXVlU2VuZChjb25maWcud2ViaG9va1VSTCwgY29uZmlnLm1lc3NhZ2UpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBjaGVja1dlYmhvb2tGaWxlQ2hhbmdlZCgpIHtcbiAgICBsZXQgc3RhdGUgPSBub3RpZmljYXRpb25TdGF0ZTtcbiAgICBpZiAoIXN0YXRlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IG5ld1dlYmhvb2tVUkw7XG4gICAgdHJ5IHtcbiAgICAgICAgbmV3V2ViaG9va1VSTCA9IGF3YWl0IHJlYWRXZWJob29rRmlsZShzdGF0ZS5maWxlUGF0aCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zb2xlLmVycm9yKGBwb3J0c2VjdXJlOiBEaXNjb3JkIHdlYmhvb2sgZmlsZSBpcyBubyBsb25nZXIgcmVhZGFibGUsIHN0aWxsIHVzaW5nIHRoZSBsb2FkZWQgd2ViaG9vay4gJHtlfWApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChuZXdXZWJob29rVVJMID09PSBzdGF0ZS53ZWJob29rVVJMKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gV2FybiB0aGUgb2xkIGNoYW5uZWwgZmlyc3QsIHdpdGggdGhlIG5ldyB3ZWJob29rIHJlZGFjdGVkLCBzbyBhIHN0b2xlbiBvbGQgd2ViaG9vayBkb2VzIG5vdFxuICAgIC8vIGhhbmQgdGhlIGF0dGFja2VyIGEgdXNhYmxlIG5ldyBvbmUuXG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgcXVldWVTZW5kKFxuICAgICAgICAgICAgc3RhdGUud2ViaG9va1VSTCxcbiAgICAgICAgICAgIGAqKnBvcnRzZWN1cmUqKjogdGhlIERpc2NvcmQgd2ViaG9vayBpbiBcXGAke3N0YXRlLmZpbGVQYXRofVxcYCBjaGFuZ2VkIHRvYFxuICAgICAgICAgICAgKyBgIFxcYCR7cmVkYWN0V2ViaG9va1VSTChuZXdXZWJob29rVVJMKX1cXGAuYFxuICAgICAgICAgICAgKyBgIE5vdGlmaWNhdGlvbnMgYXJlIG1vdmluZyB0byB0aGUgbmV3IHdlYmhvb2sgYW5kIHRoaXMgY2hhbm5lbCB3aWxsIHN0b3AgcmVjZWl2aW5nIHRoZW0uYFxuICAgICAgICApO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgcG9ydHNlY3VyZTogZmFpbGVkIHRvIHdhcm4gdGhlIG9sZCBEaXNjb3JkIHdlYmhvb2sgYWJvdXQgdGhlIGNoYW5nZS4gJHtlfWApO1xuICAgIH1cbiAgICBzdGF0ZS53ZWJob29rVVJMID0gbmV3V2ViaG9va1VSTDtcbn1cblxuLyoqIE11c3QgYmUgY2FsbGVkIG9uY2Ugb24gc3RhcnR1cCwgYmVmb3JlIGFueSBub3RpZmljYXRpb24gaXMgc2VudC4gQWJvcnRzIHRoZSBwcm9jZXNzIGlmIHRoZVxuICAgIHdlYmhvb2sgZmlsZSBpcyBtaXNzaW5nIG9yIGludmFsaWQsIHRoZW4gcmUtY2hlY2tzIHRoZSBmaWxlIG9uIGFuIGludGVydmFsIGFuZCB3YXJucyB0aGUgb2xkXG4gICAgd2ViaG9vayB3aGVuZXZlciBpdCBjaGFuZ2VzLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNvbmZpZ3VyZURpc2NvcmROb3RpZmljYXRpb25zKGNvbmZpZz86IHtcbiAgICBmaWxlUGF0aD86IHN0cmluZztcbiAgICBjaGVja0ludGVydmFsPzogbnVtYmVyO1xufSkge1xuICAgIGlmIChub3RpZmljYXRpb25TdGF0ZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGNvbmZpZ3VyZURpc2NvcmROb3RpZmljYXRpb25zIHRvIGJlIGNhbGxlZCBvbmNlLCB3YXMgY2FsbGVkIGFnYWluIChhbHJlYWR5IHVzaW5nICR7bm90aWZpY2F0aW9uU3RhdGUuZmlsZVBhdGh9KWApO1xuICAgIH1cbiAgICBsZXQgZmlsZVBhdGggPSBjb25maWc/LmZpbGVQYXRoIHx8IERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEg7XG4gICAgbGV0IGNoZWNrSW50ZXJ2YWwgPSBjb25maWc/LmNoZWNrSW50ZXJ2YWwgfHwgREVGQVVMVF9DSEVDS19JTlRFUlZBTDtcbiAgICBsZXQgd2ViaG9va1VSTDtcbiAgICB0cnkge1xuICAgICAgICB3ZWJob29rVVJMID0gYXdhaXQgcmVhZFdlYmhvb2tGaWxlKGZpbGVQYXRoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYHBvcnRzZWN1cmU6IHJlZnVzaW5nIHRvIHN0YXJ0IHdpdGhvdXQgYSB2YWxpZCBEaXNjb3JkIHdlYmhvb2sgZmlsZS5cXG4ke2V9YCk7XG4gICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICB9XG4gICAgbGV0IGNoZWNrVGltZXIgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICAgIGNoZWNrV2ViaG9va0ZpbGVDaGFuZ2VkKCkuY2F0Y2goZSA9PiBjb25zb2xlLmVycm9yKGBwb3J0c2VjdXJlOiBEaXNjb3JkIHdlYmhvb2sgZmlsZSBjaGVjayBmYWlsZWQuICR7ZX1gKSk7XG4gICAgfSwgY2hlY2tJbnRlcnZhbCk7XG4gICAgLy8gVGhlIGNoZWNrIHNob3VsZCBuZXZlciBiZSB0aGUgcmVhc29uIHRoZSBwcm9jZXNzIHN0YXlzIGFsaXZlLlxuICAgIGNoZWNrVGltZXIudW5yZWYoKTtcbiAgICBub3RpZmljYXRpb25TdGF0ZSA9IHsgZmlsZVBhdGgsIHdlYmhvb2tVUkwsIGNoZWNrVGltZXIgfTtcbiAgICByZXR1cm4geyBmaWxlUGF0aCwgY2hlY2tJbnRlcnZhbCB9O1xufVxuXG4vLyBETyBOT1QgYWRkIG5ldyBjYWxscyB0byB0aGlzLiBFdmVyeSBtZXNzYWdlIGdvZXMgdG8gYSByZWFsIERpc2NvcmQgc2VydmVyIHNvbWVvbmUgcmVhZHMsIHNvIGFcbi8vIG5vdGlmaWNhdGlvbiBpcyBvbmx5IGV2ZXIgYWRkZWQgd2hlbiB0aGUgdXNlciBleHBsaWNpdGx5IGFza3MgZm9yIHRoYXQgc3BlY2lmaWMgY2FzZS4gU3RhcnR1cCxcbi8vIHN1Y2Nlc3MsIGVycm9ycywgcmV0cmllcyBhbmQgcmVjb3ZlcmllcyBhbGwgYmVsb25nIGluIGEgbG9nIGluc3RlYWQuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2VuZERpc2NvcmROb3RpZmljYXRpb24obWVzc2FnZTogc3RyaW5nKSB7XG4gICAgbGV0IHN0YXRlID0gbm90aWZpY2F0aW9uU3RhdGU7XG4gICAgaWYgKCFzdGF0ZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGNvbmZpZ3VyZURpc2NvcmROb3RpZmljYXRpb25zIHRvIGJlIGNhbGxlZCBiZWZvcmUgc2VuZGluZywgd2FzIGNhbGxlZCB3aXRoIG1lc3NhZ2UgJHttZXNzYWdlLnNsaWNlKDAsIE1BWF9FUlJPUl9CT0RZX0xFTkdUSCl9YCk7XG4gICAgfVxuICAgIGF3YWl0IHF1ZXVlU2VuZChzdGF0ZS53ZWJob29rVVJMLCBtZXNzYWdlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHN0b3BEaXNjb3JkTm90aWZpY2F0aW9ucygpIHtcbiAgICBpZiAoIW5vdGlmaWNhdGlvblN0YXRlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY2xlYXJJbnRlcnZhbChub3RpZmljYXRpb25TdGF0ZS5jaGVja1RpbWVyKTtcbiAgICBub3RpZmljYXRpb25TdGF0ZSA9IHVuZGVmaW5lZDtcbn1cbiJdfQ==
180
+ /* _JS_SOURCE_HASH = "d5d1b1a9e262cd5b805adb2fa7d074d1a92e9a99dd226550e7f055a9766ebd19"; */
@@ -0,0 +1,68 @@
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.formatManifest = exports.buildManifest = exports.digestFile = exports.normalizeContent = exports.listRepoFiles = exports.MANIFEST_VERSION = exports.SIGN_NAMESPACE = exports.SIGNATURE_NAME = exports.MANIFEST_NAME = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const spawn_1 = require("../helpers/spawn");
11
+ // PORTED CODE: security/authorizedKeys/daemon/portsecureDaemon.js contains a plain JS port of the
12
+ // verifying half of this file, so it can check a signature with no dependencies. Both sides must
13
+ // agree on the manifest shape and on which files it covers - if you change one, make the matching
14
+ // change in the other.
15
+ exports.MANIFEST_NAME = "signedfiles.json";
16
+ exports.SIGNATURE_NAME = "signedfiles.json.sig";
17
+ // ssh signatures are namespaced, so a signature made for one purpose cannot be replayed as another.
18
+ exports.SIGN_NAMESPACE = "signfiles";
19
+ exports.MANIFEST_VERSION = 1;
20
+ /** Everything in the repo that is not ignored, which is exactly what a clone of it will contain.
21
+ The manifest and its signature are left out, since they cannot describe themselves. */
22
+ async function listRepoFiles(repoPath) {
23
+ let result = await (0, spawn_1.spawnPromise)({
24
+ command: "git",
25
+ args: ["ls-files", "--cached", "--others", "--exclude-standard"],
26
+ cwd: repoPath,
27
+ });
28
+ if (result.status !== 0) {
29
+ throw new Error(`Expected to list the files in ${repoPath}, git ls-files exited ${result.status}. `
30
+ + `${(result.stdout + result.stderr).trim()}`);
31
+ }
32
+ return result.stdout.split("\n")
33
+ .map(line => line.trim())
34
+ .filter(line => line && line !== exports.MANIFEST_NAME && line !== exports.SIGNATURE_NAME)
35
+ .sort();
36
+ }
37
+ exports.listRepoFiles = listRepoFiles;
38
+ /** A Windows checkout turns LF into CRLF, so the same commit hashes differently there than it
39
+ does on the machine that pulls it. Normalising first makes the digest describe the content
40
+ rather than whichever checkout produced it. latin1 round trips every byte, so this is safe on
41
+ files that are not text. */
42
+ function normalizeContent(contents) {
43
+ return Buffer.from(contents.toString("latin1").replace(/\r\n/g, "\n"), "latin1");
44
+ }
45
+ exports.normalizeContent = normalizeContent;
46
+ /** The size and hash of a file's normalised content. The size is the normalised one on purpose,
47
+ so it agrees with the hash rather than with whatever the local checkout happens to hold. */
48
+ async function digestFile(filePath) {
49
+ let contents = normalizeContent(await promises_1.default.readFile(filePath));
50
+ return { size: contents.length, sha256: crypto_1.default.createHash("sha256").update(contents).digest("hex") };
51
+ }
52
+ exports.digestFile = digestFile;
53
+ async function buildManifest(repoPath) {
54
+ let files = [];
55
+ for (let relativePath of await listRepoFiles(repoPath)) {
56
+ let digest = await digestFile(path_1.default.join(repoPath, relativePath));
57
+ files.push({ path: relativePath, size: digest.size, sha256: digest.sha256 });
58
+ }
59
+ return { version: exports.MANIFEST_VERSION, files };
60
+ }
61
+ exports.buildManifest = buildManifest;
62
+ /** Sorted keys and a trailing newline, so the same tree always produces the same bytes to sign. */
63
+ function formatManifest(manifest) {
64
+ return JSON.stringify(manifest, undefined, 4) + "\n";
65
+ }
66
+ exports.formatManifest = formatManifest;
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFuaWZlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJtYW5pZmVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUN4Qiw0Q0FBZ0Q7QUFFaEQsa0dBQWtHO0FBQ2xHLGlHQUFpRztBQUNqRyxrR0FBa0c7QUFDbEcsdUJBQXVCO0FBRVYsUUFBQSxhQUFhLEdBQUcsa0JBQWtCLENBQUM7QUFDbkMsUUFBQSxjQUFjLEdBQUcsc0JBQXNCLENBQUM7QUFDckQsb0dBQW9HO0FBQ3ZGLFFBQUEsY0FBYyxHQUFHLFdBQVcsQ0FBQztBQUM3QixRQUFBLGdCQUFnQixHQUFHLENBQUMsQ0FBQztBQU9sQzswRkFDMEY7QUFDbkYsS0FBSyxVQUFVLGFBQWEsQ0FBQyxRQUFnQjtJQUNoRCxJQUFJLE1BQU0sR0FBRyxNQUFNLElBQUEsb0JBQVksRUFBQztRQUM1QixPQUFPLEVBQUUsS0FBSztRQUNkLElBQUksRUFBRSxDQUFDLFVBQVUsRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLG9CQUFvQixDQUFDO1FBQ2hFLEdBQUcsRUFBRSxRQUFRO0tBQ2hCLENBQUMsQ0FBQztJQUNILElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN0QixNQUFNLElBQUksS0FBSyxDQUNYLGlDQUFpQyxRQUFRLHlCQUF5QixNQUFNLENBQUMsTUFBTSxJQUFJO2NBQ2pGLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUNoRCxDQUFDO0lBQ04sQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDO1NBQzNCLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztTQUN4QixNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLElBQUksSUFBSSxLQUFLLHFCQUFhLElBQUksSUFBSSxLQUFLLHNCQUFjLENBQUM7U0FDekUsSUFBSSxFQUFFLENBQUM7QUFDaEIsQ0FBQztBQWhCRCxzQ0FnQkM7QUFFRDs7OytCQUcrQjtBQUMvQixTQUFnQixnQkFBZ0IsQ0FBQyxRQUFnQjtJQUM3QyxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0FBQ3JGLENBQUM7QUFGRCw0Q0FFQztBQUVEOytGQUMrRjtBQUN4RixLQUFLLFVBQVUsVUFBVSxDQUFDLFFBQWdCO0lBQzdDLElBQUksUUFBUSxHQUFHLGdCQUFnQixDQUFDLE1BQU0sa0JBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztJQUM3RCxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLGdCQUFNLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztBQUN6RyxDQUFDO0FBSEQsZ0NBR0M7QUFFTSxLQUFLLFVBQVUsYUFBYSxDQUFDLFFBQWdCO0lBQ2hELElBQUksS0FBSyxHQUFzQixFQUFFLENBQUM7SUFDbEMsS0FBSyxJQUFJLFlBQVksSUFBSSxNQUFNLGFBQWEsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ3JELElBQUksTUFBTSxHQUFHLE1BQU0sVUFBVSxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7UUFDakUsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQ2pGLENBQUM7SUFDRCxPQUFPLEVBQUUsT0FBTyxFQUFFLHdCQUFnQixFQUFFLEtBQUssRUFBRSxDQUFDO0FBQ2hELENBQUM7QUFQRCxzQ0FPQztBQUVELG1HQUFtRztBQUNuRyxTQUFnQixjQUFjLENBQUMsUUFBa0I7SUFDN0MsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO0FBQ3pELENBQUM7QUFGRCx3Q0FFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjcnlwdG8gZnJvbSBcImNyeXB0b1wiO1xuaW1wb3J0IGZzIGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IHNwYXduUHJvbWlzZSB9IGZyb20gXCIuLi9oZWxwZXJzL3NwYXduXCI7XG5cbi8vIFBPUlRFRCBDT0RFOiBzZWN1cml0eS9hdXRob3JpemVkS2V5cy9kYWVtb24vcG9ydHNlY3VyZURhZW1vbi5qcyBjb250YWlucyBhIHBsYWluIEpTIHBvcnQgb2YgdGhlXG4vLyB2ZXJpZnlpbmcgaGFsZiBvZiB0aGlzIGZpbGUsIHNvIGl0IGNhbiBjaGVjayBhIHNpZ25hdHVyZSB3aXRoIG5vIGRlcGVuZGVuY2llcy4gQm90aCBzaWRlcyBtdXN0XG4vLyBhZ3JlZSBvbiB0aGUgbWFuaWZlc3Qgc2hhcGUgYW5kIG9uIHdoaWNoIGZpbGVzIGl0IGNvdmVycyAtIGlmIHlvdSBjaGFuZ2Ugb25lLCBtYWtlIHRoZSBtYXRjaGluZ1xuLy8gY2hhbmdlIGluIHRoZSBvdGhlci5cblxuZXhwb3J0IGNvbnN0IE1BTklGRVNUX05BTUUgPSBcInNpZ25lZGZpbGVzLmpzb25cIjtcbmV4cG9ydCBjb25zdCBTSUdOQVRVUkVfTkFNRSA9IFwic2lnbmVkZmlsZXMuanNvbi5zaWdcIjtcbi8vIHNzaCBzaWduYXR1cmVzIGFyZSBuYW1lc3BhY2VkLCBzbyBhIHNpZ25hdHVyZSBtYWRlIGZvciBvbmUgcHVycG9zZSBjYW5ub3QgYmUgcmVwbGF5ZWQgYXMgYW5vdGhlci5cbmV4cG9ydCBjb25zdCBTSUdOX05BTUVTUEFDRSA9IFwic2lnbmZpbGVzXCI7XG5leHBvcnQgY29uc3QgTUFOSUZFU1RfVkVSU0lPTiA9IDE7XG5cbmV4cG9ydCB0eXBlIE1hbmlmZXN0ID0ge1xuICAgIHZlcnNpb246IG51bWJlcjtcbiAgICBmaWxlczogeyBwYXRoOiBzdHJpbmc7IHNpemU6IG51bWJlcjsgc2hhMjU2OiBzdHJpbmcgfVtdO1xufTtcblxuLyoqIEV2ZXJ5dGhpbmcgaW4gdGhlIHJlcG8gdGhhdCBpcyBub3QgaWdub3JlZCwgd2hpY2ggaXMgZXhhY3RseSB3aGF0IGEgY2xvbmUgb2YgaXQgd2lsbCBjb250YWluLlxuICAgIFRoZSBtYW5pZmVzdCBhbmQgaXRzIHNpZ25hdHVyZSBhcmUgbGVmdCBvdXQsIHNpbmNlIHRoZXkgY2Fubm90IGRlc2NyaWJlIHRoZW1zZWx2ZXMuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbGlzdFJlcG9GaWxlcyhyZXBvUGF0aDogc3RyaW5nKSB7XG4gICAgbGV0IHJlc3VsdCA9IGF3YWl0IHNwYXduUHJvbWlzZSh7XG4gICAgICAgIGNvbW1hbmQ6IFwiZ2l0XCIsXG4gICAgICAgIGFyZ3M6IFtcImxzLWZpbGVzXCIsIFwiLS1jYWNoZWRcIiwgXCItLW90aGVyc1wiLCBcIi0tZXhjbHVkZS1zdGFuZGFyZFwiXSxcbiAgICAgICAgY3dkOiByZXBvUGF0aCxcbiAgICB9KTtcbiAgICBpZiAocmVzdWx0LnN0YXR1cyAhPT0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgdG8gbGlzdCB0aGUgZmlsZXMgaW4gJHtyZXBvUGF0aH0sIGdpdCBscy1maWxlcyBleGl0ZWQgJHtyZXN1bHQuc3RhdHVzfS4gYFxuICAgICAgICAgICAgKyBgJHsocmVzdWx0LnN0ZG91dCArIHJlc3VsdC5zdGRlcnIpLnRyaW0oKX1gXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQuc3Rkb3V0LnNwbGl0KFwiXFxuXCIpXG4gICAgICAgIC5tYXAobGluZSA9PiBsaW5lLnRyaW0oKSlcbiAgICAgICAgLmZpbHRlcihsaW5lID0+IGxpbmUgJiYgbGluZSAhPT0gTUFOSUZFU1RfTkFNRSAmJiBsaW5lICE9PSBTSUdOQVRVUkVfTkFNRSlcbiAgICAgICAgLnNvcnQoKTtcbn1cblxuLyoqIEEgV2luZG93cyBjaGVja291dCB0dXJucyBMRiBpbnRvIENSTEYsIHNvIHRoZSBzYW1lIGNvbW1pdCBoYXNoZXMgZGlmZmVyZW50bHkgdGhlcmUgdGhhbiBpdFxuICAgIGRvZXMgb24gdGhlIG1hY2hpbmUgdGhhdCBwdWxscyBpdC4gTm9ybWFsaXNpbmcgZmlyc3QgbWFrZXMgdGhlIGRpZ2VzdCBkZXNjcmliZSB0aGUgY29udGVudFxuICAgIHJhdGhlciB0aGFuIHdoaWNoZXZlciBjaGVja291dCBwcm9kdWNlZCBpdC4gbGF0aW4xIHJvdW5kIHRyaXBzIGV2ZXJ5IGJ5dGUsIHNvIHRoaXMgaXMgc2FmZSBvblxuICAgIGZpbGVzIHRoYXQgYXJlIG5vdCB0ZXh0LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG5vcm1hbGl6ZUNvbnRlbnQoY29udGVudHM6IEJ1ZmZlcikge1xuICAgIHJldHVybiBCdWZmZXIuZnJvbShjb250ZW50cy50b1N0cmluZyhcImxhdGluMVwiKS5yZXBsYWNlKC9cXHJcXG4vZywgXCJcXG5cIiksIFwibGF0aW4xXCIpO1xufVxuXG4vKiogVGhlIHNpemUgYW5kIGhhc2ggb2YgYSBmaWxlJ3Mgbm9ybWFsaXNlZCBjb250ZW50LiBUaGUgc2l6ZSBpcyB0aGUgbm9ybWFsaXNlZCBvbmUgb24gcHVycG9zZSxcbiAgICBzbyBpdCBhZ3JlZXMgd2l0aCB0aGUgaGFzaCByYXRoZXIgdGhhbiB3aXRoIHdoYXRldmVyIHRoZSBsb2NhbCBjaGVja291dCBoYXBwZW5zIHRvIGhvbGQuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGlnZXN0RmlsZShmaWxlUGF0aDogc3RyaW5nKSB7XG4gICAgbGV0IGNvbnRlbnRzID0gbm9ybWFsaXplQ29udGVudChhd2FpdCBmcy5yZWFkRmlsZShmaWxlUGF0aCkpO1xuICAgIHJldHVybiB7IHNpemU6IGNvbnRlbnRzLmxlbmd0aCwgc2hhMjU2OiBjcnlwdG8uY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUoY29udGVudHMpLmRpZ2VzdChcImhleFwiKSB9O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYnVpbGRNYW5pZmVzdChyZXBvUGF0aDogc3RyaW5nKSB7XG4gICAgbGV0IGZpbGVzOiBNYW5pZmVzdFtcImZpbGVzXCJdID0gW107XG4gICAgZm9yIChsZXQgcmVsYXRpdmVQYXRoIG9mIGF3YWl0IGxpc3RSZXBvRmlsZXMocmVwb1BhdGgpKSB7XG4gICAgICAgIGxldCBkaWdlc3QgPSBhd2FpdCBkaWdlc3RGaWxlKHBhdGguam9pbihyZXBvUGF0aCwgcmVsYXRpdmVQYXRoKSk7XG4gICAgICAgIGZpbGVzLnB1c2goeyBwYXRoOiByZWxhdGl2ZVBhdGgsIHNpemU6IGRpZ2VzdC5zaXplLCBzaGEyNTY6IGRpZ2VzdC5zaGEyNTYgfSk7XG4gICAgfVxuICAgIHJldHVybiB7IHZlcnNpb246IE1BTklGRVNUX1ZFUlNJT04sIGZpbGVzIH07XG59XG5cbi8qKiBTb3J0ZWQga2V5cyBhbmQgYSB0cmFpbGluZyBuZXdsaW5lLCBzbyB0aGUgc2FtZSB0cmVlIGFsd2F5cyBwcm9kdWNlcyB0aGUgc2FtZSBieXRlcyB0byBzaWduLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdE1hbmlmZXN0KG1hbmlmZXN0OiBNYW5pZmVzdCkge1xuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShtYW5pZmVzdCwgdW5kZWZpbmVkLCA0KSArIFwiXFxuXCI7XG59XG4iXX0=
68
+ /* _JS_SOURCE_HASH = "b03b98c478c3d40c11ab30a9be0a3a73ddfbb41e74230864ca940f9edd07dc68"; */