@p0security/cli 0.3.0
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.
- package/CONTRIBUTING.md +23 -0
- package/LICENSE.md +675 -0
- package/README.md +201 -0
- package/dist/commands/__tests__/login.test.d.ts +1 -0
- package/dist/commands/__tests__/login.test.js +75 -0
- package/dist/commands/__tests__/ls.test.d.ts +1 -0
- package/dist/commands/__tests__/ls.test.js +84 -0
- package/dist/commands/__tests__/request.test.d.ts +1 -0
- package/dist/commands/__tests__/request.test.js +94 -0
- package/dist/commands/__tests__/ssh.test.d.ts +1 -0
- package/dist/commands/__tests__/ssh.test.js +107 -0
- package/dist/commands/aws/__tests__/__input__/saml-response.d.ts +11 -0
- package/dist/commands/aws/__tests__/__input__/saml-response.js +18 -0
- package/dist/commands/aws/__tests__/__input__/sts-response.d.ts +11 -0
- package/dist/commands/aws/__tests__/__input__/sts-response.js +37 -0
- package/dist/commands/aws/__tests__/role.test.d.ts +1 -0
- package/dist/commands/aws/__tests__/role.test.js +98 -0
- package/dist/commands/aws/index.d.ts +4 -0
- package/dist/commands/aws/index.js +26 -0
- package/dist/commands/aws/role.d.ts +27 -0
- package/dist/commands/aws/role.js +123 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +49 -0
- package/dist/commands/login.d.ts +14 -0
- package/dist/commands/login.js +93 -0
- package/dist/commands/ls.d.ts +4 -0
- package/dist/commands/ls.js +78 -0
- package/dist/commands/request.d.ts +12 -0
- package/dist/commands/request.js +116 -0
- package/dist/commands/ssh.d.ts +11 -0
- package/dist/commands/ssh.js +154 -0
- package/dist/common/auth/oidc.d.ts +4 -0
- package/dist/common/auth/oidc.js +18 -0
- package/dist/common/auth/server.d.ts +15 -0
- package/dist/common/auth/server.js +73 -0
- package/dist/common/fetch.d.ts +16 -0
- package/dist/common/fetch.js +39 -0
- package/dist/common/mime.d.ts +14 -0
- package/dist/common/mime.js +17 -0
- package/dist/common/xml.d.ts +21 -0
- package/dist/common/xml.js +52 -0
- package/dist/drivers/__mocks__/auth.d.ts +30 -0
- package/dist/drivers/__mocks__/auth.js +46 -0
- package/dist/drivers/api.d.ts +3 -0
- package/dist/drivers/api.js +69 -0
- package/dist/drivers/auth.d.ts +11 -0
- package/dist/drivers/auth.js +122 -0
- package/dist/drivers/env.d.ts +15 -0
- package/dist/drivers/env.js +38 -0
- package/dist/drivers/firestore.d.ts +10 -0
- package/dist/drivers/firestore.js +53 -0
- package/dist/drivers/stdio.d.ts +25 -0
- package/dist/drivers/stdio.js +44 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +23 -0
- package/dist/middlewares/version.d.ts +8 -0
- package/dist/middlewares/version.js +77 -0
- package/dist/plugins/__mocks__/login.d.ts +14 -0
- package/dist/plugins/__mocks__/login.js +24 -0
- package/dist/plugins/aws/__mocks__/assumeRole.d.ts +12 -0
- package/dist/plugins/aws/__mocks__/assumeRole.js +20 -0
- package/dist/plugins/aws/api.d.ts +12 -0
- package/dist/plugins/aws/api.js +16 -0
- package/dist/plugins/aws/assumeRole.d.ts +14 -0
- package/dist/plugins/aws/assumeRole.js +55 -0
- package/dist/plugins/aws/config.d.ts +5 -0
- package/dist/plugins/aws/config.js +38 -0
- package/dist/plugins/aws/ssm/index.d.ts +18 -0
- package/dist/plugins/aws/ssm/index.js +274 -0
- package/dist/plugins/aws/ssm/install.d.ts +7 -0
- package/dist/plugins/aws/ssm/install.js +133 -0
- package/dist/plugins/aws/types.d.ts +54 -0
- package/dist/plugins/aws/types.js +2 -0
- package/dist/plugins/google/login.d.ts +2 -0
- package/dist/plugins/google/login.js +76 -0
- package/dist/plugins/login.d.ts +13 -0
- package/dist/plugins/login.js +19 -0
- package/dist/plugins/okta/aws.d.ts +5 -0
- package/dist/plugins/okta/aws.js +42 -0
- package/dist/plugins/okta/login.d.ts +8 -0
- package/dist/plugins/okta/login.js +165 -0
- package/dist/plugins/ssh/types.d.ts +22 -0
- package/dist/plugins/ssh/types.js +2 -0
- package/dist/public/favicon.ico +0 -0
- package/dist/public/redirect-landing.html +40 -0
- package/dist/testing/firestore.d.ts +2 -0
- package/dist/testing/firestore.js +16 -0
- package/dist/testing/yargs.d.ts +12 -0
- package/dist/testing/yargs.js +23 -0
- package/dist/types/identity.d.ts +23 -0
- package/dist/types/identity.js +2 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.js +15 -0
- package/dist/types/oidc.d.ts +41 -0
- package/dist/types/oidc.js +2 -0
- package/dist/types/org.d.ts +20 -0
- package/dist/types/org.js +2 -0
- package/dist/types/request.d.ts +35 -0
- package/dist/types/request.js +20 -0
- package/dist/util.d.ts +42 -0
- package/dist/util.js +87 -0
- package/p0 +16 -0
- package/package.json +70 -0
|
@@ -0,0 +1,116 @@
|
|
|
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.requestCommand = 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
|
+
const requestCommand = (yargs) => yargs.command("request [arguments..]", "Manually request permissions on a resource", requestArgs, (0, firestore_1.guard)(exports.request));
|
|
57
|
+
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,11 @@
|
|
|
1
|
+
import yargs from "yargs";
|
|
2
|
+
export declare type SshCommandArgs = {
|
|
3
|
+
destination: string;
|
|
4
|
+
command?: string;
|
|
5
|
+
L?: string;
|
|
6
|
+
N?: boolean;
|
|
7
|
+
arguments: string[];
|
|
8
|
+
sudo?: boolean;
|
|
9
|
+
reason?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const sshCommand: (yargs: yargs.Argv<{}>) => yargs.Argv<SshCommandArgs>;
|
|
@@ -0,0 +1,154 @@
|
|
|
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.sshCommand = 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 auth_1 = require("../drivers/auth");
|
|
24
|
+
const firestore_1 = require("../drivers/firestore");
|
|
25
|
+
const stdio_1 = require("../drivers/stdio");
|
|
26
|
+
const ssm_1 = require("../plugins/aws/ssm");
|
|
27
|
+
const request_1 = require("../types/request");
|
|
28
|
+
const request_2 = require("./request");
|
|
29
|
+
const firestore_2 = require("firebase/firestore");
|
|
30
|
+
const lodash_1 = require("lodash");
|
|
31
|
+
// Matches strings with the pattern "digits:digits" (e.g. 1234:5678)
|
|
32
|
+
const LOCAL_PORT_FORWARD_PATTERN = /^\d+:\d+$/;
|
|
33
|
+
/** Maximum amount of time to wait after access is approved to wait for access
|
|
34
|
+
* to be configured
|
|
35
|
+
*/
|
|
36
|
+
const GRANT_TIMEOUT_MILLIS = 60e3;
|
|
37
|
+
const sshCommand = (yargs) => yargs.command("ssh <destination> [command [arguments..]]", "SSH into a virtual machine", (yargs) => yargs
|
|
38
|
+
.positional("destination", {
|
|
39
|
+
type: "string",
|
|
40
|
+
demandOption: true,
|
|
41
|
+
})
|
|
42
|
+
.option("sudo", {
|
|
43
|
+
type: "boolean",
|
|
44
|
+
describe: "Add user to sudoers file",
|
|
45
|
+
})
|
|
46
|
+
.positional("command", {
|
|
47
|
+
type: "string",
|
|
48
|
+
describe: "Pass command to the shell",
|
|
49
|
+
})
|
|
50
|
+
.positional("arguments", {
|
|
51
|
+
describe: "Command arguments",
|
|
52
|
+
array: true,
|
|
53
|
+
string: true,
|
|
54
|
+
default: [],
|
|
55
|
+
})
|
|
56
|
+
.check((argv) => {
|
|
57
|
+
if (argv.L == null)
|
|
58
|
+
return true;
|
|
59
|
+
return (argv.L.match(LOCAL_PORT_FORWARD_PATTERN) ||
|
|
60
|
+
"Local port forward should be in the format `local_port:remote_port`");
|
|
61
|
+
})
|
|
62
|
+
.option("L", {
|
|
63
|
+
type: "string",
|
|
64
|
+
describe:
|
|
65
|
+
// the order of the sockets in the address matches the ssh man page
|
|
66
|
+
"Forward a local port to the remote host; `local_socket:remote_socket`",
|
|
67
|
+
})
|
|
68
|
+
.option("N", {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
describe: "Do not execute a remote command. Useful for forwarding ports.",
|
|
71
|
+
})
|
|
72
|
+
// Match `p0 request --reason`
|
|
73
|
+
.option("reason", {
|
|
74
|
+
describe: "Reason access is needed",
|
|
75
|
+
type: "string",
|
|
76
|
+
}), (0, firestore_1.guard)(ssh));
|
|
77
|
+
exports.sshCommand = sshCommand;
|
|
78
|
+
const validateSshInstall = (authn) => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
const configDoc = yield (0, firestore_2.getDoc)((0, firestore_1.doc)(`o/${authn.identity.org.tenantId}/integrations/ssh`));
|
|
81
|
+
const items = (_b = (_a = configDoc
|
|
82
|
+
.data()) === null || _a === void 0 ? void 0 : _a.workflows) === null || _b === void 0 ? void 0 : _b.items.filter((i) => i.state === "installed" && i.type === "aws");
|
|
83
|
+
if (!(items === null || items === void 0 ? void 0 : items.length)) {
|
|
84
|
+
throw "This organization is not configured for SSH access via the P0 CLI";
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// TODO: Move this to a shared utility
|
|
88
|
+
/** Waits until P0 grants access for a request */
|
|
89
|
+
const waitForProvisioning = (authn, requestId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
let cancel = undefined;
|
|
91
|
+
const result = yield new Promise((resolve, reject) => {
|
|
92
|
+
let isResolved = false;
|
|
93
|
+
const unsubscribe = (0, firestore_2.onSnapshot)((0, firestore_1.doc)(`o/${authn.identity.org.tenantId}/permission-requests/${requestId}`), (snap) => {
|
|
94
|
+
const data = snap.data();
|
|
95
|
+
if (!data)
|
|
96
|
+
return;
|
|
97
|
+
if (request_1.DONE_STATUSES.includes(data.status)) {
|
|
98
|
+
resolve(data);
|
|
99
|
+
}
|
|
100
|
+
else if (request_1.DENIED_STATUSES.includes(data.status)) {
|
|
101
|
+
reject("Your access request was denied");
|
|
102
|
+
}
|
|
103
|
+
else if (request_1.ERROR_STATUSES.includes(data.status)) {
|
|
104
|
+
reject("Your access request encountered an error (see Slack for details)");
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
isResolved = true;
|
|
110
|
+
unsubscribe();
|
|
111
|
+
});
|
|
112
|
+
// Skip timeout in test; it holds a ref longer than the test lasts
|
|
113
|
+
if (process.env.NODE_ENV === "test")
|
|
114
|
+
return;
|
|
115
|
+
cancel = setTimeout(() => {
|
|
116
|
+
if (!isResolved) {
|
|
117
|
+
unsubscribe();
|
|
118
|
+
reject("Timeout awaiting SSH access grant");
|
|
119
|
+
}
|
|
120
|
+
}, GRANT_TIMEOUT_MILLIS);
|
|
121
|
+
});
|
|
122
|
+
clearTimeout(cancel);
|
|
123
|
+
return result;
|
|
124
|
+
});
|
|
125
|
+
/** Connect to an SSH backend
|
|
126
|
+
*
|
|
127
|
+
* Implicitly gains access to the SSH resource if required.
|
|
128
|
+
*
|
|
129
|
+
* Supported SSH mechanisms:
|
|
130
|
+
* - AWS EC2 via SSM with Okta SAML
|
|
131
|
+
*/
|
|
132
|
+
const ssh = (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
+
// Prefix is required because the backend uses it to determine that this is an AWS request
|
|
134
|
+
const authn = yield (0, auth_1.authenticate)();
|
|
135
|
+
yield validateSshInstall(authn);
|
|
136
|
+
const response = yield (0, request_2.request)(Object.assign(Object.assign({}, (0, lodash_1.pick)(args, "$0", "_")), { arguments: [
|
|
137
|
+
"ssh",
|
|
138
|
+
args.destination,
|
|
139
|
+
"--provider",
|
|
140
|
+
"aws",
|
|
141
|
+
...(args.sudo || args.command === "sudo" ? ["--sudo"] : []),
|
|
142
|
+
...(args.reason ? ["--reason", args.reason] : []),
|
|
143
|
+
], wait: true }), authn, { message: "approval-required" });
|
|
144
|
+
if (!response) {
|
|
145
|
+
(0, stdio_1.print2)("Did not receive access ID from server");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const { id, isPreexisting } = response;
|
|
149
|
+
if (!isPreexisting)
|
|
150
|
+
(0, stdio_1.print2)("Waiting for access to be provisioned");
|
|
151
|
+
const requestData = yield waitForProvisioning(authn, id);
|
|
152
|
+
const requestWithId = Object.assign(Object.assign({}, requestData), { id });
|
|
153
|
+
yield (0, ssm_1.ssm)(authn, requestWithId, args);
|
|
154
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OIDC_HEADERS = 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 mime_1 = require("../mime");
|
|
15
|
+
exports.OIDC_HEADERS = {
|
|
16
|
+
Accept: mime_1.application.JSON,
|
|
17
|
+
"Content-Type": mime_1.application.X_WWW_FORM_URLENCODED,
|
|
18
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
import http from "node:http";
|
|
12
|
+
/** Waits for an OIDC authorization redirect using a locally mounted server */
|
|
13
|
+
export declare const withRedirectServer: <S, T, U>(start: (server: http.Server) => Promise<S>, complete: (value: S, token: T) => Promise<U>, options?: {
|
|
14
|
+
port?: number;
|
|
15
|
+
}) => Promise<U>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Copyright © 2024-present P0 Security
|
|
3
|
+
|
|
4
|
+
This file is part of @p0security/cli
|
|
5
|
+
|
|
6
|
+
@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.
|
|
7
|
+
|
|
8
|
+
@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.
|
|
9
|
+
|
|
10
|
+
You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see <https://www.gnu.org/licenses/>.
|
|
11
|
+
**/
|
|
12
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.withRedirectServer = void 0;
|
|
26
|
+
/** Implements a local auth server, which can receive auth tokens from an OIDC app */
|
|
27
|
+
const util_1 = require("../../util");
|
|
28
|
+
const express_1 = __importDefault(require("express"));
|
|
29
|
+
const node_path_1 = require("node:path");
|
|
30
|
+
const ROOT_PATH = `${(0, node_path_1.dirname)(require.main.filename)}/dist`;
|
|
31
|
+
/** A small amount of time is necessary prior to shutting down the redirect server to
|
|
32
|
+
* properly render the redirect-landing page
|
|
33
|
+
*/
|
|
34
|
+
const SERVER_SHUTDOWN_WAIT_MILLIS = 2e3;
|
|
35
|
+
/** Waits for an OIDC authorization redirect using a locally mounted server */
|
|
36
|
+
const withRedirectServer = (start, complete, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
+
var _a;
|
|
38
|
+
const app = (0, express_1.default)();
|
|
39
|
+
app.use(express_1.default.static(`${ROOT_PATH}/public`));
|
|
40
|
+
let redirectResolve;
|
|
41
|
+
let redirectReject;
|
|
42
|
+
let value;
|
|
43
|
+
const redirectPromise = new Promise((resolve, reject) => {
|
|
44
|
+
redirectResolve = resolve;
|
|
45
|
+
redirectReject = reject;
|
|
46
|
+
});
|
|
47
|
+
const redirectRouter = express_1.default.Router();
|
|
48
|
+
redirectRouter.get("/", (req, res) => {
|
|
49
|
+
const token = req.query;
|
|
50
|
+
complete(value, token)
|
|
51
|
+
.then((result) => {
|
|
52
|
+
res.status(200).sendFile(`${ROOT_PATH}/public/redirect-landing.html`);
|
|
53
|
+
redirectResolve(result);
|
|
54
|
+
})
|
|
55
|
+
.catch((error) => {
|
|
56
|
+
var _a;
|
|
57
|
+
res.status(500).send((_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error);
|
|
58
|
+
redirectReject(error);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
app.use(redirectRouter);
|
|
62
|
+
const server = app.listen((_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : 0);
|
|
63
|
+
try {
|
|
64
|
+
value = yield start(server);
|
|
65
|
+
return yield redirectPromise;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
yield (0, util_1.sleep)(SERVER_SHUTDOWN_WAIT_MILLIS);
|
|
69
|
+
server.closeAllConnections();
|
|
70
|
+
server.unref();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
exports.withRedirectServer = withRedirectServer;
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
/** Converts object data to a URL encoded form */
|
|
12
|
+
export declare const urlEncode: (data: Record<string, string>) => string;
|
|
13
|
+
/** Validates an HTTP response, throwing a friendly
|
|
14
|
+
* error message if invalid
|
|
15
|
+
*/
|
|
16
|
+
export declare const validateResponse: (response: Response) => Promise<Response>;
|
|
@@ -0,0 +1,39 @@
|
|
|
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.validateResponse = exports.urlEncode = 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
|
+
/** Converts object data to a URL encoded form */
|
|
24
|
+
const urlEncode = (data) => Object.entries(data)
|
|
25
|
+
.map((kv) => kv.map(encodeURIComponent).join("="))
|
|
26
|
+
.join("&");
|
|
27
|
+
exports.urlEncode = urlEncode;
|
|
28
|
+
/** Validates an HTTP response, throwing a friendly
|
|
29
|
+
* error message if invalid
|
|
30
|
+
*/
|
|
31
|
+
const validateResponse = (response) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
if (response.ok)
|
|
33
|
+
return response;
|
|
34
|
+
throw new Error(`Error in fetch request to ${response.url.split("?")[0]}:
|
|
35
|
+
${response.status} ${response.statusText}
|
|
36
|
+
|
|
37
|
+
${yield response.text()}`);
|
|
38
|
+
});
|
|
39
|
+
exports.validateResponse = validateResponse;
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
export declare const application: {
|
|
12
|
+
readonly JSON: "application/json";
|
|
13
|
+
readonly X_WWW_FORM_URLENCODED: "application/x-www-form-urlencoded";
|
|
14
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.application = 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
|
+
exports.application = {
|
|
15
|
+
JSON: "application/json",
|
|
16
|
+
X_WWW_FORM_URLENCODED: "application/x-www-form-urlencoded",
|
|
17
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** A janky XML document -> POJS parser
|
|
2
|
+
*
|
|
3
|
+
* The document is treated as an XML element, then transformed
|
|
4
|
+
* recursively via the following rules:
|
|
5
|
+
*
|
|
6
|
+
* If the element has child elements, it is converted to an object
|
|
7
|
+
* with a property for each unique child element name, with
|
|
8
|
+
* the property value equal to:
|
|
9
|
+
* - The child element's transformed value, if there is only
|
|
10
|
+
* one element with the property name
|
|
11
|
+
* - An array of transformed elements, if there are multiple
|
|
12
|
+
* child elements with the property name
|
|
13
|
+
*
|
|
14
|
+
* In addition, when the element has children, it receives an
|
|
15
|
+
* additional `_attributes` property, which is an object of the
|
|
16
|
+
* element's attributes.
|
|
17
|
+
*
|
|
18
|
+
* Otherwise, the element is transformed into the element's XML
|
|
19
|
+
* text.
|
|
20
|
+
*/
|
|
21
|
+
export declare const parseXml: (xml: string) => any;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseXml = 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
|
+
// Avoid XXE and friends with a (best-as-we-know) safe XML parser
|
|
15
|
+
const parse_xml_1 = require("@rgrove/parse-xml");
|
|
16
|
+
const lodash_1 = require("lodash");
|
|
17
|
+
const isXmlElement = (node) => node instanceof parse_xml_1.XmlElement;
|
|
18
|
+
const elementToObject = (el) => {
|
|
19
|
+
if (el.children.find((n) => n instanceof parse_xml_1.XmlElement)) {
|
|
20
|
+
const object = (0, lodash_1.mapValues)((0, lodash_1.groupBy)(el.children.filter(isXmlElement), (el) => el.name), (items) => items.length === 1
|
|
21
|
+
? elementToObject(items[0])
|
|
22
|
+
: items.map(elementToObject));
|
|
23
|
+
Object.assign(object, { _attributes: Object.assign({}, el.attributes) });
|
|
24
|
+
return object;
|
|
25
|
+
}
|
|
26
|
+
return el.text.trim();
|
|
27
|
+
};
|
|
28
|
+
/** A janky XML document -> POJS parser
|
|
29
|
+
*
|
|
30
|
+
* The document is treated as an XML element, then transformed
|
|
31
|
+
* recursively via the following rules:
|
|
32
|
+
*
|
|
33
|
+
* If the element has child elements, it is converted to an object
|
|
34
|
+
* with a property for each unique child element name, with
|
|
35
|
+
* the property value equal to:
|
|
36
|
+
* - The child element's transformed value, if there is only
|
|
37
|
+
* one element with the property name
|
|
38
|
+
* - An array of transformed elements, if there are multiple
|
|
39
|
+
* child elements with the property name
|
|
40
|
+
*
|
|
41
|
+
* In addition, when the element has children, it receives an
|
|
42
|
+
* additional `_attributes` property, which is an object of the
|
|
43
|
+
* element's attributes.
|
|
44
|
+
*
|
|
45
|
+
* Otherwise, the element is transformed into the element's XML
|
|
46
|
+
* text.
|
|
47
|
+
*/
|
|
48
|
+
const parseXml = (xml) => {
|
|
49
|
+
const parsed = (0, parse_xml_1.parseXml)(xml);
|
|
50
|
+
return elementToObject(parsed);
|
|
51
|
+
};
|
|
52
|
+
exports.parseXml = parseXml;
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
export declare const authenticate: () => Promise<{
|
|
12
|
+
identity: {
|
|
13
|
+
credential: {
|
|
14
|
+
access_token: string;
|
|
15
|
+
};
|
|
16
|
+
org: {
|
|
17
|
+
ssoProvider: string;
|
|
18
|
+
providerDomain: string;
|
|
19
|
+
providerType: string;
|
|
20
|
+
slug: string;
|
|
21
|
+
tenantId: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
userCredential: {
|
|
25
|
+
user: {
|
|
26
|
+
tenantId: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
export declare const cached: (_label: string, callback: () => Promise<any>) => Promise<any>;
|
|
@@ -0,0 +1,46 @@
|
|
|
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.cached = exports.authenticate = 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 authenticate = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
+
return ({
|
|
25
|
+
identity: {
|
|
26
|
+
credential: {
|
|
27
|
+
access_token: "test-access-token",
|
|
28
|
+
},
|
|
29
|
+
org: {
|
|
30
|
+
ssoProvider: "oidc-pkce",
|
|
31
|
+
providerDomain: "test.okta.com",
|
|
32
|
+
providerType: "okta",
|
|
33
|
+
slug: "test-org",
|
|
34
|
+
tenantId: "test-tenant",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
userCredential: {
|
|
38
|
+
user: {
|
|
39
|
+
tenantId: "test-tenant",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
exports.authenticate = authenticate;
|
|
45
|
+
const cached = (_label, callback) => __awaiter(void 0, void 0, void 0, function* () { return yield callback(); });
|
|
46
|
+
exports.cached = cached;
|