@automattic/vip 4.0.9 → 4.0.11
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/dist/bin/vip-defensive-mode-configure.js +216 -0
- package/dist/bin/vip-defensive-mode-disable.js +42 -0
- package/dist/bin/vip-defensive-mode-enable.js +45 -0
- package/dist/bin/vip-defensive-mode.js +21 -0
- package/dist/bin/vip.js +6 -1
- package/dist/lib/api/constants.js +7 -0
- package/dist/lib/api/feature-flags.js +17 -4
- package/dist/lib/api/http.js +3 -3
- package/dist/lib/api.js +23 -8
- package/dist/lib/cli/internal-bin-loader.js +4 -0
- package/dist/lib/defensive-mode/api.js +106 -0
- package/dist/lib/defensive-mode/cli-helpers.js +60 -0
- package/dist/lib/dev-environment/dev-environment-configuration-file.js +19 -14
- package/dist/lib/keychain/insecure.js +2 -2
- package/dist/lib/keychain/secure.js +2 -2
- package/dist/lib/keychain.js +20 -15
- package/dist/lib/logout.js +9 -4
- package/dist/lib/rechallenge/client.js +55 -0
- package/dist/lib/rechallenge/errors.js +55 -0
- package/dist/lib/rechallenge/flow.js +145 -0
- package/dist/lib/rechallenge/index.js +27 -0
- package/dist/lib/rechallenge/link.js +167 -0
- package/dist/lib/rechallenge/open-browser.js +21 -0
- package/dist/lib/rechallenge/token-cache.js +113 -0
- package/dist/lib/rechallenge/types.js +15 -0
- package/dist/lib/token.js +18 -14
- package/docs/SETUP.md +7 -6
- package/docs/decisions/0001-operation-scoped-elevated-token-reuse.md +87 -0
- package/npm-shrinkwrap.json +18 -18
- package/package.json +6 -2
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
exports.__esModule = true;
|
|
5
|
+
exports.defensiveModeConfigureCommand = defensiveModeConfigureCommand;
|
|
6
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
7
|
+
var _enquirer = require("enquirer");
|
|
8
|
+
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
9
|
+
var _format = require("../lib/cli/format");
|
|
10
|
+
var _api = require("../lib/defensive-mode/api");
|
|
11
|
+
var _cliHelpers = require("../lib/defensive-mode/cli-helpers");
|
|
12
|
+
var _input = require("../lib/envvar/input");
|
|
13
|
+
var _tracker = require("../lib/tracker");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
const baseUsage = 'vip defensive-mode configure';
|
|
16
|
+
const exampleUsage = 'vip @example-app.production defensive-mode configure';
|
|
17
|
+
const examples = [{
|
|
18
|
+
usage: `${exampleUsage} --enabled=true --challenge-type=1`,
|
|
19
|
+
description: 'Update defensive mode configuration non-interactively (minimal required flags).'
|
|
20
|
+
}, {
|
|
21
|
+
usage: `${exampleUsage} --enabled=true --challenge-type=2 --connection-threshold-absolute=5000 --connection-threshold-percentage=80`,
|
|
22
|
+
description: 'Update with explicit thresholds.'
|
|
23
|
+
}];
|
|
24
|
+
function parseBoolean(raw) {
|
|
25
|
+
if (raw === true || raw === false) {
|
|
26
|
+
return raw;
|
|
27
|
+
}
|
|
28
|
+
if (typeof raw !== 'string') {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const normalized = raw.trim().toLowerCase();
|
|
32
|
+
if (['true', 'yes', '1', 'on', 'enable', 'enabled'].includes(normalized)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (['false', 'no', '0', 'off', 'disable', 'disabled'].includes(normalized)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function parsePositiveInt(raw) {
|
|
41
|
+
// A bare flag (e.g. `--challenge-type` with no value) arrives as boolean true,
|
|
42
|
+
// which Number() would silently coerce to 1.
|
|
43
|
+
if (raw === undefined || raw === null || typeof raw === 'boolean') {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (typeof raw === 'string' && raw.trim() === '') {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const num = Number(raw);
|
|
50
|
+
if (!Number.isInteger(num) || num < 0) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return num;
|
|
54
|
+
}
|
|
55
|
+
function validateFlags(opt) {
|
|
56
|
+
const errors = [];
|
|
57
|
+
const enabled = opt.enabled === undefined ? null : parseBoolean(opt.enabled);
|
|
58
|
+
if (opt.enabled !== undefined && enabled === null) {
|
|
59
|
+
errors.push(`Invalid value for --enabled: ${opt.enabled}. Expected true or false.`);
|
|
60
|
+
}
|
|
61
|
+
const challengeType = opt.challengeType === undefined ? null : parsePositiveInt(opt.challengeType);
|
|
62
|
+
if (opt.challengeType !== undefined && challengeType === null) {
|
|
63
|
+
errors.push(`Invalid value for --challenge-type: ${opt.challengeType}. Expected a non-negative integer.`);
|
|
64
|
+
}
|
|
65
|
+
const absolute = opt.connectionThresholdAbsolute === undefined ? undefined : parsePositiveInt(opt.connectionThresholdAbsolute);
|
|
66
|
+
if (opt.connectionThresholdAbsolute !== undefined && absolute === null) {
|
|
67
|
+
errors.push(`Invalid value for --connection-threshold-absolute: ${opt.connectionThresholdAbsolute}. Expected a non-negative integer.`);
|
|
68
|
+
}
|
|
69
|
+
const percentage = opt.connectionThresholdPercentage === undefined ? undefined : parsePositiveInt(opt.connectionThresholdPercentage);
|
|
70
|
+
if (opt.connectionThresholdPercentage !== undefined && percentage === null) {
|
|
71
|
+
errors.push(`Invalid value for --connection-threshold-percentage: ${opt.connectionThresholdPercentage}. Expected a non-negative integer.`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
enabled,
|
|
75
|
+
challengeType,
|
|
76
|
+
absolute,
|
|
77
|
+
percentage,
|
|
78
|
+
errors
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function formatSettingValue(value) {
|
|
82
|
+
return value === undefined || value === null ? '-' : String(value);
|
|
83
|
+
}
|
|
84
|
+
function buildSettingRows(currentConfig, {
|
|
85
|
+
enabled,
|
|
86
|
+
challengeType,
|
|
87
|
+
absolute,
|
|
88
|
+
percentage
|
|
89
|
+
}) {
|
|
90
|
+
return [{
|
|
91
|
+
setting: 'Enabled',
|
|
92
|
+
current: formatSettingValue(currentConfig?.enabled),
|
|
93
|
+
proposed: formatSettingValue(enabled)
|
|
94
|
+
}, {
|
|
95
|
+
setting: 'Challenge type',
|
|
96
|
+
current: formatSettingValue(currentConfig?.challengeType),
|
|
97
|
+
proposed: formatSettingValue(challengeType)
|
|
98
|
+
}, {
|
|
99
|
+
setting: 'Connection threshold (absolute)',
|
|
100
|
+
current: formatSettingValue(currentConfig?.connectionThresholdAbsolute),
|
|
101
|
+
proposed: absolute === undefined ? '(not specified)' : formatSettingValue(absolute)
|
|
102
|
+
}, {
|
|
103
|
+
setting: 'Connection threshold (percentage)',
|
|
104
|
+
current: formatSettingValue(currentConfig?.connectionThresholdPercentage),
|
|
105
|
+
proposed: percentage === undefined ? '(not specified)' : formatSettingValue(percentage)
|
|
106
|
+
}];
|
|
107
|
+
}
|
|
108
|
+
async function resolveRequiredViaPrompt(missing, enabled, challengeType) {
|
|
109
|
+
const answers = await (0, _enquirer.prompt)(missing.map(flag => flag === '--enabled' ? {
|
|
110
|
+
type: 'confirm',
|
|
111
|
+
name: 'enabled',
|
|
112
|
+
message: 'Enable defensive mode?'
|
|
113
|
+
} : {
|
|
114
|
+
type: 'input',
|
|
115
|
+
name: 'challengeType',
|
|
116
|
+
message: 'Challenge type (integer):'
|
|
117
|
+
}));
|
|
118
|
+
let resolvedEnabled = enabled;
|
|
119
|
+
let resolvedChallengeType = challengeType;
|
|
120
|
+
if (resolvedEnabled === null && 'enabled' in answers) {
|
|
121
|
+
resolvedEnabled = Boolean(answers.enabled);
|
|
122
|
+
}
|
|
123
|
+
if (resolvedChallengeType === null && 'challengeType' in answers) {
|
|
124
|
+
resolvedChallengeType = parsePositiveInt(answers.challengeType);
|
|
125
|
+
if (resolvedChallengeType === null) {
|
|
126
|
+
console.error(_chalk.default.red('Challenge type must be a non-negative integer.'));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
enabled: resolvedEnabled,
|
|
132
|
+
challengeType: resolvedChallengeType
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function defensiveModeConfigureCommand(_args, opt) {
|
|
136
|
+
const interactive = (0, _cliHelpers.isInteractive)(opt);
|
|
137
|
+
const trackingParams = {
|
|
138
|
+
app_id: opt.app.id,
|
|
139
|
+
command: baseUsage,
|
|
140
|
+
env_id: opt.env.id,
|
|
141
|
+
org_id: opt.app.organization.id,
|
|
142
|
+
org_sfid: opt.app.organization.salesforceId,
|
|
143
|
+
interactive,
|
|
144
|
+
skip_confirm: Boolean(opt.skipConfirmation)
|
|
145
|
+
};
|
|
146
|
+
await (0, _tracker.trackEvent)('defensive_mode_configure_command_execute', trackingParams);
|
|
147
|
+
const {
|
|
148
|
+
enabled: rawEnabled,
|
|
149
|
+
challengeType: rawChallengeType,
|
|
150
|
+
absolute,
|
|
151
|
+
percentage,
|
|
152
|
+
errors
|
|
153
|
+
} = validateFlags(opt);
|
|
154
|
+
if (errors.length > 0) {
|
|
155
|
+
errors.forEach(msg => console.error(_chalk.default.red(msg)));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const missing = [];
|
|
159
|
+
if (rawEnabled === null) {
|
|
160
|
+
missing.push('--enabled');
|
|
161
|
+
}
|
|
162
|
+
if (rawChallengeType === null) {
|
|
163
|
+
missing.push('--challenge-type');
|
|
164
|
+
}
|
|
165
|
+
let enabled = rawEnabled;
|
|
166
|
+
let challengeType = rawChallengeType;
|
|
167
|
+
if (missing.length > 0) {
|
|
168
|
+
if (!interactive) {
|
|
169
|
+
console.error(_chalk.default.red(`Missing required flags in non-interactive mode: ${missing.join(', ')}`));
|
|
170
|
+
console.error('Re-run with all required flags, or remove --non-interactive and run on a TTY.');
|
|
171
|
+
await (0, _tracker.trackEvent)('defensive_mode_configure_command_error', {
|
|
172
|
+
...trackingParams,
|
|
173
|
+
error: 'missing-required-flags'
|
|
174
|
+
});
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
({
|
|
178
|
+
enabled,
|
|
179
|
+
challengeType
|
|
180
|
+
} = await resolveRequiredViaPrompt(missing, enabled, challengeType));
|
|
181
|
+
}
|
|
182
|
+
const input = {
|
|
183
|
+
appId: opt.app.id,
|
|
184
|
+
envId: opt.env.id,
|
|
185
|
+
enabled,
|
|
186
|
+
challengeType
|
|
187
|
+
};
|
|
188
|
+
if (absolute !== undefined) {
|
|
189
|
+
input.connectionThresholdAbsolute = absolute;
|
|
190
|
+
}
|
|
191
|
+
if (percentage !== undefined) {
|
|
192
|
+
input.connectionThresholdPercentage = percentage;
|
|
193
|
+
}
|
|
194
|
+
const currentConfig = opt.env.defensiveMode?.config?.effective ?? null;
|
|
195
|
+
const settingRows = buildSettingRows(currentConfig, {
|
|
196
|
+
enabled,
|
|
197
|
+
challengeType,
|
|
198
|
+
absolute,
|
|
199
|
+
percentage
|
|
200
|
+
});
|
|
201
|
+
console.log(`Defensive mode configuration for ${_chalk.default.bold(opt.app.name)} (${(0, _format.formatEnvironment)(opt.env.type)}):`);
|
|
202
|
+
console.log((0, _format.table)(settingRows));
|
|
203
|
+
|
|
204
|
+
// Production mutations require confirmation. In non-interactive contexts this
|
|
205
|
+
// hard-errors unless --skip-confirmation is passed, so unattended/CI runs fail
|
|
206
|
+
// fast rather than silently mutating production — matching enable/disable.
|
|
207
|
+
await (0, _cliHelpers.guardProductionMutation)(opt, 'configure', trackingParams, _input.confirm, _tracker.trackEvent, _format.formatEnvironment);
|
|
208
|
+
const result = await (0, _api.updateDefensiveModeConfig)(input);
|
|
209
|
+
await (0, _cliHelpers.reportMutationResult)(result, trackingParams, 'configure', opt.app.name, opt.env.type, 'configuration updated', 'update defensive mode config', _tracker.trackEvent);
|
|
210
|
+
}
|
|
211
|
+
(0, _command.default)({
|
|
212
|
+
appContext: true,
|
|
213
|
+
appQuery: _api.appQuery,
|
|
214
|
+
envContext: true,
|
|
215
|
+
usage: baseUsage
|
|
216
|
+
}).option('enabled', 'Whether defensive mode should be enabled (true|false). Required.').option('challenge-type', 'Challenge type integer. Required.').option('connection-threshold-absolute', 'Absolute connection threshold that triggers defensive mode.').option('connection-threshold-percentage', 'Connection threshold percentage that triggers defensive mode.').option('non-interactive', 'Disable prompts and browser-open; fail fast if a required flag is missing.', false).option('skip-confirmation', 'Skip the confirmation prompt for production envs.', false).option('rechallenge-wait', 'When step-up verification is required non-interactively, print the URL and wait for verification on another device instead of failing fast.', false).examples(examples).argv(process.argv, defensiveModeConfigureCommand);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
exports.__esModule = true;
|
|
5
|
+
exports.defensiveModeDisableCommand = defensiveModeDisableCommand;
|
|
6
|
+
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
7
|
+
var _format = require("../lib/cli/format");
|
|
8
|
+
var _api = require("../lib/defensive-mode/api");
|
|
9
|
+
var _cliHelpers = require("../lib/defensive-mode/cli-helpers");
|
|
10
|
+
var _input = require("../lib/envvar/input");
|
|
11
|
+
var _tracker = require("../lib/tracker");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
const baseUsage = 'vip defensive-mode disable';
|
|
14
|
+
const exampleUsage = 'vip @example-app.production defensive-mode disable';
|
|
15
|
+
const examples = [{
|
|
16
|
+
usage: exampleUsage,
|
|
17
|
+
description: 'Disable defensive mode for the environment.'
|
|
18
|
+
}];
|
|
19
|
+
async function defensiveModeDisableCommand(_args, opt) {
|
|
20
|
+
const trackingParams = {
|
|
21
|
+
app_id: opt.app.id,
|
|
22
|
+
command: baseUsage,
|
|
23
|
+
env_id: opt.env.id,
|
|
24
|
+
org_id: opt.app.organization.id,
|
|
25
|
+
org_sfid: opt.app.organization.salesforceId,
|
|
26
|
+
skip_confirm: Boolean(opt.skipConfirmation)
|
|
27
|
+
};
|
|
28
|
+
await (0, _tracker.trackEvent)('defensive_mode_disable_command_execute', trackingParams);
|
|
29
|
+
await (0, _cliHelpers.guardProductionMutation)(opt, 'disable', trackingParams, _input.confirm, _tracker.trackEvent, _format.formatEnvironment);
|
|
30
|
+
const result = await (0, _api.updateDefensiveModeStatus)({
|
|
31
|
+
appId: opt.app.id,
|
|
32
|
+
envId: opt.env.id,
|
|
33
|
+
enabled: false
|
|
34
|
+
});
|
|
35
|
+
await (0, _cliHelpers.reportMutationResult)(result, trackingParams, 'disable', opt.app.name, opt.env.type, 'disabled', 'disable defensive mode', _tracker.trackEvent);
|
|
36
|
+
}
|
|
37
|
+
(0, _command.default)({
|
|
38
|
+
appContext: true,
|
|
39
|
+
appQuery: _api.appQuery,
|
|
40
|
+
envContext: true,
|
|
41
|
+
usage: baseUsage
|
|
42
|
+
}).option('skip-confirmation', 'Skip the confirmation prompt for production envs.', false).option('non-interactive', 'Disable prompts; error if a production mutation is attempted without --skip-confirmation.', false).option('rechallenge-wait', 'When step-up verification is required non-interactively, print the URL and wait for verification on another device instead of failing fast.', false).examples(examples).argv(process.argv, defensiveModeDisableCommand);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
exports.__esModule = true;
|
|
5
|
+
exports.defensiveModeEnableCommand = defensiveModeEnableCommand;
|
|
6
|
+
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
7
|
+
var _format = require("../lib/cli/format");
|
|
8
|
+
var _api = require("../lib/defensive-mode/api");
|
|
9
|
+
var _cliHelpers = require("../lib/defensive-mode/cli-helpers");
|
|
10
|
+
var _input = require("../lib/envvar/input");
|
|
11
|
+
var _tracker = require("../lib/tracker");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
const baseUsage = 'vip defensive-mode enable';
|
|
14
|
+
const exampleUsage = 'vip @example-app.production defensive-mode enable';
|
|
15
|
+
const examples = [{
|
|
16
|
+
usage: exampleUsage,
|
|
17
|
+
description: 'Enable defensive mode for the environment (interactive).'
|
|
18
|
+
}, {
|
|
19
|
+
usage: `${exampleUsage} --skip-confirmation`,
|
|
20
|
+
description: 'Enable defensive mode without the production confirmation prompt.'
|
|
21
|
+
}];
|
|
22
|
+
async function defensiveModeEnableCommand(_args, opt) {
|
|
23
|
+
const trackingParams = {
|
|
24
|
+
app_id: opt.app.id,
|
|
25
|
+
command: baseUsage,
|
|
26
|
+
env_id: opt.env.id,
|
|
27
|
+
org_id: opt.app.organization.id,
|
|
28
|
+
org_sfid: opt.app.organization.salesforceId,
|
|
29
|
+
skip_confirm: Boolean(opt.skipConfirmation)
|
|
30
|
+
};
|
|
31
|
+
await (0, _tracker.trackEvent)('defensive_mode_enable_command_execute', trackingParams);
|
|
32
|
+
await (0, _cliHelpers.guardProductionMutation)(opt, 'enable', trackingParams, _input.confirm, _tracker.trackEvent, _format.formatEnvironment);
|
|
33
|
+
const result = await (0, _api.updateDefensiveModeStatus)({
|
|
34
|
+
appId: opt.app.id,
|
|
35
|
+
envId: opt.env.id,
|
|
36
|
+
enabled: true
|
|
37
|
+
});
|
|
38
|
+
await (0, _cliHelpers.reportMutationResult)(result, trackingParams, 'enable', opt.app.name, opt.env.type, 'enabled', 'enable defensive mode', _tracker.trackEvent);
|
|
39
|
+
}
|
|
40
|
+
(0, _command.default)({
|
|
41
|
+
appContext: true,
|
|
42
|
+
appQuery: _api.appQuery,
|
|
43
|
+
envContext: true,
|
|
44
|
+
usage: baseUsage
|
|
45
|
+
}).option('skip-confirmation', 'Skip the confirmation prompt for production envs.', false).option('non-interactive', 'Disable prompts; error if a production mutation is attempted without --skip-confirmation.', false).option('rechallenge-wait', 'When step-up verification is required non-interactively, print the URL and wait for verification on another device instead of failing fast.', false).examples(examples).argv(process.argv, defensiveModeEnableCommand);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
5
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
const usage = 'vip defensive-mode';
|
|
7
|
+
const exampleUsage = 'vip @example-app.production defensive-mode';
|
|
8
|
+
const examples = [{
|
|
9
|
+
usage: `${exampleUsage} enable`,
|
|
10
|
+
description: 'Enable defensive mode for the environment.'
|
|
11
|
+
}, {
|
|
12
|
+
usage: `${exampleUsage} disable`,
|
|
13
|
+
description: 'Disable defensive mode for the environment.'
|
|
14
|
+
}, {
|
|
15
|
+
usage: `${exampleUsage} configure --enabled=true --challenge-type=1`,
|
|
16
|
+
description: 'Update the defensive mode configuration non-interactively.'
|
|
17
|
+
}];
|
|
18
|
+
(0, _command.default)({
|
|
19
|
+
requiredArgs: 1,
|
|
20
|
+
usage
|
|
21
|
+
}).command('enable', 'Enable defensive mode (step-up auth required).').command('disable', 'Disable defensive mode (step-up auth required).').command('configure', 'Update the defensive mode configuration (step-up auth required).').examples(examples).argv(process.argv);
|
package/dist/bin/vip.js
CHANGED
|
@@ -9,6 +9,7 @@ var _command = _interopRequireWildcard(require("../lib/cli/command"));
|
|
|
9
9
|
var _config = _interopRequireDefault(require("../lib/cli/config"));
|
|
10
10
|
var _internalBinLoader = require("../lib/cli/internal-bin-loader");
|
|
11
11
|
var _seaDispatch = require("../lib/cli/sea-dispatch");
|
|
12
|
+
var _tokenCache = _interopRequireDefault(require("../lib/rechallenge/token-cache"));
|
|
12
13
|
var _token = _interopRequireDefault(require("../lib/token"));
|
|
13
14
|
var _tracker = require("../lib/tracker");
|
|
14
15
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -42,7 +43,7 @@ async function maybeExecuteSeaTargetCommand() {
|
|
|
42
43
|
}
|
|
43
44
|
const runCmd = async function () {
|
|
44
45
|
const cmd = (0, _command.default)();
|
|
45
|
-
cmd.command('logout', 'Log out the current authenticated VIP-CLI user.').command('app', 'Interact with applications that the current authenticated VIP-CLI user has permission to access.').command('backup', 'Generate a backup of an environment.').command('cache', 'Manage page cache for an environment.').command('config', 'Manage environment configurations.').command('dev-env', 'Create and manage VIP Local Development Environments.').command('export', 'Export a copy of data associated with an environment.').command('import', 'Import media or SQL database files to an environment.').command('logs', 'Retrieve Runtime Logs from an environment.').command('search-replace', 'Search for a string in a local SQL file and replace it with a new string.').command('slowlogs', 'Retrieve MySQL slow query logs from an environment.').command('db', "Access an environment's database.").command('sync', 'Sync the database from production to a non-production environment.').command('whoami', 'Retrieve details about the current authenticated VIP-CLI user.').command('wp', 'Execute a WP-CLI command against an environment.');
|
|
46
|
+
cmd.command('logout', 'Log out the current authenticated VIP-CLI user.').command('app', 'Interact with applications that the current authenticated VIP-CLI user has permission to access.').command('backup', 'Generate a backup of an environment.').command('cache', 'Manage page cache for an environment.').command('config', 'Manage environment configurations.').command('dev-env', 'Create and manage VIP Local Development Environments.').command('export', 'Export a copy of data associated with an environment.').command('import', 'Import media or SQL database files to an environment.').command('logs', 'Retrieve Runtime Logs from an environment.').command('search-replace', 'Search for a string in a local SQL file and replace it with a new string.').command('slowlogs', 'Retrieve MySQL slow query logs from an environment.').command('db', "Access an environment's database.").command('defensive-mode', 'Manage VIP defensive mode for an environment.').command('sync', 'Sync the database from production to a non-production environment.').command('whoami', 'Retrieve details about the current authenticated VIP-CLI user.').command('wp', 'Execute a WP-CLI command against an environment.');
|
|
46
47
|
await cmd.argv(process.argv);
|
|
47
48
|
};
|
|
48
49
|
|
|
@@ -123,6 +124,10 @@ async function runLoginFlow() {
|
|
|
123
124
|
throw err;
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
// Elevated tokens are keyed by API host + scope, not by user identity. Drop any
|
|
128
|
+
// cached elevation from a previous login so it cannot carry across identities.
|
|
129
|
+
await _tokenCache.default.clearAll();
|
|
130
|
+
|
|
126
131
|
// De-anonymize user for tracking
|
|
127
132
|
await (0, _tracker.aliasUser)(token.id);
|
|
128
133
|
await (0, _tracker.trackEvent)('login_command_token_submit_success');
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.PRODUCTION_API_HOST = exports.API_URL = exports.API_HOST = void 0;
|
|
5
|
+
const PRODUCTION_API_HOST = exports.PRODUCTION_API_HOST = 'https://api.wpvip.com';
|
|
6
|
+
const API_HOST = exports.API_HOST = process.env.API_HOST || PRODUCTION_API_HOST; // NOSONAR
|
|
7
|
+
const API_URL = exports.API_URL = `${API_HOST}/graphql`;
|
|
@@ -5,9 +5,22 @@ exports.get = get;
|
|
|
5
5
|
var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
|
|
6
6
|
var _api = _interopRequireDefault(require("../../lib/api"));
|
|
7
7
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
// Lazy-initialize the API client so that this module can be imported during the
|
|
9
|
+
// rechallenge module chain without triggering a circular-dependency crash. The
|
|
10
|
+
// cycle that existed before this change:
|
|
11
|
+
// api.ts → rechallenge/link.ts → flow.ts → tracker.ts → tracks.ts
|
|
12
|
+
// → cli/apiConfig.ts → api/feature-flags.ts → api.ts
|
|
13
|
+
// By deferring construction to the first call we ensure api.ts is fully
|
|
14
|
+
// evaluated before API() is invoked.
|
|
15
|
+
let api = null;
|
|
16
|
+
function getApi() {
|
|
17
|
+
if (!api) {
|
|
18
|
+
api = (0, _api.default)({
|
|
19
|
+
silenceAuthErrors: true
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return api;
|
|
23
|
+
}
|
|
11
24
|
const isVipQuery = (0, _graphqlTag.default)`
|
|
12
25
|
query isVIP {
|
|
13
26
|
me {
|
|
@@ -16,7 +29,7 @@ const isVipQuery = (0, _graphqlTag.default)`
|
|
|
16
29
|
}
|
|
17
30
|
`;
|
|
18
31
|
function get() {
|
|
19
|
-
return
|
|
32
|
+
return getApi().query({
|
|
20
33
|
query: isVipQuery,
|
|
21
34
|
fetchPolicy: 'cache-first'
|
|
22
35
|
});
|
package/dist/lib/api/http.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.__esModule = true;
|
|
|
4
4
|
exports.default = void 0;
|
|
5
5
|
var _debug = _interopRequireDefault(require("debug"));
|
|
6
6
|
var _undici = require("undici");
|
|
7
|
-
var
|
|
7
|
+
var _constants = require("./constants");
|
|
8
8
|
var _env = _interopRequireDefault(require("../../lib/env"));
|
|
9
9
|
var _proxyDispatcher = require("../../lib/http/proxy-dispatcher");
|
|
10
10
|
var _token = _interopRequireDefault(require("../../lib/token"));
|
|
@@ -31,8 +31,8 @@ var _default = async (path, options = {}) => {
|
|
|
31
31
|
|
|
32
32
|
// For convenience, we support just passing in the path to this function...
|
|
33
33
|
// but some things (Apollo) always pass the full url
|
|
34
|
-
if (!path.startsWith(
|
|
35
|
-
url = `${
|
|
34
|
+
if (!path.startsWith(_constants.API_HOST)) {
|
|
35
|
+
url = `${_constants.API_HOST}${path}`;
|
|
36
36
|
}
|
|
37
37
|
const authToken = await _token.default.get();
|
|
38
38
|
const proxyDispatcher = (0, _proxyDispatcher.createProxyDispatcher)(url);
|
package/dist/lib/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.PRODUCTION_API_HOST = exports.
|
|
4
|
+
exports.PRODUCTION_API_HOST = exports.API_HOST = void 0;
|
|
5
5
|
exports.default = API;
|
|
6
6
|
exports.disableGlobalGraphQLErrorHandling = disableGlobalGraphQLErrorHandling;
|
|
7
7
|
exports.enableGlobalGraphQLErrorHandling = enableGlobalGraphQLErrorHandling;
|
|
@@ -12,12 +12,16 @@ var _retry = require("@apollo/client/link/retry");
|
|
|
12
12
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
13
13
|
var _debug = _interopRequireDefault(require("debug"));
|
|
14
14
|
var _graphql = require("graphql");
|
|
15
|
+
var _constants = require("./api/constants");
|
|
16
|
+
exports.API_URL = _constants.API_URL;
|
|
17
|
+
exports.API_HOST = _constants.API_HOST;
|
|
18
|
+
exports.PRODUCTION_API_HOST = _constants.PRODUCTION_API_HOST;
|
|
15
19
|
var _http = _interopRequireDefault(require("./api/http"));
|
|
16
20
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
-
// Config
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
// Config — re-exported from ./api/constants so modules in the rechallenge tree
|
|
22
|
+
// can import them without pulling in the full api.ts graph (which would create
|
|
23
|
+
// a circular dependency via the rechallenge link).
|
|
24
|
+
|
|
21
25
|
let globalGraphQLErrorHandlingEnabled = true;
|
|
22
26
|
const RETRY_LINK_MAX_ATTEMPTS = 5;
|
|
23
27
|
const RETRY_LINK_INITIAL_DELAY_MS = 1000; // 1 second
|
|
@@ -88,10 +92,10 @@ function API({
|
|
|
88
92
|
uri: operation => {
|
|
89
93
|
// to make it easier to write tests, we'll skip adding x_query for tests
|
|
90
94
|
if (process.env.NODE_ENV === 'test') {
|
|
91
|
-
return API_URL;
|
|
95
|
+
return _constants.API_URL;
|
|
92
96
|
}
|
|
93
97
|
const operationName = operation.operationName ?? '';
|
|
94
|
-
return `${API_URL}?x_query=${encodeURIComponent(operationName)}`;
|
|
98
|
+
return `${_constants.API_URL}?x_query=${encodeURIComponent(operationName)}`;
|
|
95
99
|
},
|
|
96
100
|
fetch: _http.default
|
|
97
101
|
});
|
|
@@ -102,8 +106,19 @@ function API({
|
|
|
102
106
|
},
|
|
103
107
|
attempts: shouldRetryRequest
|
|
104
108
|
});
|
|
109
|
+
|
|
110
|
+
// Lazy-require the rechallenge link to avoid a circular-dependency issue in
|
|
111
|
+
// Jest tests. Importing at module top-level would cause rechallenge/client.ts
|
|
112
|
+
// to be loaded during jest.setupMocks.js (via apiConfig → feature-flags →
|
|
113
|
+
// api.ts → link.ts → client.ts), preventing jest.mock('../api/http') from
|
|
114
|
+
// intercepting the http reference captured inside client.ts. A require()
|
|
115
|
+
// call inside the function body is resolved after all mocks are registered.
|
|
116
|
+
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
118
|
+
const linkMod = require('./rechallenge/link');
|
|
119
|
+
const createRechallengeLink = linkMod.default;
|
|
105
120
|
return new _core.ApolloClient({
|
|
106
|
-
link: _core.ApolloLink.from([errorLink, retryLink, httpLink]),
|
|
121
|
+
link: _core.ApolloLink.from([errorLink, createRechallengeLink(), retryLink, httpLink]),
|
|
107
122
|
cache: new _core.InMemoryCache({
|
|
108
123
|
typePolicies: {
|
|
109
124
|
WPSite: {
|
|
@@ -28,6 +28,10 @@ const internalBinLoaders = {
|
|
|
28
28
|
'vip-config-software-update': () => import('../../bin/vip-config-software-update'),
|
|
29
29
|
'vip-db': () => import('../../bin/vip-db'),
|
|
30
30
|
'vip-db-phpmyadmin': () => import('../../bin/vip-db-phpmyadmin'),
|
|
31
|
+
'vip-defensive-mode': () => import('../../bin/vip-defensive-mode'),
|
|
32
|
+
'vip-defensive-mode-configure': () => import('../../bin/vip-defensive-mode-configure'),
|
|
33
|
+
'vip-defensive-mode-disable': () => import('../../bin/vip-defensive-mode-disable'),
|
|
34
|
+
'vip-defensive-mode-enable': () => import('../../bin/vip-defensive-mode-enable'),
|
|
31
35
|
'vip-dev-env': () => import('../../bin/vip-dev-env'),
|
|
32
36
|
'vip-dev-env-create': () => import('../../bin/vip-dev-env-create'),
|
|
33
37
|
'vip-dev-env-destroy': () => import('../../bin/vip-dev-env-destroy'),
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.appQuery = void 0;
|
|
5
|
+
exports.updateDefensiveModeConfig = updateDefensiveModeConfig;
|
|
6
|
+
exports.updateDefensiveModeStatus = updateDefensiveModeStatus;
|
|
7
|
+
var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
|
|
8
|
+
var _api = _interopRequireDefault(require("../api"));
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
const appQuery = exports.appQuery = `
|
|
11
|
+
id
|
|
12
|
+
name
|
|
13
|
+
typeId
|
|
14
|
+
environments {
|
|
15
|
+
id
|
|
16
|
+
appId
|
|
17
|
+
name
|
|
18
|
+
primaryDomain {
|
|
19
|
+
name
|
|
20
|
+
}
|
|
21
|
+
type
|
|
22
|
+
defensiveMode {
|
|
23
|
+
config {
|
|
24
|
+
effective {
|
|
25
|
+
enabled
|
|
26
|
+
challengeType
|
|
27
|
+
connectionThresholdAbsolute
|
|
28
|
+
connectionThresholdPercentage
|
|
29
|
+
disableAtEpoch
|
|
30
|
+
keepEnabledUnderThresholdForSeconds
|
|
31
|
+
maxRequestRate
|
|
32
|
+
priorityBypass
|
|
33
|
+
}
|
|
34
|
+
stored {
|
|
35
|
+
enabled
|
|
36
|
+
challengeType
|
|
37
|
+
connectionThresholdAbsolute
|
|
38
|
+
connectionThresholdPercentage
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
organization {
|
|
44
|
+
id
|
|
45
|
+
name
|
|
46
|
+
salesforceId
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
const STATUS_MUTATION = (0, _graphqlTag.default)`
|
|
50
|
+
mutation UpdateDefensiveModeStatus($input: AppEnvironmentDefensiveModeUpdateStatusInput) {
|
|
51
|
+
updateDefensiveModeStatus(input: $input) {
|
|
52
|
+
success
|
|
53
|
+
message
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
const CONFIG_MUTATION = (0, _graphqlTag.default)`
|
|
58
|
+
mutation UpdateDefensiveModeConfig($input: AppEnvironmentDefensiveModeConfigInput) {
|
|
59
|
+
updateDefensiveModeConfig(input: $input) {
|
|
60
|
+
success
|
|
61
|
+
message
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
async function updateDefensiveModeStatus(input) {
|
|
66
|
+
const api = (0, _api.default)();
|
|
67
|
+
const result = await api.mutate({
|
|
68
|
+
mutation: STATUS_MUTATION,
|
|
69
|
+
variables: {
|
|
70
|
+
input: {
|
|
71
|
+
id: input.appId,
|
|
72
|
+
environmentId: input.envId,
|
|
73
|
+
enabled: input.enabled
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (!result.data) {
|
|
78
|
+
throw new Error('updateDefensiveModeStatus returned no data; the API may have rejected the request.');
|
|
79
|
+
}
|
|
80
|
+
return result.data.updateDefensiveModeStatus;
|
|
81
|
+
}
|
|
82
|
+
async function updateDefensiveModeConfig(input) {
|
|
83
|
+
const api = (0, _api.default)();
|
|
84
|
+
const mutationInput = {
|
|
85
|
+
id: input.appId,
|
|
86
|
+
environmentId: input.envId,
|
|
87
|
+
enabled: input.enabled,
|
|
88
|
+
challengeType: input.challengeType
|
|
89
|
+
};
|
|
90
|
+
if (input.connectionThresholdAbsolute !== undefined) {
|
|
91
|
+
mutationInput.connectionThresholdAbsolute = input.connectionThresholdAbsolute;
|
|
92
|
+
}
|
|
93
|
+
if (input.connectionThresholdPercentage !== undefined) {
|
|
94
|
+
mutationInput.connectionThresholdPercentage = input.connectionThresholdPercentage;
|
|
95
|
+
}
|
|
96
|
+
const result = await api.mutate({
|
|
97
|
+
mutation: CONFIG_MUTATION,
|
|
98
|
+
variables: {
|
|
99
|
+
input: mutationInput
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
if (!result.data) {
|
|
103
|
+
throw new Error('updateDefensiveModeConfig returned no data; the API may have rejected the request.');
|
|
104
|
+
}
|
|
105
|
+
return result.data.updateDefensiveModeConfig;
|
|
106
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.guardProductionMutation = guardProductionMutation;
|
|
5
|
+
exports.isInteractive = isInteractive;
|
|
6
|
+
exports.reportMutationResult = reportMutationResult;
|
|
7
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
function isInteractive(opt) {
|
|
10
|
+
if (process.env.VIP_NON_INTERACTIVE === '1') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (opt.nonInteractive) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return Boolean(process.stdout.isTTY);
|
|
17
|
+
}
|
|
18
|
+
function capitalize(str) {
|
|
19
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Guards production mutations that require confirmation.
|
|
24
|
+
* Returns when the command should proceed. In non-interactive contexts without
|
|
25
|
+
* --skip-confirmation it emits an error and calls process.exit(1) directly.
|
|
26
|
+
* If the user declines the interactive prompt it calls process.exit() directly.
|
|
27
|
+
*/
|
|
28
|
+
async function guardProductionMutation(opt, action, trackingParams, confirmFn, trackEventFn, formatEnvironment) {
|
|
29
|
+
if (opt.skipConfirmation || opt.env.type !== 'production') {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!isInteractive(opt)) {
|
|
33
|
+
console.error(_chalk.default.red(`Refusing to ${action} defensive mode on production without confirmation. ` + 'Pass --skip-confirmation to proceed non-interactively.'));
|
|
34
|
+
await trackEventFn(`defensive_mode_${action}_command_cancelled`, trackingParams);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
const yes = await confirmFn(`${capitalize(action)} defensive mode on ${formatEnvironment(opt.env.type)} for ${opt.app.name}?`);
|
|
38
|
+
if (!yes) {
|
|
39
|
+
await trackEventFn(`defensive_mode_${action}_command_cancelled`, trackingParams);
|
|
40
|
+
console.log('Command cancelled');
|
|
41
|
+
process.exit();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Handles success/failure reporting, telemetry, and log output after a
|
|
47
|
+
* defensive-mode mutation. Exits the process on failure.
|
|
48
|
+
*/
|
|
49
|
+
async function reportMutationResult(result, trackingParams, action, appName, envType, successVerb, failureVerb, trackEventFn) {
|
|
50
|
+
if (!result.success) {
|
|
51
|
+
await trackEventFn(`defensive_mode_${action}_command_error`, {
|
|
52
|
+
...trackingParams,
|
|
53
|
+
error: result.message
|
|
54
|
+
});
|
|
55
|
+
console.error(_chalk.default.red(`Failed to ${failureVerb}: ${result.message}`));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
await trackEventFn(`defensive_mode_${action}_command_success`, trackingParams);
|
|
59
|
+
console.log(_chalk.default.green('✓'), `Defensive mode ${successVerb} for ${appName}.${envType} — ${result.message}`);
|
|
60
|
+
}
|