sliftutils 1.7.124 → 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 (50) hide show
  1. package/CLAUDE.md +3 -1
  2. package/bin/derivekey.js +10 -0
  3. package/bin/portsecuredaemon.js +13 -0
  4. package/bin/securessh.js +10 -0
  5. package/bin/setupnotify.js +9 -0
  6. package/bin/signfiles.js +10 -0
  7. package/bin/unrevoke.js +9 -0
  8. package/package.json +14 -3
  9. package/security/README.md +141 -0
  10. package/security/authorizedKeys/authorizedKeys.ts +66 -0
  11. package/security/authorizedKeys/daemon/authLog.ts +117 -0
  12. package/security/authorizedKeys/daemon/daemon.ts +308 -0
  13. package/security/authorizedKeys/daemon/git.ts +119 -0
  14. package/security/authorizedKeys/daemon/notify.ts +25 -0
  15. package/security/authorizedKeys/daemon/paths.ts +26 -0
  16. package/security/authorizedKeys/daemon/portsecure.service +19 -0
  17. package/security/authorizedKeys/daemon/revocation.ts +306 -0
  18. package/security/authorizedKeys/daemon/rootKeys.ts +135 -0
  19. package/security/authorizedKeys/daemon/sshdConfig.ts +85 -0
  20. package/security/authorizedKeys/daemon/state.ts +108 -0
  21. package/security/authorizedKeys/daemon/trust.ts +291 -0
  22. package/security/authorizedKeys/daemon/userKeys.ts +76 -0
  23. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +73 -0
  24. package/security/authorizedKeys/dist/revokeSource.ts.cache +44 -0
  25. package/security/authorizedKeys/dist/secureSSH.ts.cache +552 -0
  26. package/security/authorizedKeys/dist/sources.ts.cache +24 -0
  27. package/security/authorizedKeys/dist/unrevoke.ts.cache +145 -0
  28. package/security/authorizedKeys/revokeSource.ts +40 -0
  29. package/security/authorizedKeys/secureSSH.ts +613 -0
  30. package/security/authorizedKeys/sources.ts +20 -0
  31. package/security/authorizedKeys/unrevoke.ts +149 -0
  32. package/security/helpers/dist/paths.ts.cache +28 -0
  33. package/security/helpers/dist/remoteSSH.ts.cache +90 -0
  34. package/security/helpers/dist/spawn.ts.cache +34 -0
  35. package/security/helpers/paths.ts +20 -0
  36. package/security/helpers/remoteSSH.ts +95 -0
  37. package/security/helpers/spawn.ts +36 -0
  38. package/security/keys/deriveKey.ts +72 -0
  39. package/security/keys/dist/deriveKey.ts.cache +72 -0
  40. package/security/keys/dist/sshKeyFile.ts.cache +153 -0
  41. package/security/keys/sshKeyFile.ts +156 -0
  42. package/security/notifications/discord.ts +190 -0
  43. package/security/notifications/dist/discord.ts.cache +180 -0
  44. package/security/notifications/remoteWebhook.ts +85 -0
  45. package/security/notifications/setupNotify.ts +29 -0
  46. package/security/signedFiles/dist/manifest.ts.cache +68 -0
  47. package/security/signedFiles/dist/signFiles.ts.cache +146 -0
  48. package/security/signedFiles/manifest.ts +69 -0
  49. package/security/signedFiles/signFiles.ts +151 -0
  50. package/storage/BulkDatabase2/dist/BulkDatabaseBase.ts.cache +17 -20
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ // PORTED CODE: security/authorizedKeys/daemon/portsecureDaemon.js contains a plain JS port of everything here, so it can
3
+ // resolve the same locations with no dependencies. Both sides must derive identical paths from a
4
+ // repo url - if you change one, make the matching change in the other.
5
+ Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
6
+ //exports.sourceRepoPath = exports.sourceKeyPath = exports.sourceName = exports.REPOS_DIR = exports.REPO_KEYS_DIR = void 0;
7
+ exports.REPO_KEYS_DIR = "/etc/portsecure/repo-keys";
8
+ exports.REPOS_DIR = "/var/lib/portsecure/authorized-keys-repos";
9
+ /** A repo url reduced to something usable as a file name. Derived rather than configured, so the
10
+ daemon and the deploy script always agree on where a source's key and checkout live. */
11
+ function sourceName(repoURL) {
12
+ return repoURL.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
13
+ }
14
+ exports.sourceName = sourceName;
15
+ function sourceKeyPath(repoURL) {
16
+ return `${exports.REPO_KEYS_DIR}/${sourceName(repoURL)}`;
17
+ }
18
+ exports.sourceKeyPath = sourceKeyPath;
19
+ function sourceRepoPath(repoURL) {
20
+ return `${exports.REPOS_DIR}/${sourceName(repoURL)}`;
21
+ }
22
+ exports.sourceRepoPath = sourceRepoPath;
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic291cmNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNvdXJjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHlIQUF5SDtBQUN6SCxpR0FBaUc7QUFDakcsdUVBQXVFOzs7QUFFMUQsUUFBQSxhQUFhLEdBQUcsMkJBQTJCLENBQUM7QUFDNUMsUUFBQSxTQUFTLEdBQUcsMkNBQTJDLENBQUM7QUFFckU7MkZBQzJGO0FBQzNGLFNBQWdCLFVBQVUsQ0FBQyxPQUFlO0lBQ3RDLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ3hGLENBQUM7QUFGRCxnQ0FFQztBQUVELFNBQWdCLGFBQWEsQ0FBQyxPQUFlO0lBQ3pDLE9BQU8sR0FBRyxxQkFBYSxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0FBQ3JELENBQUM7QUFGRCxzQ0FFQztBQUVELFNBQWdCLGNBQWMsQ0FBQyxPQUFlO0lBQzFDLE9BQU8sR0FBRyxpQkFBUyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0FBQ2pELENBQUM7QUFGRCx3Q0FFQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFBPUlRFRCBDT0RFOiBzZWN1cml0eS9hdXRob3JpemVkS2V5cy9kYWVtb24vcG9ydHNlY3VyZURhZW1vbi5qcyBjb250YWlucyBhIHBsYWluIEpTIHBvcnQgb2YgZXZlcnl0aGluZyBoZXJlLCBzbyBpdCBjYW5cbi8vIHJlc29sdmUgdGhlIHNhbWUgbG9jYXRpb25zIHdpdGggbm8gZGVwZW5kZW5jaWVzLiBCb3RoIHNpZGVzIG11c3QgZGVyaXZlIGlkZW50aWNhbCBwYXRocyBmcm9tIGFcbi8vIHJlcG8gdXJsIC0gaWYgeW91IGNoYW5nZSBvbmUsIG1ha2UgdGhlIG1hdGNoaW5nIGNoYW5nZSBpbiB0aGUgb3RoZXIuXG5cbmV4cG9ydCBjb25zdCBSRVBPX0tFWVNfRElSID0gXCIvZXRjL3BvcnRzZWN1cmUvcmVwby1rZXlzXCI7XG5leHBvcnQgY29uc3QgUkVQT1NfRElSID0gXCIvdmFyL2xpYi9wb3J0c2VjdXJlL2F1dGhvcml6ZWQta2V5cy1yZXBvc1wiO1xuXG4vKiogQSByZXBvIHVybCByZWR1Y2VkIHRvIHNvbWV0aGluZyB1c2FibGUgYXMgYSBmaWxlIG5hbWUuIERlcml2ZWQgcmF0aGVyIHRoYW4gY29uZmlndXJlZCwgc28gdGhlXG4gICAgZGFlbW9uIGFuZCB0aGUgZGVwbG95IHNjcmlwdCBhbHdheXMgYWdyZWUgb24gd2hlcmUgYSBzb3VyY2UncyBrZXkgYW5kIGNoZWNrb3V0IGxpdmUuICovXG5leHBvcnQgZnVuY3Rpb24gc291cmNlTmFtZShyZXBvVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gcmVwb1VSTC5yZXBsYWNlKC9bXkEtWmEtejAtOV0rL2csIFwiLVwiKS5yZXBsYWNlKC9eLSt8LSskL2csIFwiXCIpLnRvTG93ZXJDYXNlKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzb3VyY2VLZXlQYXRoKHJlcG9VUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPX0tFWVNfRElSfS8ke3NvdXJjZU5hbWUocmVwb1VSTCl9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNvdXJjZVJlcG9QYXRoKHJlcG9VUkw6IHN0cmluZykge1xuICAgIHJldHVybiBgJHtSRVBPU19ESVJ9LyR7c291cmNlTmFtZShyZXBvVVJMKX1gO1xufVxuIl19
24
+ /* _JS_SOURCE_HASH = "d4a51163d16ac57a8fa5966ca3b09eaebcbef4ca2200c706a7e125220c7db7b8"; */
@@ -0,0 +1,145 @@
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.main = exports.revokedKeysInRepo = exports.readRemoteRevocations = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const runPromise_1 = require("socket-function/src/runPromise");
11
+ const authorizedKeys_1 = require("./authorizedKeys");
12
+ const revokeSource_1 = require("./revokeSource");
13
+ const authorizedKeys_2 = require("./authorizedKeys");
14
+ const paths_1 = require("../helpers/paths");
15
+ const spawn_1 = require("../helpers/spawn");
16
+ const UNREVOKES_DIR = "unrevoked";
17
+ const REVOCATIONS_DIR = "revocations";
18
+ const USAGE = `Usage: yarn unrevoke [keys-repo]
19
+
20
+ Run this in a keys repo, or name one. It reads that repo's revoke repo and writes one unrevoke file
21
+ naming every revocation in it, so the keys are accepted again once each machine's hour long wait
22
+ passes.
23
+
24
+ Keys that were revoked should normally be deleted from the repo instead. Unrevoking only matters
25
+ for a key you still want.`;
26
+ /** Every revocation the revoke repo lists. Cloned read only into a temp directory, with whatever
27
+ credentials this machine already has - the derived deploy key is for servers, and a person
28
+ running this has their own access to the repo. */
29
+ async function readRemoteRevocations(config) {
30
+ let { sourceURL } = config;
31
+ let temporaryDirectory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "unrevoke-"));
32
+ let repoPath = path_1.default.join(temporaryDirectory, "repo");
33
+ let clone = await (0, spawn_1.spawnPromise)({
34
+ command: "git",
35
+ args: ["clone", "--depth", "1", (0, revokeSource_1.revokeRepoURL)(sourceURL), repoPath],
36
+ });
37
+ if (clone.status !== 0) {
38
+ await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
39
+ throw new Error(`Expected to be able to read ${(0, revokeSource_1.revokeRepoURL)(sourceURL)}.\n`
40
+ + `${(clone.stdout + clone.stderr).trim()}`);
41
+ }
42
+ let revocations = [];
43
+ let directory = path_1.default.join(repoPath, REVOCATIONS_DIR);
44
+ try {
45
+ for (let name of (await promises_1.default.readdir(directory)).sort()) {
46
+ if (name.endsWith(".json")) {
47
+ revocations.push(JSON.parse(await promises_1.default.readFile(path_1.default.join(directory, name), "utf8")));
48
+ }
49
+ }
50
+ }
51
+ catch (e) {
52
+ // No revocations directory at all just means nothing has ever been revoked.
53
+ }
54
+ await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
55
+ return revocations;
56
+ }
57
+ exports.readRemoteRevocations = readRemoteRevocations;
58
+ /** Which keys in a repo are revoked. What signfiles and securessh refuse over. */
59
+ async function revokedKeysInRepo(config) {
60
+ let { repoPath, sourceURL } = config;
61
+ let revocations = await readRemoteRevocations({ sourceURL });
62
+ let revokedFingerprints = new Set(revocations.map(revocation => revocation.fingerprint));
63
+ let unrevoked = new Set();
64
+ try {
65
+ let directory = path_1.default.join(repoPath, UNREVOKES_DIR);
66
+ for (let name of await promises_1.default.readdir(directory)) {
67
+ if (!name.endsWith(".json")) {
68
+ continue;
69
+ }
70
+ let parsed = JSON.parse(await promises_1.default.readFile(path_1.default.join(directory, name), "utf8"));
71
+ for (let revocationId of parsed.revocationIds || []) {
72
+ unrevoked.add(revocationId);
73
+ }
74
+ }
75
+ }
76
+ catch (e) {
77
+ // Nothing has been unrevoked.
78
+ }
79
+ let stillRevoked = revocations.filter(revocation => !unrevoked.has(revocation.revocationId));
80
+ let keys = (0, authorizedKeys_1.normalizeKeys)(await promises_1.default.readFile(path_1.default.join(repoPath, "authorized_keys"), "utf8").catch(() => ""));
81
+ return stillRevoked
82
+ .filter(revocation => keys.some(key => (0, authorizedKeys_1.keyFingerprint)(key) === revocation.fingerprint))
83
+ .map(revocation => ({ revocation, key: keys.find(key => (0, authorizedKeys_1.keyFingerprint)(key) === revocation.fingerprint) || "" }));
84
+ }
85
+ exports.revokedKeysInRepo = revokedKeysInRepo;
86
+ /** The repo named, or the one we are standing in. Checked the same way securessh checks it: a git
87
+ repo that actually holds keys and has an origin to clone from. */
88
+ async function resolveKeysRepo(named) {
89
+ let cwd = named && (0, paths_1.expandHome)(named) || undefined;
90
+ let repoPath = (await (0, runPromise_1.runPromise)("git rev-parse --show-toplevel", { quiet: true, cwd })).trim();
91
+ if (!repoPath) {
92
+ throw new Error(`Expected ${named || "the current directory"} to be inside a git repo, it is not.\n${USAGE}`);
93
+ }
94
+ try {
95
+ await (0, authorizedKeys_2.readRepoKeys)(repoPath);
96
+ }
97
+ catch (e) {
98
+ throw new Error(`Expected ${repoPath} to be a keys repo, it holds no keys.\n${e}\n${USAGE}`);
99
+ }
100
+ let originURL = (await (0, runPromise_1.runPromise)("git remote get-url origin", { quiet: true, cwd: repoPath })).trim();
101
+ if (!originURL) {
102
+ throw new Error(`Expected ${repoPath} to have an origin remote, it has none.\n${USAGE}`);
103
+ }
104
+ return { repoPath, originURL };
105
+ }
106
+ async function main() {
107
+ var _a;
108
+ let argv = process.argv.slice(2);
109
+ if (argv.length > 1) {
110
+ throw new Error(`Expected at most a keys repo, was ${argv.length} argument(s)\n${USAGE}`);
111
+ }
112
+ let { repoPath, originURL } = await resolveKeysRepo(argv[0]);
113
+ let revocations = await readRemoteRevocations({ sourceURL: originURL });
114
+ if (!revocations.length) {
115
+ console.log(`${(0, revokeSource_1.revokeRepoURL)(originURL)} lists no revocations, so there is nothing to undo.`);
116
+ return;
117
+ }
118
+ let directory = path_1.default.join(repoPath, UNREVOKES_DIR);
119
+ await promises_1.default.mkdir(directory, { recursive: true });
120
+ let stamp = new Date().toISOString().replace(/[:.]/g, "-");
121
+ let unrevokeId = `${stamp}-unrevoke`;
122
+ await promises_1.default.writeFile(path_1.default.join(directory, `${unrevokeId}.json`), JSON.stringify({
123
+ unrevokeId,
124
+ createdAt: new Date().toISOString(),
125
+ // Named one by one rather than as a blanket "allow everything again", so this file only
126
+ // ever undoes the revocations that existed when it was written.
127
+ revocationIds: revocations.map(revocation => revocation.revocationId),
128
+ revocations: revocations.map(revocation => ({
129
+ revocationId: revocation.revocationId,
130
+ fingerprint: revocation.fingerprint,
131
+ revokedAt: revocation.revokedAt,
132
+ revokedBy: revocation.revokedBy,
133
+ attempt: revocation.attempt,
134
+ })),
135
+ }, undefined, 4) + "\n");
136
+ console.log(`Wrote ${UNREVOKES_DIR}/${unrevokeId}.json covering ${revocations.length} revocation(s):`);
137
+ for (let revocation of revocations) {
138
+ console.log(` ${revocation.fingerprint} revoked by ${revocation.revokedBy || "?"} from ${((_a = revocation.attempt) === null || _a === void 0 ? void 0 : _a.ip) || "?"}`);
139
+ }
140
+ console.log(`Each machine waits an hour after seeing it before the keys work again.`);
141
+ console.log(`Sign and publish it with:\n yarn signfiles git`);
142
+ }
143
+ exports.main = main;
144
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5yZXZva2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ1bnJldm9rZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSwyREFBNkI7QUFDN0IsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QiwrREFBNEQ7QUFDNUQscURBQWlFO0FBQ2pFLGlEQUErQztBQUMvQyxxREFBZ0Q7QUFDaEQsNENBQThDO0FBQzlDLDRDQUFnRDtBQUVoRCxNQUFNLGFBQWEsR0FBRyxXQUFXLENBQUM7QUFDbEMsTUFBTSxlQUFlLEdBQUcsYUFBYSxDQUFDO0FBQ3RDLE1BQU0sS0FBSyxHQUFHOzs7Ozs7OzBCQU9ZLENBQUM7QUFXM0I7O3FEQUVxRDtBQUM5QyxLQUFLLFVBQVUscUJBQXFCLENBQUMsTUFBNkI7SUFDckUsSUFBSSxFQUFFLFNBQVMsRUFBRSxHQUFHLE1BQU0sQ0FBQztJQUMzQixJQUFJLGtCQUFrQixHQUFHLE1BQU0sa0JBQUUsQ0FBQyxPQUFPLENBQUMsY0FBSSxDQUFDLElBQUksQ0FBQyxZQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQztJQUMvRSxJQUFJLFFBQVEsR0FBRyxjQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3JELElBQUksS0FBSyxHQUFHLE1BQU0sSUFBQSxvQkFBWSxFQUFDO1FBQzNCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsSUFBSSxFQUFFLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsSUFBQSw0QkFBYSxFQUFDLFNBQVMsQ0FBQyxFQUFFLFFBQVEsQ0FBQztLQUN0RSxDQUFDLENBQUM7SUFDSCxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDckIsTUFBTSxrQkFBRSxDQUFDLEVBQUUsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFD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QUFDLE1BQU0saUJBQWlCLENBQUMsQ0FBQztJQUN2RyxLQUFLLElBQUksVUFBVSxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ2pDLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxVQUFVLENBQUMsV0FBVyxlQUFlLFVBQVUsQ0FBQyxTQUFTLElBQUksR0FBRyxTQUFTLENBQUEsTUFBQSxVQUFVLENBQUMsT0FBTywwQ0FBRSxFQUFFLEtBQUksR0FBRyxFQUFFLENBQUMsQ0FBQztJQUMvSCxDQUFDO0lBQ0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3RUFBd0UsQ0FBQyxDQUFDO0lBQ3RGLE9BQU8sQ0FBQyxHQUFHLENBQUMsaURBQWlELENBQUMsQ0FBQztBQUNuRSxDQUFDO0FBdENELG9CQXNDQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBvcyBmcm9tIFwib3NcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBydW5Qcm9taXNlIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvcnVuUHJvbWlzZVwiO1xuaW1wb3J0IHsga2V5RmluZ2VycHJpbnQsIG5vcm1hbGl6ZUtleXMgfSBmcm9tIFwiLi9hdXRob3JpemVkS2V5c1wiO1xuaW1wb3J0IHsgcmV2b2tlUmVwb1VSTCB9IGZyb20gXCIuL3Jldm9rZVNvdXJjZVwiO1xuaW1wb3J0IHsgcmVhZFJlcG9LZXlzIH0gZnJvbSBcIi4vYXV0aG9yaXplZEtleXNcIjtcbmltcG9ydCB7IGV4cGFuZEhvbWUgfSBmcm9tIFwiLi4vaGVscGVycy9wYXRoc1wiO1xuaW1wb3J0IHsgc3Bhd25Qcm9taXNlIH0gZnJvbSBcIi4uL2hlbHBlcnMvc3Bhd25cIjtcblxuY29uc3QgVU5SRVZPS0VTX0RJUiA9IFwidW5yZXZva2VkXCI7XG5jb25zdCBSRVZPQ0FUSU9OU19ESVIgPSBcInJldm9jYXRpb25zXCI7XG5jb25zdCBVU0FHRSA9IGBVc2FnZTogeWFybiB1bnJldm9rZSBba2V5cy1yZXBvXVxuXG5SdW4gdGhpcyBpbiBhIGtleXMgcmVwbywgb3IgbmFtZSBvbmUuIEl0IHJlYWRzIHRoYXQgcmVwbydzIHJldm9rZSByZXBvIGFuZCB3cml0ZXMgb25lIHVucmV2b2tlIGZpbGVcbm5hbWluZyBldmVyeSByZXZvY2F0aW9uIGluIGl0LCBzbyB0aGUga2V5cyBhcmUgYWNjZXB0ZWQgYWdhaW4gb25jZSBlYWNoIG1hY2hpbmUncyBob3VyIGxvbmcgd2FpdFxucGFzc2VzLlxuXG5LZXlzIHRoYXQgd2VyZSByZXZva2VkIHNob3VsZCBub3JtYWxseSBiZSBkZWxldGVkIGZyb20gdGhlIHJlcG8gaW5zdGVhZC4gVW5yZXZva2luZyBvbmx5IG1hdHRlcnNcbmZvciBhIGtleSB5b3Ugc3RpbGwgd2FudC5gO1xuXG5leHBvcnQgdHlwZSBSZXZvY2F0aW9uID0ge1xuICAgIHJldm9jYXRpb25JZDogc3RyaW5nO1xuICAgIGZpbmdlcnByaW50OiBzdHJpbmc7XG4gICAga2V5Pzogc3RyaW5nO1xuICAgIHJldm9rZWRBdD86IHN0cmluZztcbiAgICByZXZva2VkQnk/OiBzdHJpbmc7XG4gICAgYXR0ZW1wdD86IHsgaXA/OiBzdHJpbmc7IHVzZXI/OiBzdHJpbmc7IHJlcXVpcmVkPzogc3RyaW5nIH07XG59O1xuXG4vKiogRXZlcnkgcmV2b2NhdGlvbiB0aGUgcmV2b2tlIHJlcG8gbGlzdHMuIENsb25lZCByZWFkIG9ubHkgaW50byBhIHRlbXAgZGlyZWN0b3J5LCB3aXRoIHdoYXRldmVyXG4gICAgY3JlZGVudGlhbHMgdGhpcyBtYWNoaW5lIGFscmVhZHkgaGFzIC0gdGhlIGRlcml2ZWQgZGVwbG95IGtleSBpcyBmb3Igc2VydmVycywgYW5kIGEgcGVyc29uXG4gICAgcnVubmluZyB0aGlzIGhhcyB0aGVpciBvd24gYWNjZXNzIHRvIHRoZSByZXBvLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlYWRSZW1vdGVSZXZvY2F0aW9ucyhjb25maWc6IHsgc291cmNlVVJMOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHNvdXJjZVVSTCB9ID0gY29uZmlnO1xuICAgIGxldCB0ZW1wb3JhcnlEaXJlY3RvcnkgPSBhd2FpdCBmcy5ta2R0ZW1wKHBhdGguam9pbihvcy50bXBkaXIoKSwgXCJ1bnJldm9rZS1cIikpO1xuICAgIGxldCByZXBvUGF0aCA9IHBhdGguam9pbih0ZW1wb3JhcnlEaXJlY3RvcnksIFwicmVwb1wiKTtcbiAgICBsZXQgY2xvbmUgPSBhd2FpdCBzcGF3blByb21pc2Uoe1xuICAgICAgICBjb21tYW5kOiBcImdpdFwiLFxuICAgICAgICBhcmdzOiBbXCJjbG9uZVwiLCBcIi0tZGVwdGhcIiwgXCIxXCIsIHJldm9rZVJlcG9VUkwoc291cmNlVVJMKSwgcmVwb1BhdGhdLFxuICAgIH0pO1xuICAgIGlmIChjbG9uZS5zdGF0dXMgIT09IDApIHtcbiAgICAgICAgYXdhaXQgZnMucm0odGVtcG9yYXJ5RGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IHRydWUgfSk7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBFeHBlY3RlZCB0byBiZSBhYmxlIHRvIHJlYWQgJHtyZXZva2VSZXBvVVJMKHNvdXJjZVVSTCl9LlxcbmBcbiAgICAgICAgICAgICsgYCR7KGNsb25lLnN0ZG91dCArIGNsb25lLnN0ZGVycikudHJpbSgpfWBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgbGV0IHJldm9jYXRpb25zOiBSZXZvY2F0aW9uW10gPSBbXTtcbiAgICBsZXQgZGlyZWN0b3J5ID0gcGF0aC5qb2luKHJlcG9QYXRoLCBSRVZPQ0FUSU9OU19ESVIpO1xuICAgIHRyeSB7XG4gICAgICAgIGZvciAobGV0IG5hbWUgb2YgKGF3YWl0IGZzLnJlYWRkaXIoZGlyZWN0b3J5KSkuc29ydCgpKSB7XG4gICAgICAgICAgICBpZiAobmFtZS5lbmRzV2l0aChcIi5qc29uXCIpKSB7XG4gICAgICAgICAgICAgICAgcmV2b2NhdGlvbnMucHVzaChKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKHBhdGguam9pbihkaXJlY3RvcnksIG5hbWUpLCBcInV0ZjhcIikpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8gTm8gcmV2b2NhdGlvbnMgZGlyZWN0b3J5IGF0IGFsbCBqdXN0IG1lYW5zIG5vdGhpbmcgaGFzIGV2ZXIgYmVlbiByZXZva2VkLlxuICAgIH1cbiAgICBhd2FpdCBmcy5ybSh0ZW1wb3JhcnlEaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICByZXR1cm4gcmV2b2NhdGlvbnM7XG59XG5cbi8qKiBXaGljaCBrZXlzIGluIGEgcmVwbyBhcmUgcmV2b2tlZC4gV2hhdCBzaWduZmlsZXMgYW5kIHNlY3VyZXNzaCByZWZ1c2Ugb3Zlci4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXZva2VkS2V5c0luUmVwbyhjb25maWc6IHsgcmVwb1BhdGg6IHN0cmluZzsgc291cmNlVVJMOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHJlcG9QYXRoLCBzb3VyY2VVUkwgfSA9IGNvbmZpZztcbiAgICBsZXQgcmV2b2NhdGlvbnMgPSBhd2FpdCByZWFkUmVtb3RlUmV2b2NhdGlvbnMoeyBzb3VyY2VVUkwgfSk7XG4gICAgbGV0IHJldm9rZWRGaW5nZXJwcmludHMgPSBuZXcgU2V0KHJldm9jYXRpb25zLm1hcChyZXZvY2F0aW9uID0+IHJldm9jYXRpb24uZmluZ2VycHJpbnQpKTtcbiAgICBsZXQgdW5yZXZva2VkID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgdHJ5IHtcbiAgICAgICAgbGV0IGRpcmVjdG9yeSA9IHBhdGguam9pbihyZXBvUGF0aCwgVU5SRVZPS0VTX0RJUik7XG4gICAgICAgIGZvciAobGV0IG5hbWUgb2YgYXdhaXQgZnMucmVhZGRpcihkaXJlY3RvcnkpKSB7XG4gICAgICAgICAgICBpZiAoIW5hbWUuZW5kc1dpdGgoXCIuanNvblwiKSkge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGV0IHBhcnNlZCA9IEpTT04ucGFyc2UoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKGRpcmVjdG9yeSwgbmFtZSksIFwidXRmOFwiKSk7XG4gICAgICAgICAgICBmb3IgKGxldCByZXZvY2F0aW9uSWQgb2YgcGFyc2VkLnJldm9jYXRpb25JZHMgfHwgW10pIHtcbiAgICAgICAgICAgICAgICB1bnJldm9rZWQuYWRkKHJldm9jYXRpb25JZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIE5vdGhpbmcgaGFzIGJlZW4gdW5yZXZva2VkLlxuICAgIH1cbiAgICBsZXQgc3RpbGxSZXZva2VkID0gcmV2b2NhdGlvbnMuZmlsdGVyKHJldm9jYXRpb24gPT4gIXVucmV2b2tlZC5oYXMocmV2b2NhdGlvbi5yZXZvY2F0aW9uSWQpKTtcbiAgICBsZXQga2V5cyA9IG5vcm1hbGl6ZUtleXMoYXdhaXQgZnMucmVhZEZpbGUocGF0aC5qb2luKHJlcG9QYXRoLCBcImF1dGhvcml6ZWRfa2V5c1wiKSwgXCJ1dGY4XCIpLmNhdGNoKCgpID0+IFwiXCIpKTtcbiAgICByZXR1cm4gc3RpbGxSZXZva2VkXG4gICAgICAgIC5maWx0ZXIocmV2b2NhdGlvbiA9PiBrZXlzLnNvbWUoa2V5ID0+IGtleUZpbmdlcnByaW50KGtleSkgPT09IHJldm9jYXRpb24uZmluZ2VycHJpbnQpKVxuICAgICAgICAubWFwKHJldm9jYXRpb24gPT4gKHsgcmV2b2NhdGlvbiwga2V5OiBrZXlzLmZpbmQoa2V5ID0+IGtleUZpbmdlcnByaW50KGtleSkgPT09IHJldm9jYXRpb24uZmluZ2VycHJpbnQpIHx8IFwiXCIgfSkpO1xufVxuXG4vKiogVGhlIHJlcG8gbmFtZWQsIG9yIHRoZSBvbmUgd2UgYXJlIHN0YW5kaW5nIGluLiBDaGVja2VkIHRoZSBzYW1lIHdheSBzZWN1cmVzc2ggY2hlY2tzIGl0OiBhIGdpdFxuICAgIHJlcG8gdGhhdCBhY3R1YWxseSBob2xkcyBrZXlzIGFuZCBoYXMgYW4gb3JpZ2luIHRvIGNsb25lIGZyb20uICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlS2V5c1JlcG8obmFtZWQ6IHN0cmluZyB8IHVuZGVmaW5lZCkge1xuICAgIGxldCBjd2QgPSBuYW1lZCAmJiBleHBhbmRIb21lKG5hbWVkKSB8fCB1bmRlZmluZWQ7XG4gICAgbGV0IHJlcG9QYXRoID0gKGF3YWl0IHJ1blByb21pc2UoXCJnaXQgcmV2LXBhcnNlIC0tc2hvdy10b3BsZXZlbFwiLCB7IHF1aWV0OiB0cnVlLCBjd2QgfSkpLnRyaW0oKTtcbiAgICBpZiAoIXJlcG9QYXRoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtuYW1lZCB8fCBcInRoZSBjdXJyZW50IGRpcmVjdG9yeVwifSB0byBiZSBpbnNpZGUgYSBnaXQgcmVwbywgaXQgaXMgbm90LlxcbiR7VVNBR0V9YCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHJlYWRSZXBvS2V5cyhyZXBvUGF0aCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7cmVwb1BhdGh9IHRvIGJlIGEga2V5cyByZXBvLCBpdCBob2xkcyBubyBrZXlzLlxcbiR7ZX1cXG4ke1VTQUdFfWApO1xuICAgIH1cbiAgICBsZXQgb3JpZ2luVVJMID0gKGF3YWl0IHJ1blByb21pc2UoXCJnaXQgcmVtb3RlIGdldC11cmwgb3JpZ2luXCIsIHsgcXVpZXQ6IHRydWUsIGN3ZDogcmVwb1BhdGggfSkpLnRyaW0oKTtcbiAgICBpZiAoIW9yaWdpblVSTCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7cmVwb1BhdGh9IHRvIGhhdmUgYW4gb3JpZ2luIHJlbW90ZSwgaXQgaGFzIG5vbmUuXFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcmVwb1BhdGgsIG9yaWdpblVSTCB9O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFpbigpIHtcbiAgICBsZXQgYXJndiA9IHByb2Nlc3MuYXJndi5zbGljZSgyKTtcbiAgICBpZiAoYXJndi5sZW5ndGggPiAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYXQgbW9zdCBhIGtleXMgcmVwbywgd2FzICR7YXJndi5sZW5ndGh9IGFyZ3VtZW50KHMpXFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgbGV0IHsgcmVwb1BhdGgsIG9yaWdpblVSTCB9ID0gYXdhaXQgcmVzb2x2ZUtleXNSZXBvKGFyZ3ZbMF0pO1xuXG4gICAgbGV0IHJldm9jYXRpb25zID0gYXdhaXQgcmVhZFJlbW90ZVJldm9jYXRpb25zKHsgc291cmNlVVJMOiBvcmlnaW5VUkwgfSk7XG4gICAgaWYgKCFyZXZvY2F0aW9ucy5sZW5ndGgpIHtcbiAgICAgICAgY29uc29sZS5sb2coYCR7cmV2b2tlUmVwb1VSTChvcmlnaW5VUkwpfSBsaXN0cyBubyByZXZvY2F0aW9ucywgc28gdGhlcmUgaXMgbm90aGluZyB0byB1bmRvLmApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbGV0IGRpcmVjdG9yeSA9IHBhdGguam9pbihyZXBvUGF0aCwgVU5SRVZPS0VTX0RJUik7XG4gICAgYXdhaXQgZnMubWtkaXIoZGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBsZXQgc3RhbXAgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkucmVwbGFjZSgvWzouXS9nLCBcIi1cIik7XG4gICAgbGV0IHVucmV2b2tlSWQgPSBgJHtzdGFtcH0tdW5yZXZva2VgO1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBgJHt1bnJldm9rZUlkfS5qc29uYCksIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgdW5yZXZva2VJZCxcbiAgICAgICAgY3JlYXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgIC8vIE5hbWVkIG9uZSBieSBvbmUgcmF0aGVyIHRoYW4gYXMgYSBibGFua2V0IFwiYWxsb3cgZXZlcnl0aGluZyBhZ2FpblwiLCBzbyB0aGlzIGZpbGUgb25seVxuICAgICAgICAvLyBldmVyIHVuZG9lcyB0aGUgcmV2b2NhdGlvbnMgdGhhdCBleGlzdGVkIHdoZW4gaXQgd2FzIHdyaXR0ZW4uXG4gICAgICAgIHJldm9jYXRpb25JZHM6IHJldm9jYXRpb25zLm1hcChyZXZvY2F0aW9uID0+IHJldm9jYXRpb24ucmV2b2NhdGlvbklkKSxcbiAgICAgICAgcmV2b2NhdGlvbnM6IHJldm9jYXRpb25zLm1hcChyZXZvY2F0aW9uID0+ICh7XG4gICAgICAgICAgICByZXZvY2F0aW9uSWQ6IHJldm9jYXRpb24ucmV2b2NhdGlvbklkLFxuICAgICAgICAgICAgZmluZ2VycHJpbnQ6IHJldm9jYXRpb24uZmluZ2VycHJpbnQsXG4gICAgICAgICAgICByZXZva2VkQXQ6IHJldm9jYXRpb24ucmV2b2tlZEF0LFxuICAgICAgICAgICAgcmV2b2tlZEJ5OiByZXZvY2F0aW9uLnJldm9rZWRCeSxcbiAgICAgICAgICAgIGF0dGVtcHQ6IHJldm9jYXRpb24uYXR0ZW1wdCxcbiAgICAgICAgfSkpLFxuICAgIH0sIHVuZGVmaW5lZCwgNCkgKyBcIlxcblwiKTtcblxuICAgIGNvbnNvbGUubG9nKGBXcm90ZSAke1VOUkVWT0tFU19ESVJ9LyR7dW5yZXZva2VJZH0uanNvbiBjb3ZlcmluZyAke3Jldm9jYXRpb25zLmxlbmd0aH0gcmV2b2NhdGlvbihzKTpgKTtcbiAgICBmb3IgKGxldCByZXZvY2F0aW9uIG9mIHJldm9jYXRpb25zKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGAgICR7cmV2b2NhdGlvbi5maW5nZXJwcmludH0gcmV2b2tlZCBieSAke3Jldm9jYXRpb24ucmV2b2tlZEJ5IHx8IFwiP1wifSBmcm9tICR7cmV2b2NhdGlvbi5hdHRlbXB0Py5pcCB8fCBcIj9cIn1gKTtcbiAgICB9XG4gICAgY29uc29sZS5sb2coYEVhY2ggbWFjaGluZSB3YWl0cyBhbiBob3VyIGFmdGVyIHNlZWluZyBpdCBiZWZvcmUgdGhlIGtleXMgd29yayBhZ2Fpbi5gKTtcbiAgICBjb25zb2xlLmxvZyhgU2lnbiBhbmQgcHVibGlzaCBpdCB3aXRoOlxcbiAgeWFybiBzaWduZmlsZXMgZ2l0YCk7XG59XG4iXX0=
145
+ /* _JS_SOURCE_HASH = "1f43574c62196a1eb6f7594da6497ffb17128b3c8914274f810f020ee0cc4224"; */
@@ -0,0 +1,40 @@
1
+ import { deriveEd25519Key } from "../keys/deriveKey";
2
+ import { formatOpenSSHPrivateKey, parseOpenSSHPrivateKey } from "../keys/sshKeyFile";
3
+ import { REPO_KEYS_DIR, REPOS_DIR, sourceName } from "./sources";
4
+
5
+ // A source's revocations live in a repo beside it. Derived rather than configured, so adding a
6
+ // source brings its revoke repo with it and there is nothing extra to pass on the command line.
7
+ const REVOKED_SUFFIX = "_revoked";
8
+ // GitHub will not take the same public key as a deploy key on two repos, so the revoke repo is
9
+ // reached with a key derived from the source's, under this label.
10
+ export const REVOKE_KEY_LABEL = "revokegithubkey";
11
+
12
+ export function revokeRepoURL(sourceURL: string) {
13
+ let withoutGit = sourceURL.replace(/\.git$/, "");
14
+ return `${withoutGit}${REVOKED_SUFFIX}.git`;
15
+ }
16
+
17
+ export function revokeKeyPath(sourceURL: string) {
18
+ return `${REPO_KEYS_DIR}/${sourceName(sourceURL)}${REVOKED_SUFFIX}`;
19
+ }
20
+
21
+ export function revokeRepoPath(sourceURL: string) {
22
+ return `${REPOS_DIR}/${sourceName(sourceURL)}${REVOKED_SUFFIX}`;
23
+ }
24
+
25
+ /** The revoke repo's key, worked out from the source repo's key. Anything holding the source key
26
+ can produce it, so it never has to be stored anywhere separately or handed around. */
27
+ export function deriveRevokeKey(sourcePrivateKey: string) {
28
+ let source = parseOpenSSHPrivateKey(sourcePrivateKey);
29
+ let derived = deriveEd25519Key({ seed: source.seed, label: REVOKE_KEY_LABEL });
30
+ return {
31
+ publicKey: `ssh-ed25519 ${Buffer.concat([
32
+ Buffer.from([0, 0, 0, 11]), Buffer.from("ssh-ed25519"),
33
+ Buffer.from([0, 0, 0, 32]), derived.publicKey,
34
+ ]).toString("base64")}`,
35
+ privateKeyFile: formatOpenSSHPrivateKey({
36
+ seed: derived.seed,
37
+ comment: `${source.comment || "portsecure"} ${REVOKE_KEY_LABEL}`,
38
+ }),
39
+ };
40
+ }