UNPKG

@zowe/imperative

Version:
373 lines 18.2 kB
"use strict"; /* * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v2.0 which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-v20.html * * SPDX-License-Identifier: EPL-2.0 * * Copyright Contributors to the Zowe Project. * */ var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.Censor = void 0; const lodash = require("lodash"); const CliUtils_1 = require("../../utilities/src/CliUtils"); const ImperativeConfig_1 = require("../../utilities/src/ImperativeConfig"); const EnvironmentalVariableSettings_1 = require("../../imperative/src/env/EnvironmentalVariableSettings"); class Censor { // A set of default censored options. static get DEFAULT_CENSORED_OPTIONS() { const censoredList = new Set(); for (const option of this.MAIN_CENSORED_OPTIONS) { censoredList.add(option); censoredList.add(CliUtils_1.CliUtils.getOptionFormat(option).kebabCase); } return Array.from(censoredList); } // Return a customized list of secure prompt options static get SECURE_PROMPT_OPTIONS() { const censoredList = new Set(); for (const option of this.MAIN_SECURE_PROMPT_OPTIONS) { censoredList.add(option); censoredList.add(CliUtils_1.CliUtils.getOptionFormat(option).kebabCase); } return Array.from(censoredList); } // Return a customized list of censored options (or just the defaults if not set). static get CENSORED_OPTIONS() { return Array.from(this.mCensoredOptions); } /** * Helper method to get an internal reference to the loaded profiles */ static get profileSchemas() { var _b, _c; if (this.mSchema == null) this.mSchema = (_c = (_b = ImperativeConfig_1.ImperativeConfig.instance.loadedConfig) === null || _b === void 0 ? void 0 : _b.profiles) !== null && _c !== void 0 ? _c : []; return this.mSchema; } /** * Helper method to set an internal reference to loaded profiles * @param _schemas - The schmas to pass in to set to the logger */ static setProfileSchemas(_schemas) { if (this.mSchema == null) { this.mSchema = []; } if (_schemas instanceof Map) { _schemas.forEach((v) => { this.mSchema.push({ type: v.type, schema: v }); }); } else if (Array.isArray(_schemas)) { _schemas.forEach((v) => { this.mSchema.push({ type: v.type, schema: v.schema }); }); } } /**************************************************** * Helper functions for more advanced functionality * ****************************************************/ /** * Helper function to handle profile schemas when setting the censored options * @param {IProfileTypeConfiguration | ICommandProfileTypeConfiguration} profileType - the profile type configuration to iterate over */ static handleSchema(profileType) { const secureOptions = new Set(); /* eslint-disable-next-line no-unused-vars */ for (const [key, cmdProp] of Object.entries(profileType.schema.properties)) { const prop = cmdProp; // Add censored options from the schema if the option is secure if (prop.secure) { // Handle the case of a single option definition if (prop.optionDefinition) { secureOptions.add(prop.optionDefinition.name); for (const alias of prop.optionDefinition.aliases || []) { // Remember to add the alias secureOptions.add(alias); } } else if (prop.optionDefinitions) { // Handle the case of multiple option definitions prop.optionDefinitions.forEach(opDef => { secureOptions.add(opDef.name); for (const alias of opDef.aliases || []) { // Remember to add the alias secureOptions.add(alias); } }); } else { secureOptions.add(key); } } } secureOptions.forEach(prop => this.addCensoredOption(prop)); } /** * Add a censored option, including it's camelCase and kebabCase versions * @param {string} option - The option to censor */ static addCensoredOption(option) { // This option is required, but we do not want to ever allow null or undefined itself into the censored options if (option != null) { this.mCensoredOptions.add(option); this.mCensoredOptions.add(CliUtils_1.CliUtils.getOptionFormat(option).camelCase); this.mCensoredOptions.add(CliUtils_1.CliUtils.getOptionFormat(option).kebabCase); } } /** * Specifies whether a given property path (e.g. "profiles.lpar1.properties.host") is a special value or not. * Special value: Refers to any value defined as secure in the schema definition. * These values should be already masked by the application (and/or plugin) developer. * @param prop Property path to determine if it is a special value * @returns True - if the given property is to be treated as a special value; False - otherwise */ static isSpecialValue(prop) { let specialValues = this.SECURE_PROMPT_OPTIONS; const getPropertyNames = (prop) => { var _b, _c; const ret = []; ret.push((_b = prop.optionDefinition) === null || _b === void 0 ? void 0 : _b.name); (_c = prop.optionDefinitions) === null || _c === void 0 ? void 0 : _c.map(opDef => ret.push(opDef.name)); return ret; }; for (const profile of this.profileSchemas) { for (const prop of Object.values(profile.schema.properties)) { if (prop.secure) specialValues = lodash.union(specialValues, getPropertyNames(prop)); } } return specialValues.some((v) => prop.endsWith(`.${v}`)); } /** * Identifies if a property is a secure property * @param {string} prop - The property to check * @returns {boolean} - True if the property is secure; False otherwise */ static isSecureValue(prop) { return this.CENSORED_OPTIONS.includes(prop); } /**************************************************************************************** * Bread and butter functions, setting up the class and performing censorship of values * ****************************************************************************************/ /** * Generate and set the list of censored options. * Attempt to source the censored options from the schema, config, and/or command being executed. * @param {ICensorOptions} censorOpts - The objects to use to gather options that should be censored */ static setCensoredOptions(censorOpts) { var _b, _c, _d, _e, _f, _g, _h, _j; this.mCensoredOptions = new Set(this.DEFAULT_CENSORED_OPTIONS); if (censorOpts) { // Save off the config object this.mConfig = censorOpts.config; // If we have a ProfileTypeConfiguration (i.e. ImperativeConfig.instance.loadedConfig.profiles) if (censorOpts.profiles) { this.mSchema = []; this.setProfileSchemas(censorOpts.profiles); } for (const profileType of (_b = this.profileSchemas) !== null && _b !== void 0 ? _b : []) { // If we know the command we are running, and we know the profile types that the command uses // we should only use those profiles to determine what should be censored. If we do not, we should // add everything if (censorOpts.commandDefinition == null || ((_d = (_c = censorOpts.commandDefinition.profile) === null || _c === void 0 ? void 0 : _c.optional) === null || _d === void 0 ? void 0 : _d.includes(profileType.type)) || ((_f = (_e = censorOpts.commandDefinition.profile) === null || _e === void 0 ? void 0 : _e.required) === null || _f === void 0 ? void 0 : _f.includes(profileType.type))) { this.handleSchema(profileType); } } // Include any secure options from the config if (censorOpts.config) { const secureOptions = new Set(); // Try to use the command and inputs to find the profiles being loaded if (censorOpts.commandDefinition && censorOpts.commandArguments) { const profiles = []; for (const prof of ((_g = censorOpts.commandDefinition.profile) === null || _g === void 0 ? void 0 : _g.required) || []) { profiles.push(prof); } for (const prof of ((_h = censorOpts.commandDefinition.profile) === null || _h === void 0 ? void 0 : _h.optional) || []) { profiles.push(prof); } for (const prof of profiles) { // If the profile exists, append all of the secure props to the censored list let profName = (_j = censorOpts.commandArguments) === null || _j === void 0 ? void 0 : _j[`${prof}-profile`]; if (!profName) { profName = this.mConfig.mProperties.defaults[`${prof}`]; } if (profName && censorOpts.config.api.profiles.get(profName)) { censorOpts.config.api.secure.securePropsForProfile(profName).forEach(prop => secureOptions.add(prop)); } } } else { // We only have a configuration file, assume every property that is secured should be censored censorOpts.config.api.secure.findSecure(censorOpts.config.mProperties.profiles, "profiles").forEach(prop => secureOptions.add(prop.split(".").pop())); } secureOptions.forEach(prop => this.addCensoredOption(prop)); } } else if (this.profileSchemas) { for (const profileType of this.profileSchemas) { this.handleSchema(profileType); } } } /** * Copy and censor any sensitive CLI arguments before logging/printing * @param {string[]} args - The args list to censor * @returns {string[]} */ static censorCLIArgs(args) { const newArgs = JSON.parse(JSON.stringify(args)); const censoredValues = this.CENSORED_OPTIONS.map(CliUtils_1.CliUtils.getDashFormOfOption); for (const value of censoredValues) { if (args.indexOf(value) >= 0) { const valueIndex = args.indexOf(value); if (valueIndex < args.length - 1) { newArgs[valueIndex + 1] = this.CENSOR_RESPONSE; // censor the argument after the option name } } } return newArgs; } /** * Copy and censor any sensitive CLI arguments before logging/printing * @param {string} data - the data to censor * @returns {string} - the censored data */ static censorRawData(data, category = "") { var _b, _c, _d; const config = (_b = this.mConfig) !== null && _b !== void 0 ? _b : (_c = ImperativeConfig_1.ImperativeConfig.instance) === null || _c === void 0 ? void 0 : _c.config; // Return the data if not using config if (!(config === null || config === void 0 ? void 0 : config.exists)) return data; // Return the data if we are printing to the console and masking is disabled if ((_d = ImperativeConfig_1.ImperativeConfig.instance) === null || _d === void 0 ? void 0 : _d.envVariablePrefix) { const envMaskOutput = EnvironmentalVariableSettings_1.EnvironmentalVariableSettings.read(ImperativeConfig_1.ImperativeConfig.instance.envVariablePrefix).maskOutput.value; const envShowSecureArgs = EnvironmentalVariableSettings_1.EnvironmentalVariableSettings.read(ImperativeConfig_1.ImperativeConfig.instance.envVariablePrefix).showSecureArgs.value; // Hardcoding "console" instead of using Logger.DEFAULT_CONSOLE_NAME because of circular dependencies if ((category === "console" || category === "json") && (envMaskOutput.toUpperCase() === "FALSE" || envShowSecureArgs.toUpperCase() === "TRUE")) { return data; } } let newData = data; const secureFields = config.api.secure.findSecure(config.mProperties.profiles, "profiles"); for (const prop of secureFields) { const sec = lodash.get(config.mProperties, prop); if (sec && typeof sec !== "object" && !this.isSpecialValue(prop) && this.isSecureValue(prop.split(".").pop())) { newData = newData.replace(new RegExp(sec, "gi"), this.CENSOR_RESPONSE); } } return newData; } /** * Copy and censor a yargs argument object before logging * @param {yargs.Arguments} args - the args to censor * @returns {yargs.Arguments} - a censored copy of the arguments */ static censorYargsArguments(args) { const newArgs = JSON.parse(JSON.stringify(args)); for (const optionName of Object.keys(newArgs)) { if (this.CENSORED_OPTIONS.indexOf(optionName) >= 0) { const valueToCensor = newArgs[optionName]; newArgs[optionName] = this.CENSOR_RESPONSE; for (const checkAliasKey of Object.keys(newArgs)) { if (newArgs[checkAliasKey] === valueToCensor) { newArgs[checkAliasKey] = this.CENSOR_RESPONSE; } } } } return newArgs; } // *********************************************************************** /** * Censor sensitive data from an session object or a sub-object of a session. * The intent is to create a copy of the object that is suitable for logging. * * @param sessObj - A Session object (or ISession, or availableCreds) to be censored. * * @returns - The censored object as a string. */ static censorSession(sessObj) { if (!sessObj) { return _a.NULL_SESS_OBJ_MSG + " censorSession"; } return _a.replaceValsInSess(sessObj, true); } // *********************************************************************** /** * Recursively replace sensitive data in an session-related object * and any relevant sub-objects. * * @param sessObj - A Session object (or ISession, or the availableCreds) to be censored. * * @returns - The censored object as a string. */ static replaceValsInSess(sessObj, createCopy) { var _b; if (!sessObj) { return _a.NULL_SESS_OBJ_MSG + " replaceValsInSess"; } const propsToBeCensored = [..._a.SECURE_PROMPT_OPTIONS, ..._a.DEFAULT_CENSORED_OPTIONS]; // create copy of sessObj so that we can replace values in a censored object let censoredSessObj; if (createCopy) { try { censoredSessObj = JSON.parse(JSON.stringify(sessObj)); } catch (error) { return "Invalid session object was passed to API replaceValsInSess. Reason = " + error.toString(); } } else { censoredSessObj = sessObj; } // Censor values in the top level of the supplied object for (const censoredProp of propsToBeCensored) { if (censoredSessObj[censoredProp] != null) { censoredSessObj[censoredProp] = _a.CENSOR_RESPONSE; } } if (censoredSessObj.mISession) { // the object has an ISession sub-object, so censor those values _a.replaceValsInSess(censoredSessObj.mISession, false); } if ((_b = censoredSessObj._authCache) === null || _b === void 0 ? void 0 : _b.availableCreds) { // the object has an availableCreds sub-object, so censor those values _a.replaceValsInSess(censoredSessObj._authCache.availableCreds, false); } return JSON.stringify(censoredSessObj, null, 2); } } exports.Censor = Censor; _a = Censor; /********************************************************************* * Basic censorship items - list definitions & initialiazations, etc. * **********************************************************************/ /* * NOTE(Kelosky): Ideally we might have a consolidated list for secure fields, but for now we'll just * make sure they're collocated within the same class. * * NOTE(Harn): This list should be kept in sync with the base profile secure definitions and MUST be in camel case. */ Censor.MAIN_CENSORED_OPTIONS = ["auth", "authentication", "basicAuth", "base64EncodedAuth", "certFilePassphrase", "credentials", "pw", "pass", "password", "passphrase", "tv", "tokenValue"]; Censor.MAIN_SECURE_PROMPT_OPTIONS = ["keyPassphrase", "password", "passphrase", "tokenValue", "user"]; // The censor response. Censor.CENSOR_RESPONSE = "****"; // The censor response. Censor.NULL_SESS_OBJ_MSG = "Null session object was passed to API"; // Set a censored options list that can be set and retrieved for each command. Censor.mCensoredOptions = new Set(_a.DEFAULT_CENSORED_OPTIONS); //Singleton caches of the Config, Command Definition and Command Arguments Censor.mConfig = null; /** * Singleton implementation of an internal reference to the schema */ Censor.mSchema = null; //# sourceMappingURL=Censor.js.map