Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add param noSensitiveData to Plugin.load #665

Merged
merged 7 commits into from
Mar 22, 2023

Conversation

peternhale
Copy link
Contributor

@W-12513575@

@peternhale peternhale requested a review from mshanemc March 20, 2023 19:35
@@ -813,11 +813,11 @@ export class Config implements IConfig {
}

// when no manifest exists, the default is calculated. This may throw, so we need to catch it
const defaultFlagToCached = async (flag: CompletableOptionFlag<any>) => {
const defaultFlagToCached = async (flag: CompletableOptionFlag<any>, noSensitiveData = false) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add noSensitiveData param to defaultFlagToCached. Value is pass to defaultHelp and default

} catch {}
} else {
return flag.default
}
}

const defaultArgToCached = async (arg: Arg<any>): Promise<any> => {
const defaultArgToCached = async (arg: Arg<any>, noSensitiveData = false): Promise<any> => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add noSensitiveData param to defaultArgToCached. Value is pass to defaultHelp and default

} catch {}
} else {
return arg.default
}
}

export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Command.Cached> {
export async function toCached(c: Command.Class, plugin?: IPlugin | undefined, noSensitiveData?: boolean): Promise<Command.Cached> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add noSensitiveData param to toCached. Value will be passes to defaultFlagToCached and defaultArgToCached functions

@@ -136,7 +136,7 @@ export class Plugin implements IPlugin {

constructor(public options: PluginOptions) {}

public async load(): Promise<void> {
public async load(noSensitiveData?: boolean): Promise<void> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add noSensitiveData param to load. Value is passed to _manifest

@@ -279,7 +279,7 @@ export class Plugin implements IPlugin {
version: this.version,
commands: (await Promise.all(this.commandIDs.map(async id => {
try {
return [id, await toCached(await this.findCommand(id, {must: true}), this)]
return [id, await toCached(await this.findCommand(id, {must: true}), this, noSensitiveData)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass noSensitiveData to toCached

@@ -55,11 +55,11 @@ export type DefaultContext<T> = {
flags: Record<string, string>;
}

export type FlagDefault<T, P = CustomOptions> = T | ((context: DefaultContext<P & OptionFlag<T, P>>) => Promise<T>)
export type FlagDefaultHelp<T, P = CustomOptions> = T | ((context: DefaultContext<P & OptionFlag<T, P>>) => Promise<string | undefined>)
export type FlagDefault<T, P = CustomOptions> = T | ((context: DefaultContext<P & OptionFlag<T, P>>, noSensitiveData?: boolean) => Promise<T>)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add noSensitiveData to flag and arg default function types

@@ -47,7 +47,7 @@ export class TestHelp extends Help {

export const commandHelp = (command?: any) => ({
async run(ctx: {help: TestHelp; commandHelp: string; expectation: string}) {
const cached = await toCached(command!, {} as any)
const cached = await toCached(command!, {} as any, false)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Command help is being called, do not suppress sensitive data

@peternhale
Copy link
Contributor Author

@mshanemc With this change to core, oclif manifest will be changed to pass true to plugin.load(). Any custom default or defaultHelp functions will then be able to omit sensitive info from return values.

Copy link
Member

@mshanemc mshanemc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we pass it around like a hot potato to make sure the various "consumers" get the correct value, but it's up to the user-defined default function to decide what to do.

I don't love the name...would I set it to true if I'm including sensitive information that I want omitted (and therefore containsSensitiveInformation:true would be a better name.

Or...does noSensitiveData : true mean "this contains no sensitive data" and so things should proceed as normal?

@peternhale
Copy link
Contributor Author

So we pass it around like a hot potato to make sure the various "consumers" get the correct value, but it's up to the user-defined default function to decide what to do.

I don't love the name...would I set it to true if I'm including sensitive information that I want omitted (and therefore containsSensitiveInformation:true would be a better name.

Or...does noSensitiveData : true mean "this contains no sensitive data" and so things should proceed as normal?

Backwards compatibility. Plugin.load is currently a no parameter function. I could change the name to suppressSensitiveData with default of false.

@peternhale peternhale requested a review from mshanemc March 20, 2023 20:31
@peternhale peternhale requested a review from cristiand391 March 21, 2023 16:33
@@ -43,6 +43,7 @@ export type Metadata = {

type MetadataFlag = {
setFromDefault?: boolean;
defaultHelp?: unknown;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to retain value from defaultHelp to be used in later steps of parse/validation.

@@ -208,7 +292,7 @@ export type BooleanFlag<T> = FlagProps & BooleanFlagProps & {

export type OptionFlagDefaults<T, P = CustomOptions, M = false> = FlagProps & OptionFlagProps & {
parse: FlagParser<T, string, P>
defaultHelp?: FlagDefaultHelp<T>;
defaultHelp?: T | FlagDefaultHelp<T>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to check this change. Should not be needed

@@ -217,6 +217,14 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
const flag = this.input.flags[token.flag]

if (!flag) throw new CLIError(`Unexpected flag ${token.flag}`)

// if flag has defaultHelp, capture its value into metadata
if (Reflect.has(flag, 'defaultHelp')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check to see if defaultHelp is present on the flag. If so capture either constant or results of function call for later use.

@@ -271,7 +279,7 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
}

if (!(k in flags) && flag.default !== undefined) {
this.metaData.flags[k] = {setFromDefault: true}
this.metaData.flags[k] = {...this.metaData.flags[k], setFromDefault: true}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure all metadata captured are retained

@@ -118,7 +118,8 @@ export async function validate(parse: {
if (parse.output.metadata.flags && parse.output.metadata.flags[name]?.setFromDefault)
continue
if (parse.output.flags[flag] !== undefined) {
return {...base, status: 'failed', reason: `--${flag}=${parse.output.flags[flag]} cannot also be provided when using --${name}`}
const flagValue = parse.output.metadata.flags?.[flag]?.defaultHelp ?? parse.output.flags[flag]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exclusive use error to be created here. To avoid --target-org=[object Object] cannot also be provided when using --all, see if defaultHelp value is present and if so use that in message.

expect(error.message).to.include('--foo=bar cannot also be provided when using --bar')
}
})
it('should error with exclusive flag violation and defaultHelp value', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

Copy link
Member

@mshanemc mshanemc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved with non-blocking comments.

Can you add some ideas for the best way to real-world QA this, since oclif changes don't have NUT coverage?

@peternhale
Copy link
Contributor Author

approved with non-blocking comments.

Can you add some ideas for the best way to real-world QA this, since oclif changes don't have NUT coverage?
@mshanemc
I am going to put this change to work in sf-plugins-core, org flags straight away. The other choice might be to create a canary plugin project in salesforcecli, so we might test plugin tooling outside the context of dx plugins.

@peternhale peternhale merged commit b4a738e into main Mar 22, 2023
@peternhale peternhale deleted the phale/no-sensitive-data branch March 22, 2023 15:19
* default is false to maintain backwards compatibility
* @returns Promise<void>
*/
public async load(isWritingManifest?: boolean): Promise<void> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered adding isWritingManifest as a new field in the options object here?
https://github.com/oclif/core/blob/main/src/config/plugin.ts#L137

That way we could load plugins in oclif manifest like this:

const plugin = new Plugin({root: fullPath, isWritingManifest: true})
await plugin.load()

instead of adding a new arg to Plugin.load

peternhale added a commit that referenced this pull request Mar 22, 2023
peternhale added a commit that referenced this pull request Mar 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants