@p0security/cli 0.8.2 → 0.8.3

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ /** Copyright © 2024-present P0 Security
16
+
17
+ This file is part of @p0security/cli
18
+
19
+ @p0security/cli is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License.
20
+
21
+ @p0security/cli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
22
+
23
+ You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
24
+ **/
25
+ const api_1 = require("../../drivers/api");
26
+ const stdio_1 = require("../../drivers/stdio");
27
+ const grant_1 = require("../grant");
28
+ const yargs_1 = __importDefault(require("yargs"));
29
+ jest.mock("../../drivers/api");
30
+ jest.mock("../../drivers/auth");
31
+ jest.mock("../../drivers/stdio");
32
+ const mockFetchCommand = api_1.fetchCommand;
33
+ const mockPrint1 = stdio_1.print1;
34
+ const mockPrint2 = stdio_1.print2;
35
+ describe("grant", () => {
36
+ beforeEach(() => jest.clearAllMocks());
37
+ describe("when valid grant command", () => {
38
+ const command = "grant gcloud role viewer --to someone@test.com --principal-type user";
39
+ function mockFetch() {
40
+ mockFetchCommand.mockResolvedValue({
41
+ ok: true,
42
+ message: "a message",
43
+ id: "abcefg",
44
+ isPreexisting: false,
45
+ isPersistent: false,
46
+ });
47
+ }
48
+ it(`should print request response`, () => __awaiter(void 0, void 0, void 0, function* () {
49
+ mockFetch();
50
+ yield (0, grant_1.grantCommand)((0, yargs_1.default)()).parse(command);
51
+ expect(mockPrint2.mock.calls).toMatchSnapshot();
52
+ expect(mockPrint1).not.toHaveBeenCalled();
53
+ }));
54
+ });
55
+ });
@@ -0,0 +1,4 @@
1
+ import yargs from "yargs";
2
+ export declare const grantCommand: (yargs: yargs.Argv<{}>) => yargs.Argv<{
3
+ arguments: string[];
4
+ }>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.grantCommand = void 0;
4
+ /** Copyright © 2024-present P0 Security
5
+
6
+ This file is part of @p0security/cli
7
+
8
+ @p0security/cli is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License.
9
+
10
+ @p0security/cli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11
+
12
+ You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
13
+ **/
14
+ const firestore_1 = require("../drivers/firestore");
15
+ const request_1 = require("./shared/request");
16
+ const grantCommand = (yargs) => yargs.command("grant [arguments..]", "Grant access to another identity", request_1.requestArgs, (0, firestore_1.guard)((0, request_1.request)("grant")));
17
+ exports.grantCommand = grantCommand;
@@ -18,6 +18,7 @@ const stdio_1 = require("../drivers/stdio");
18
18
  const version_1 = require("../middlewares/version");
19
19
  const allow_1 = require("./allow");
20
20
  const aws_1 = require("./aws");
21
+ const grant_1 = require("./grant");
21
22
  const login_1 = require("./login");
22
23
  const ls_1 = require("./ls");
23
24
  const request_1 = require("./request");
@@ -28,6 +29,7 @@ const yargs_1 = __importDefault(require("yargs"));
28
29
  const helpers_1 = require("yargs/helpers");
29
30
  const commands = [
30
31
  aws_1.awsCommand,
32
+ grant_1.grantCommand,
31
33
  login_1.loginCommand,
32
34
  ls_1.lsCommand,
33
35
  request_1.requestCommand,
@@ -1,12 +1,4 @@
1
- import { Authn } from "../types/identity";
2
- import { RequestResponse } from "../types/request";
3
1
  import yargs from "yargs";
4
2
  export declare const requestCommand: (yargs: yargs.Argv<{}>) => yargs.Argv<{
5
3
  arguments: string[];
6
4
  }>;
7
- export declare const request: <T>(args: yargs.ArgumentsCamelCase<{
8
- arguments: string[];
9
- wait?: boolean;
10
- }>, authn?: Authn, options?: {
11
- message?: "all" | "approval-required" | "none";
12
- }) => Promise<RequestResponse<T> | undefined>;
@@ -1,15 +1,6 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.request = exports.requestCommand = void 0;
3
+ exports.requestCommand = void 0;
13
4
  /** Copyright © 2024-present P0 Security
14
5
 
15
6
  This file is part of @p0security/cli
@@ -20,97 +11,7 @@ This file is part of @p0security/cli
20
11
 
21
12
  You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
22
13
  **/
23
- const api_1 = require("../drivers/api");
24
- const auth_1 = require("../drivers/auth");
25
14
  const firestore_1 = require("../drivers/firestore");
26
- const stdio_1 = require("../drivers/stdio");
27
- const firestore_2 = require("firebase/firestore");
28
- const typescript_1 = require("typescript");
29
- const WAIT_TIMEOUT = 300e3;
30
- const APPROVED = { message: "Your request was approved", code: 0 };
31
- const DENIED = { message: "Your request was denied", code: 2 };
32
- const ERRORED = { message: "Your request encountered an error", code: 1 };
33
- const COMPLETED_REQUEST_STATUSES = {
34
- APPROVED,
35
- APPROVED_NOTIFIED: APPROVED,
36
- DONE: APPROVED,
37
- DONE_NOTIFIED: APPROVED,
38
- DENIED,
39
- ERRORED,
40
- };
41
- const isCompletedStatus = (status) => status in COMPLETED_REQUEST_STATUSES;
42
- const requestArgs = (yargs) => yargs
43
- .parserConfiguration({ "unknown-options-as-args": true })
44
- .help(false) // Turn off help in order to forward the --help command to the backend so P0 can provide the available requestable resources
45
- .option("wait", {
46
- alias: "w",
47
- boolean: true,
48
- default: false,
49
- describe: "Block until the command is completed",
50
- })
51
- .option("arguments", {
52
- array: true,
53
- string: true,
54
- default: [],
55
- });
56
- const requestCommand = (yargs) => yargs.command("request [arguments..]", "Manually request permissions on a resource", requestArgs, (0, firestore_1.guard)(exports.request));
15
+ const request_1 = require("./shared/request");
16
+ const requestCommand = (yargs) => yargs.command("request [arguments..]", "Manually request permissions on a resource", request_1.requestArgs, (0, firestore_1.guard)((0, request_1.request)("request")));
57
17
  exports.requestCommand = requestCommand;
58
- const waitForRequest = (tenantId, requestId, logMessage) => __awaiter(void 0, void 0, void 0, function* () {
59
- return yield new Promise((resolve) => {
60
- if (logMessage)
61
- (0, stdio_1.print2)("Will wait up to 5 minutes for this request to complete...");
62
- let cancel = undefined;
63
- const unsubscribe = (0, firestore_2.onSnapshot)((0, firestore_1.doc)(`o/${tenantId}/permission-requests/${requestId}`), (snap) => {
64
- const data = snap.data();
65
- if (!data)
66
- return;
67
- const { status } = data;
68
- if (isCompletedStatus(status)) {
69
- if (cancel)
70
- clearTimeout(cancel);
71
- unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
72
- const { message, code } = COMPLETED_REQUEST_STATUSES[status];
73
- if (code !== 0 || logMessage)
74
- (0, stdio_1.print2)(message);
75
- resolve(code);
76
- }
77
- });
78
- cancel = setTimeout(() => {
79
- unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
80
- (0, stdio_1.print2)("Your request did not complete within 5 minutes.");
81
- resolve(4);
82
- }, WAIT_TIMEOUT);
83
- });
84
- });
85
- const request = (args, authn, options) => __awaiter(void 0, void 0, void 0, function* () {
86
- const resolvedAuthn = authn !== null && authn !== void 0 ? authn : (yield (0, auth_1.authenticate)());
87
- const { userCredential } = resolvedAuthn;
88
- const data = yield (0, api_1.fetchCommand)(resolvedAuthn, args, [
89
- "request",
90
- ...args.arguments,
91
- ]);
92
- if (data && "ok" in data && "message" in data && data.ok) {
93
- const logMessage = !(options === null || options === void 0 ? void 0 : options.message) ||
94
- (options === null || options === void 0 ? void 0 : options.message) === "all" ||
95
- ((options === null || options === void 0 ? void 0 : options.message) === "approval-required" &&
96
- !data.isPreexisting &&
97
- !data.isPersistent);
98
- if (logMessage)
99
- (0, stdio_1.print2)(data.message);
100
- const { id } = data;
101
- if (args.wait && id && userCredential.user.tenantId) {
102
- const code = yield waitForRequest(userCredential.user.tenantId, id, logMessage);
103
- if (code) {
104
- typescript_1.sys.exit(code);
105
- return undefined;
106
- }
107
- return data;
108
- }
109
- else
110
- return undefined;
111
- }
112
- else {
113
- throw data;
114
- }
115
- });
116
- exports.request = request;
@@ -0,0 +1,14 @@
1
+ import { Authn } from "../../types/identity";
2
+ import { RequestResponse } from "../../types/request";
3
+ import yargs from "yargs";
4
+ export declare const requestArgs: <T>(yargs: yargs.Argv<T>) => yargs.Argv<T & {
5
+ wait: boolean;
6
+ } & {
7
+ arguments: string[];
8
+ }>;
9
+ export declare const request: (command: "grant" | "request") => <T>(args: yargs.ArgumentsCamelCase<{
10
+ arguments: string[];
11
+ wait?: boolean;
12
+ }>, authn?: Authn, options?: {
13
+ message?: "all" | "approval-required" | "none";
14
+ }) => Promise<RequestResponse<T> | undefined>;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.request = exports.requestArgs = void 0;
13
+ /** Copyright © 2024-present P0 Security
14
+
15
+ This file is part of @p0security/cli
16
+
17
+ @p0security/cli is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License.
18
+
19
+ @p0security/cli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
22
+ **/
23
+ const api_1 = require("../../drivers/api");
24
+ const auth_1 = require("../../drivers/auth");
25
+ const firestore_1 = require("../../drivers/firestore");
26
+ const stdio_1 = require("../../drivers/stdio");
27
+ const firestore_2 = require("firebase/firestore");
28
+ const typescript_1 = require("typescript");
29
+ const WAIT_TIMEOUT = 300e3;
30
+ const APPROVED = { message: "Your request was approved", code: 0 };
31
+ const DENIED = { message: "Your request was denied", code: 2 };
32
+ const ERRORED = { message: "Your request encountered an error", code: 1 };
33
+ const COMPLETED_REQUEST_STATUSES = {
34
+ APPROVED,
35
+ APPROVED_NOTIFIED: APPROVED,
36
+ DONE: APPROVED,
37
+ DONE_NOTIFIED: APPROVED,
38
+ DENIED,
39
+ ERRORED,
40
+ };
41
+ const isCompletedStatus = (status) => status in COMPLETED_REQUEST_STATUSES;
42
+ const requestArgs = (yargs) => yargs
43
+ .parserConfiguration({ "unknown-options-as-args": true })
44
+ .help(false) // Turn off help in order to forward the --help command to the backend so P0 can provide the available requestable resources
45
+ .option("wait", {
46
+ alias: "w",
47
+ boolean: true,
48
+ default: false,
49
+ describe: "Block until the command is completed",
50
+ })
51
+ .option("arguments", {
52
+ array: true,
53
+ string: true,
54
+ default: [],
55
+ });
56
+ exports.requestArgs = requestArgs;
57
+ const waitForRequest = (tenantId, requestId, logMessage) => __awaiter(void 0, void 0, void 0, function* () {
58
+ return yield new Promise((resolve) => {
59
+ if (logMessage)
60
+ (0, stdio_1.print2)("Will wait up to 5 minutes for this request to complete...");
61
+ let cancel = undefined;
62
+ const unsubscribe = (0, firestore_2.onSnapshot)((0, firestore_1.doc)(`o/${tenantId}/permission-requests/${requestId}`), (snap) => {
63
+ const data = snap.data();
64
+ if (!data)
65
+ return;
66
+ const { status } = data;
67
+ if (isCompletedStatus(status)) {
68
+ if (cancel)
69
+ clearTimeout(cancel);
70
+ unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
71
+ const { message, code } = COMPLETED_REQUEST_STATUSES[status];
72
+ if (code !== 0 || logMessage)
73
+ (0, stdio_1.print2)(message);
74
+ resolve(code);
75
+ }
76
+ });
77
+ cancel = setTimeout(() => {
78
+ unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
79
+ (0, stdio_1.print2)("Your request did not complete within 5 minutes.");
80
+ resolve(4);
81
+ }, WAIT_TIMEOUT);
82
+ });
83
+ });
84
+ const request = (command) => (args, authn, options) => __awaiter(void 0, void 0, void 0, function* () {
85
+ const resolvedAuthn = authn !== null && authn !== void 0 ? authn : (yield (0, auth_1.authenticate)());
86
+ const { userCredential } = resolvedAuthn;
87
+ const data = yield (0, api_1.fetchCommand)(resolvedAuthn, args, [
88
+ command,
89
+ ...args.arguments,
90
+ ]);
91
+ if (data && "ok" in data && "message" in data && data.ok) {
92
+ const logMessage = !(options === null || options === void 0 ? void 0 : options.message) ||
93
+ (options === null || options === void 0 ? void 0 : options.message) === "all" ||
94
+ ((options === null || options === void 0 ? void 0 : options.message) === "approval-required" &&
95
+ !data.isPreexisting &&
96
+ !data.isPersistent);
97
+ if (logMessage)
98
+ (0, stdio_1.print2)(data.message);
99
+ const { id } = data;
100
+ if (args.wait && id && userCredential.user.tenantId) {
101
+ const code = yield waitForRequest(userCredential.user.tenantId, id, logMessage);
102
+ if (code) {
103
+ typescript_1.sys.exit(code);
104
+ return undefined;
105
+ }
106
+ return data;
107
+ }
108
+ else
109
+ return undefined;
110
+ }
111
+ else {
112
+ throw data;
113
+ }
114
+ });
115
+ exports.request = request;
@@ -1,6 +1,6 @@
1
1
  import { Authn } from "../../types/identity";
2
2
  import { Request } from "../../types/request";
3
- import { CliSshRequest, SshRequest, SupportedSshProvider } from "../../types/ssh";
3
+ import { CliSshRequest, SshProvider, SshRequest, SupportedSshProvider } from "../../types/ssh";
4
4
  import yargs from "yargs";
5
5
  export declare type BaseSshCommandArgs = {
6
6
  sudo?: boolean;
@@ -23,6 +23,12 @@ export declare type SshCommandArgs = BaseSshCommandArgs & {
23
23
  arguments: string[];
24
24
  command?: string;
25
25
  };
26
+ export declare type CommandArgs = ScpCommandArgs | SshCommandArgs;
27
+ export declare const SSH_PROVIDERS: Record<SupportedSshProvider, SshProvider<any, any, any, any>>;
28
+ export declare const isSudoCommand: (args: {
29
+ sudo?: boolean;
30
+ command?: string;
31
+ }) => boolean;
26
32
  export declare const provisionRequest: (authn: Authn, args: yargs.ArgumentsCamelCase<BaseSshCommandArgs>, destination: string) => Promise<{
27
33
  request: Request<CliSshRequest>;
28
34
  publicKey: string;
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.requestToSsh = exports.provisionRequest = void 0;
12
+ exports.requestToSsh = exports.provisionRequest = exports.isSudoCommand = exports.SSH_PROVIDERS = void 0;
13
13
  /** Copyright © 2024-present P0 Security
14
14
 
15
15
  This file is part of @p0security/cli
@@ -27,10 +27,10 @@ const stdio_1 = require("../../drivers/stdio");
27
27
  const ssh_1 = require("../../plugins/aws/ssh");
28
28
  const ssh_2 = require("../../plugins/google/ssh");
29
29
  const ssh_3 = require("../../types/ssh");
30
- const request_1 = require("../request");
30
+ const request_1 = require("./request");
31
31
  const firestore_2 = require("firebase/firestore");
32
32
  const lodash_1 = require("lodash");
33
- const SSH_PROVIDERS = {
33
+ exports.SSH_PROVIDERS = {
34
34
  aws: ssh_1.awsSshProvider,
35
35
  gcloud: ssh_2.gcpSshProvider,
36
36
  };
@@ -48,19 +48,21 @@ const validateSshInstall = (authn, args) => __awaiter(void 0, void 0, void 0, fu
48
48
  }
49
49
  });
50
50
  const pluginToCliRequest = (request, options) => __awaiter(void 0, void 0, void 0, function* () {
51
- return yield SSH_PROVIDERS[request.permission.spec.type].toCliRequest(request, options);
51
+ return yield exports.SSH_PROVIDERS[request.permission.spec.type].toCliRequest(request, options);
52
52
  });
53
+ const isSudoCommand = (args) => args.sudo || args.command === "sudo";
54
+ exports.isSudoCommand = isSudoCommand;
53
55
  const provisionRequest = (authn, args, destination) => __awaiter(void 0, void 0, void 0, function* () {
54
56
  yield validateSshInstall(authn, args);
55
57
  const { publicKey, privateKey } = yield (0, keys_1.createKeyPair)();
56
- const response = yield (0, request_1.request)(Object.assign(Object.assign({}, (0, lodash_1.pick)(args, "$0", "_")), { arguments: [
58
+ const response = yield (0, request_1.request)("request")(Object.assign(Object.assign({}, (0, lodash_1.pick)(args, "$0", "_")), { arguments: [
57
59
  "ssh",
58
60
  "session",
59
61
  destination,
60
62
  "--public-key",
61
63
  publicKey,
62
64
  ...(args.provider ? ["--provider", args.provider] : []),
63
- ...(args.sudo || args.command === "sudo" ? ["--sudo"] : []),
65
+ ...((0, exports.isSudoCommand)(args) ? ["--sudo"] : []),
64
66
  ...(args.reason ? ["--reason", args.reason] : []),
65
67
  ...(args.account ? ["--account", args.account] : []),
66
68
  ], wait: true }), authn, { message: "approval-required" });
@@ -81,5 +83,5 @@ const provisionRequest = (authn, args, destination) => __awaiter(void 0, void 0,
81
83
  return { request: cliRequest, publicKey, privateKey };
82
84
  });
83
85
  exports.provisionRequest = provisionRequest;
84
- const requestToSsh = (request) => SSH_PROVIDERS[request.permission.spec.type].requestToSsh(request);
86
+ const requestToSsh = (request) => exports.SSH_PROVIDERS[request.permission.spec.type].requestToSsh(request);
85
87
  exports.requestToSsh = requestToSsh;
@@ -9,5 +9,5 @@ This file is part of @p0security/cli
9
9
  You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
10
10
  **/
11
11
  import { SshProvider } from "../../types/ssh";
12
- import { AwsSshPermissionSpec, AwsSshRequest } from "./types";
13
- export declare const awsSshProvider: SshProvider<AwsSshPermissionSpec, undefined, AwsSshRequest>;
12
+ import { AwsCredentials, AwsSshPermissionSpec, AwsSshRequest } from "./types";
13
+ export declare const awsSshProvider: SshProvider<AwsSshPermissionSpec, undefined, AwsSshRequest, AwsCredentials>;
@@ -10,6 +10,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.awsSshProvider = void 0;
13
+ const util_1 = require("../../util");
14
+ const aws_1 = require("../okta/aws");
15
+ const config_1 = require("./config");
16
+ const idc_1 = require("./idc");
17
+ const install_1 = require("./ssm/install");
18
+ /** Maximum number of attempts to start an SSH session
19
+ *
20
+ * Each attempt consumes ~ 1 s.
21
+ */
22
+ const MAX_SSH_RETRIES = 30;
23
+ /** The name of the SessionManager port forwarding document. This document is managed by AWS. */
24
+ const START_SSH_SESSION_DOCUMENT_NAME = "AWS-StartSSHSession";
13
25
  exports.awsSshProvider = {
14
26
  requestToSsh: (request) => {
15
27
  const { permission, generated } = request;
@@ -21,4 +33,46 @@ exports.awsSshProvider = {
21
33
  ? Object.assign(Object.assign({}, common), { role: name, type: "aws", access: "role" }) : Object.assign(Object.assign({}, common), { idc, permissionSet: name, type: "aws", access: "idc" });
22
34
  },
23
35
  toCliRequest: (request) => __awaiter(void 0, void 0, void 0, function* () { return (Object.assign(Object.assign({}, request), { cliLocalData: undefined })); }),
36
+ cloudProviderLogin: (authn, request) => __awaiter(void 0, void 0, void 0, function* () {
37
+ var _a, _b, _c, _d;
38
+ if (!(yield (0, install_1.ensureSsmInstall)())) {
39
+ throw "Please try again after installing the required AWS utilities";
40
+ }
41
+ const { config } = yield (0, config_1.getAwsConfig)(authn, request.accountId);
42
+ if (!((_a = config.login) === null || _a === void 0 ? void 0 : _a.type) || ((_b = config.login) === null || _b === void 0 ? void 0 : _b.type) === "iam") {
43
+ throw "This account is not configured for SSH access via the P0 CLI";
44
+ }
45
+ return ((_c = config.login) === null || _c === void 0 ? void 0 : _c.type) === "idc"
46
+ ? yield (0, idc_1.assumeRoleWithIdc)(request)
47
+ : ((_d = config.login) === null || _d === void 0 ? void 0 : _d.type) === "federated"
48
+ ? yield (0, aws_1.assumeRoleWithOktaSaml)(authn, request)
49
+ : (0, util_1.throwAssertNever)(config.login);
50
+ }),
51
+ proxyCommand: (request) => {
52
+ return [
53
+ "aws",
54
+ "ssm",
55
+ "start-session",
56
+ "--region",
57
+ request.region,
58
+ "--target",
59
+ "%h",
60
+ "--document-name",
61
+ START_SSH_SESSION_DOCUMENT_NAME,
62
+ "--parameters",
63
+ '"portNumber=%p"',
64
+ ];
65
+ },
66
+ reproCommands: (request) => {
67
+ // TODO: Add manual commands for IDC login
68
+ if (request.access !== "idc") {
69
+ return [
70
+ `eval $(p0 aws role assume ${request.role} --account ${request.accountId})`,
71
+ ];
72
+ }
73
+ return undefined;
74
+ },
75
+ preTestAccessPropagationArgs: () => undefined,
76
+ maxRetries: MAX_SSH_RETRIES,
77
+ friendlyName: "AWS",
24
78
  };
@@ -1,13 +1,3 @@
1
- /** Copyright © 2024-present P0 Security
2
-
3
- This file is part of @p0security/cli
4
-
5
- @p0security/cli is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License.
6
-
7
- @p0security/cli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
-
9
- You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
10
- **/
11
1
  import { SshProvider } from "../../types/ssh";
12
2
  import { GcpSshPermissionSpec, GcpSshRequest } from "./types";
13
3
  export declare const gcpSshProvider: SshProvider<GcpSshPermissionSpec, {
@@ -10,7 +10,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.gcpSshProvider = void 0;
13
+ /** Copyright © 2024-present P0 Security
14
+
15
+ This file is part of @p0security/cli
16
+
17
+ @p0security/cli is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License.
18
+
19
+ @p0security/cli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
22
+ **/
23
+ const ssh_1 = require("../../commands/shared/ssh");
13
24
  const ssh_key_1 = require("./ssh-key");
25
+ /** Maximum number of attempts to start an SSH session
26
+ *
27
+ * The length of each attempt varies based on the type of error from a few seconds to < 1s
28
+ */
29
+ const MAX_SSH_RETRIES = 120;
14
30
  exports.gcpSshProvider = {
15
31
  requestToSsh: (request) => {
16
32
  return {
@@ -26,4 +42,33 @@ exports.gcpSshProvider = {
26
42
  linuxUserName: yield (0, ssh_key_1.importSshKey)(request.permission.spec.publicKey, options),
27
43
  } }));
28
44
  }),
45
+ cloudProviderLogin: () => __awaiter(void 0, void 0, void 0, function* () { return undefined; }),
46
+ proxyCommand: (request) => {
47
+ return [
48
+ "gcloud",
49
+ "compute",
50
+ "start-iap-tunnel",
51
+ request.id,
52
+ "%p",
53
+ // --listen-on-stdin flag is required for interactive SSH session.
54
+ // It is undocumented on page https://cloud.google.com/sdk/gcloud/reference/compute/start-iap-tunnel
55
+ // but mention on page https://cloud.google.com/iap/docs/tcp-by-host
56
+ // and also found in `gcloud ssh --dry-run` output
57
+ "--listen-on-stdin",
58
+ `--zone=${request.zone}`,
59
+ `--project=${request.projectId}`,
60
+ ];
61
+ },
62
+ reproCommands: () => undefined,
63
+ preTestAccessPropagationArgs: (cmdArgs) => {
64
+ if ((0, ssh_1.isSudoCommand)(cmdArgs)) {
65
+ return Object.assign(Object.assign({}, cmdArgs), {
66
+ // `sudo -v` prints `Sorry, user <user> may not run sudo on <hostname>.` to stderr when user is not a sudoer.
67
+ // It prints nothing to stdout when user is a sudoer - which is important because we don't want any output from the pre-test.
68
+ command: "sudo", arguments: ["-v"] });
69
+ }
70
+ return undefined;
71
+ },
72
+ maxRetries: MAX_SSH_RETRIES,
73
+ friendlyName: "Google Cloud",
29
74
  };
@@ -8,7 +8,7 @@ This file is part of @p0security/cli
8
8
 
9
9
  You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
10
10
  **/
11
- import { ScpCommandArgs, SshCommandArgs } from "../../commands/shared/ssh";
11
+ import { CommandArgs } from "../../commands/shared/ssh";
12
12
  import { Authn } from "../../types/identity";
13
13
  import { SshRequest } from "../../types/ssh";
14
- export declare const sshOrScp: (authn: Authn, data: SshRequest, cmdArgs: ScpCommandArgs | SshCommandArgs, privateKey: string) => Promise<number | null>;
14
+ export declare const sshOrScp: (authn: Authn, request: SshRequest, cmdArgs: CommandArgs, privateKey: string) => Promise<number | null>;
@@ -10,13 +10,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.sshOrScp = void 0;
13
+ /** Copyright © 2024-present P0 Security
14
+
15
+ This file is part of @p0security/cli
16
+
17
+ @p0security/cli is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License.
18
+
19
+ @p0security/cli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
22
+ **/
23
+ const ssh_1 = require("../../commands/shared/ssh");
13
24
  const keys_1 = require("../../common/keys");
14
25
  const stdio_1 = require("../../drivers/stdio");
15
- const util_1 = require("../../util");
16
- const config_1 = require("../aws/config");
17
- const idc_1 = require("../aws/idc");
18
- const install_1 = require("../aws/ssm/install");
19
- const aws_1 = require("../okta/aws");
20
26
  const ssh_agent_1 = require("../ssh-agent");
21
27
  const node_child_process_1 = require("node:child_process");
22
28
  /** Matches the error message that AWS SSM print1 when access is not propagated */
@@ -35,18 +41,11 @@ const UNAUTHORIZED_TUNNEL_USER_MESSAGE = /Error while connecting \[4033: 'not au
35
41
  const UNAUTHORIZED_INSTANCES_GET_MESSAGE = /Required 'compute\.instances\.get' permission/;
36
42
  const DESTINATION_READ_ERROR = /Error while connecting \[4010: 'destination read failed'\]/;
37
43
  const GOOGLE_LOGIN_MESSAGE = /You do not currently have an active account selected/;
44
+ const SUDO_MESSAGE = /Sorry, user .+ may not run sudo on .+/; // The output of `sudo -v` when the user is not allowed to run sudo
38
45
  /** Maximum amount of time after SSH subprocess starts to check for {@link UNPROVISIONED_ACCESS_MESSAGES}
39
46
  * in the process's stderr
40
47
  */
41
48
  const DEFAULT_VALIDATION_WINDOW_MS = 5e3;
42
- /** Maximum number of attempts to start an SSH session
43
- *
44
- * Note that each attempt consumes ~ 1 s.
45
- */
46
- const DEFAULT_MAX_SSH_RETRIES = 30;
47
- const GCP_MAX_SSH_RETRIES = 120; // GCP requires more time to propagate access
48
- /** The name of the SessionManager port forwarding document. This document is managed by AWS. */
49
- const START_SSH_SESSION_DOCUMENT_NAME = "AWS-StartSSHSession";
50
49
  /**
51
50
  * AWS
52
51
  * There are 2 cases of unprovisioned access in AWS
@@ -57,7 +56,7 @@ const START_SSH_SESSION_DOCUMENT_NAME = "AWS-StartSSHSession";
57
56
  * 2: results in CONNECTION_CLOSED_MESSAGE
58
57
  *
59
58
  * Google Cloud
60
- * There are 5 cases of unprovisioned access in Google Cloud.
59
+ * There are 7 cases of unprovisioned access in Google Cloud.
61
60
  * These are all potentially subject to propagation delays.
62
61
  * 1. The linux user name is not present in the user's Google Workspace profile `posixAccounts` attribute
63
62
  * 2. The public key is not present in the user's Google Workspace profile `sshPublicKeys` attribute
@@ -66,17 +65,20 @@ const START_SSH_SESSION_DOCUMENT_NAME = "AWS-StartSSHSession";
66
65
  * 5. The user doesn't have osLogin or osAdminLogin role to the instance
67
66
  * 5.a. compute.instances.get permission is missing
68
67
  * 5.b. compute.instances.osLogin permission is missing
69
- * 6: Rare occurrence, the exact conditions so far undetermined (together with CONNECTION_CLOSED_MESSAGE)
68
+ * 6. compute.instances.osAdminLogin is not provisioned but compute.instances.osLogin is - happens when a user upgrades existing access to sudo
69
+ * 7: Rare occurrence, the exact conditions so far undetermined (together with CONNECTION_CLOSED_MESSAGE)
70
70
  *
71
71
  * 1, 2, 3 (yes!), 5b: result in PUBLIC_KEY_DENIED_MESSAGE
72
72
  * 4: results in UNAUTHORIZED_TUNNEL_USER_MESSAGE and also CONNECTION_CLOSED_MESSAGE
73
73
  * 5a: results in UNAUTHORIZED_INSTANCES_GET_MESSAGE
74
- * 6: results in DESTINATION_READ_ERROR and also CONNECTION_CLOSED_MESSAGE
74
+ * 6: results in SUDO_MESSAGE
75
+ * 7: results in DESTINATION_READ_ERROR and also CONNECTION_CLOSED_MESSAGE
75
76
  */
76
77
  const UNPROVISIONED_ACCESS_MESSAGES = [
77
78
  { pattern: UNAUTHORIZED_START_SESSION_MESSAGE },
78
79
  { pattern: CONNECTION_CLOSED_MESSAGE },
79
80
  { pattern: PUBLIC_KEY_DENIED_MESSAGE },
81
+ { pattern: SUDO_MESSAGE },
80
82
  { pattern: UNAUTHORIZED_TUNNEL_USER_MESSAGE },
81
83
  { pattern: UNAUTHORIZED_INSTANCES_GET_MESSAGE, validationWindowMs: 30e3 },
82
84
  { pattern: DESTINATION_READ_ERROR },
@@ -127,11 +129,6 @@ const spawnChildProcess = (credential, command, args, stdio) => (0, node_child_p
127
129
  stdio,
128
130
  shell: false,
129
131
  });
130
- const friendlyProvider = (provider) => provider === "aws"
131
- ? "AWS"
132
- : provider === "gcloud"
133
- ? "Google Cloud"
134
- : (0, util_1.throwAssertNever)(provider);
135
132
  /** Starts an SSM session in the terminal by spawning `aws ssm` as a subprocess
136
133
  *
137
134
  * Requires `aws ssm` to be installed on the client machine.
@@ -139,6 +136,7 @@ const friendlyProvider = (provider) => provider === "aws"
139
136
  function spawnSshNode(options) {
140
137
  return __awaiter(this, void 0, void 0, function* () {
141
138
  return new Promise((resolve, reject) => {
139
+ const provider = ssh_1.SSH_PROVIDERS[options.provider];
142
140
  const child = spawnChildProcess(options.credential, options.command, options.args, options.stdio);
143
141
  // TODO ENG-2284 support login with Google Cloud: currently return a boolean to indicate if the exception was a Google login error.
144
142
  const { isAccessPropagated, isGoogleLoginException } = accessPropagationGuard(child, options.debug);
@@ -153,7 +151,7 @@ function spawnSshNode(options) {
153
151
  (0, stdio_1.print2)(`Waiting for access to propagate. Retrying SSH session... (remaining attempts: ${attemptsRemaining})`);
154
152
  }
155
153
  if (attemptsRemaining <= 0) {
156
- reject(`Access did not propagate through ${friendlyProvider(options.provider)} before max retry attempts were exceeded. Please contact support@p0.dev for assistance.`);
154
+ reject(`Access did not propagate through ${provider.friendlyName} before max retry attempts were exceeded. Please contact support@p0.dev for assistance.`);
157
155
  return;
158
156
  }
159
157
  spawnSshNode(Object.assign(Object.assign({}, options), { attemptsRemaining: attemptsRemaining - 1 }))
@@ -166,50 +164,16 @@ function spawnSshNode(options) {
166
164
  return;
167
165
  }
168
166
  (_a = options.abortController) === null || _a === void 0 ? void 0 : _a.abort(code);
169
- (0, stdio_1.print2)(`SSH session terminated`);
167
+ if (!options.isAccessPropagationPreTest)
168
+ (0, stdio_1.print2)(`SSH session terminated`);
170
169
  resolve(code);
171
170
  });
172
171
  });
173
172
  });
174
173
  }
175
- const createProxyCommands = (data, args, debug) => {
176
- let proxyCommand;
177
- if (data.type === "aws") {
178
- proxyCommand = [
179
- "aws",
180
- "ssm",
181
- "start-session",
182
- "--region",
183
- data.region,
184
- "--target",
185
- "%h",
186
- "--document-name",
187
- START_SSH_SESSION_DOCUMENT_NAME,
188
- "--parameters",
189
- '"portNumber=%p"',
190
- ];
191
- }
192
- else if (data.type === "gcloud") {
193
- proxyCommand = [
194
- "gcloud",
195
- "compute",
196
- "start-iap-tunnel",
197
- data.id,
198
- "%p",
199
- // --listen-on-stdin flag is required for interactive SSH session.
200
- // It is undocumented on page https://cloud.google.com/sdk/gcloud/reference/compute/start-iap-tunnel
201
- // but mention on page https://cloud.google.com/iap/docs/tcp-by-host
202
- // and also found in `gcloud ssh --dry-run` output
203
- "--listen-on-stdin",
204
- `--zone=${data.zone}`,
205
- `--project=${data.projectId}`,
206
- ];
207
- }
208
- else {
209
- throw (0, util_1.assertNever)(data);
210
- }
174
+ const createCommand = (data, args, proxyCommand) => {
211
175
  const commonArgs = [
212
- ...(debug ? ["-v"] : []),
176
+ ...(args.debug ? ["-v"] : []),
213
177
  "-o",
214
178
  `ProxyCommand=${proxyCommand.join(" ")}`,
215
179
  ];
@@ -257,45 +221,53 @@ const transformForShell = (args) => {
257
221
  return arg;
258
222
  });
259
223
  };
260
- const awsLogin = (authn, data) => __awaiter(void 0, void 0, void 0, function* () {
261
- var _a, _b, _c, _d;
262
- if (!(yield (0, install_1.ensureSsmInstall)())) {
263
- throw "Please try again after installing the required AWS utilities";
264
- }
265
- const { config } = yield (0, config_1.getAwsConfig)(authn, data.accountId);
266
- if (!((_a = config.login) === null || _a === void 0 ? void 0 : _a.type) || ((_b = config.login) === null || _b === void 0 ? void 0 : _b.type) === "iam") {
267
- throw "This account is not configured for SSH access via the P0 CLI";
224
+ /** Construct another command to use for testing access propagation prior to actually logging in the user to the ssh session */
225
+ const preTestAccessPropagationIfNeeded = (sshProvider, request, cmdArgs, proxyCommand, credential) => __awaiter(void 0, void 0, void 0, function* () {
226
+ const testCmdArgs = sshProvider.preTestAccessPropagationArgs(cmdArgs);
227
+ // Pre-testing comes at a performance cost because we have to execute another ssh subprocess after
228
+ // a successful test. Only do when absolutely necessary.
229
+ if (testCmdArgs) {
230
+ const { command, args } = createCommand(request, testCmdArgs, proxyCommand);
231
+ // Assumes that this is a non-interactive ssh command that exits automatically
232
+ return spawnSshNode({
233
+ credential,
234
+ abortController: new AbortController(),
235
+ command,
236
+ args,
237
+ stdio: ["inherit", "inherit", "pipe"],
238
+ debug: cmdArgs.debug,
239
+ provider: request.type,
240
+ attemptsRemaining: sshProvider.maxRetries,
241
+ isAccessPropagationPreTest: true,
242
+ });
268
243
  }
269
- return ((_c = config.login) === null || _c === void 0 ? void 0 : _c.type) === "idc"
270
- ? yield (0, idc_1.assumeRoleWithIdc)(data)
271
- : ((_d = config.login) === null || _d === void 0 ? void 0 : _d.type) === "federated"
272
- ? yield (0, aws_1.assumeRoleWithOktaSaml)(authn, data)
273
- : (0, util_1.throwAssertNever)(config.login);
244
+ return null;
274
245
  });
275
- const sshOrScp = (authn, data, cmdArgs, privateKey) => __awaiter(void 0, void 0, void 0, function* () {
246
+ const sshOrScp = (authn, request, cmdArgs, privateKey) => __awaiter(void 0, void 0, void 0, function* () {
276
247
  if (!privateKey) {
277
248
  throw "Failed to load a private key for this request. Please contact support@p0.dev for assistance.";
278
249
  }
279
- // TODO ENG-2284 support login with Google Cloud
280
- const credential = data.type === "aws" ? yield awsLogin(authn, data) : undefined;
250
+ const sshProvider = ssh_1.SSH_PROVIDERS[request.type];
251
+ const credential = yield sshProvider.cloudProviderLogin(authn, request);
252
+ const proxyCommand = sshProvider.proxyCommand(request);
281
253
  return (0, ssh_agent_1.withSshAgent)(cmdArgs, () => __awaiter(void 0, void 0, void 0, function* () {
282
- const { command, args } = createProxyCommands(data, cmdArgs, cmdArgs.debug);
254
+ const { command, args } = createCommand(request, cmdArgs, proxyCommand);
283
255
  if (cmdArgs.debug) {
284
- const reproCommands = [
285
- `eval $(ssh-agent)`,
286
- `ssh-add "${keys_1.PRIVATE_KEY_PATH}"`,
287
- // TODO ENG-2284 support login with Google Cloud
288
- // TODO: Modify commands to add the ability to get permission set commands
289
- ...(data.type === "aws" && data.access !== "idc"
290
- ? [
291
- `eval $(p0 aws role assume ${data.role} --account ${data.accountId})`,
292
- ]
293
- : []),
294
- `${command} ${transformForShell(args).join(" ")}`,
295
- ];
296
- (0, stdio_1.print2)(`Execute the following commands to create a similar SSH/SCP session:\n*** COMMANDS BEGIN ***\n${reproCommands.join("\n")}\n*** COMMANDS END ***"\n`);
256
+ const reproCommands = sshProvider.reproCommands(request);
257
+ if (reproCommands) {
258
+ const repro = [
259
+ `eval $(ssh-agent)`,
260
+ `ssh-add "${keys_1.PRIVATE_KEY_PATH}"`,
261
+ ...reproCommands,
262
+ `${command} ${transformForShell(args).join(" ")}`,
263
+ ].join("\n");
264
+ (0, stdio_1.print2)(`Execute the following commands to create a similar SSH/SCP session:\n*** COMMANDS BEGIN ***\n${repro}\n*** COMMANDS END ***"\n`);
265
+ }
266
+ }
267
+ const exitCode = yield preTestAccessPropagationIfNeeded(sshProvider, request, cmdArgs, proxyCommand, credential);
268
+ if (exitCode && exitCode !== 0) {
269
+ return exitCode; // Only exit if there was an error when pre-testing
297
270
  }
298
- const maxRetries = data.type === "gcloud" ? GCP_MAX_SSH_RETRIES : DEFAULT_MAX_SSH_RETRIES;
299
271
  return spawnSshNode({
300
272
  credential,
301
273
  abortController: new AbortController(),
@@ -303,8 +275,8 @@ const sshOrScp = (authn, data, cmdArgs, privateKey) => __awaiter(void 0, void 0,
303
275
  args,
304
276
  stdio: ["inherit", "inherit", "pipe"],
305
277
  debug: cmdArgs.debug,
306
- provider: data.type,
307
- attemptsRemaining: maxRetries,
278
+ provider: request.type,
279
+ attemptsRemaining: sshProvider.maxRetries,
308
280
  });
309
281
  }));
310
282
  });
@@ -8,8 +8,10 @@ This file is part of @p0security/cli
8
8
 
9
9
  You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
10
10
  **/
11
+ import { CommandArgs } from "../commands/shared/ssh";
11
12
  import { AwsSsh, AwsSshPermissionSpec, AwsSshRequest } from "../plugins/aws/types";
12
13
  import { GcpSsh, GcpSshPermissionSpec, GcpSshRequest } from "../plugins/google/types";
14
+ import { Authn } from "./identity";
13
15
  import { Request } from "./request";
14
16
  export declare type CliSshRequest = AwsSsh | GcpSsh;
15
17
  export declare type PluginSshRequest = AwsSshPermissionSpec | GcpSshPermissionSpec;
@@ -18,10 +20,28 @@ export declare type CliPermissionSpec<P extends PluginSshRequest, C extends obje
18
20
  };
19
21
  export declare const SupportedSshProviders: readonly ["aws", "gcloud"];
20
22
  export declare type SupportedSshProvider = (typeof SupportedSshProviders)[number];
21
- export declare type SshProvider<PR extends PluginSshRequest = PluginSshRequest, O extends object | undefined = undefined, SR extends SshRequest = SshRequest> = {
23
+ export declare type SshProvider<PR extends PluginSshRequest = PluginSshRequest, O extends object | undefined = undefined, SR extends SshRequest = SshRequest, C extends object | undefined = undefined> = {
22
24
  requestToSsh: (request: CliPermissionSpec<PR, O>) => SR;
25
+ /** Converts a backend request to a CLI request */
23
26
  toCliRequest: (request: Request<PR>, options?: {
24
27
  debug?: boolean;
25
28
  }) => Promise<Request<CliSshRequest>>;
29
+ /** Logs in the user to the cloud provider */
30
+ cloudProviderLogin: (authn: Authn, request: SR) => Promise<C>;
31
+ /** Returns the command and its arguments that are going to be injected as the ssh ProxyCommand option */
32
+ proxyCommand: (request: SR) => string[];
33
+ /** Each element in the returned array is a command that can be run to reproduce the
34
+ * steps of logging in the user to the ssh session. */
35
+ reproCommands: (request: SR) => string[] | undefined;
36
+ /** Arguments for a pre-test command to verify access propagation prior
37
+ * to actually logging in the user to the ssh session.
38
+ * This must return arguments for a non-interactive command - meaning the `command`
39
+ * and potentially the `args` props must be specified in the returned scp/ssh command.
40
+ * If the return value is undefined then no pre-testing is done prior to executing
41
+ * the actual ssh/scp command.
42
+ */
43
+ preTestAccessPropagationArgs: (cmdArgs: CommandArgs) => CommandArgs | undefined;
44
+ maxRetries: number;
45
+ friendlyName: string;
26
46
  };
27
47
  export declare type SshRequest = AwsSshRequest | GcpSshRequest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p0security/cli",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Execute infra CLI commands with P0 grants",
5
5
  "main": "index.ts",
6
6
  "repository": {
@@ -25,7 +25,7 @@
25
25
  "express": "^4.18.2",
26
26
  "firebase": "^10.7.2",
27
27
  "inquirer": "^9.2.15",
28
- "jsdom": "^24.0.0",
28
+ "jsdom": "^24.1.1",
29
29
  "lodash": "^4.17.21",
30
30
  "node-forge": "^1.3.1",
31
31
  "open": "^8.4.0",
@@ -67,5 +67,6 @@
67
67
  "lint": "yarn prettier --check . && yarn run eslint --max-warnings 0 .",
68
68
  "p0": "node --no-deprecation ./p0",
69
69
  "prepublishOnly": "npm run clean && npm run build"
70
- }
70
+ },
71
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
71
72
  }