Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,9 @@ fileignoreconfig:
- filename: test/integration/projects-delete.test.ts
checksum: bb59cbe1f4f7b3ac6aa7944067c1ab1214a7c5d20229e9d6577cdaabec56622e
- filename: src/core/launch-command.ts
checksum: 01904855aca54cbbcb846069cee5a0732da1db082110e7e454a5c1c532ab02fb
checksum: 999468661532665870b81face9086998e543df648175e2417045840dc97cb1dd
- filename: src/core/launch-command.test.ts
checksum: c4d3785f25377db560bb9daa5d0d080a0131056e6c31e3b45cb4f1f2347305ed
checksum: f2570ccfd27539240b251afafd67d5525e5831c4144daad7f7b909eb25e7ee0f
- filename: src/projects/project.inputs.test.ts
checksum: 8242f4d40388f1a7f1d08c11d2571cb45a9294069a8248a5272bc5606807bb60
- filename: test/integration/projects-command-flows.test.ts
Expand Down Expand Up @@ -326,13 +326,13 @@ fileignoreconfig:
- filename: src/core/prompt.test.ts
checksum: 7be5497acf4cee870667751d44aa230e309784df76dc9e95d5ec2c4e6e6a57e0
- filename: test/integration/environments-update.test.ts
checksum: 4cd0106bf1881d373d23f063314627057d85cab0c43b793f013ffc2ca8859036
checksum: 249f27ad679b68f40eac3af90c616229838bceffad57ba508dd298ab053fa51a
- filename: src/commands/launch/env/update.ts
checksum: 06add97108d878eaf415e32160439b8963683e63a0e37ce77a56f66b88383c7a
- filename: src/commands/launch/env/update.test.ts
checksum: 2da219594e7bdd589adc0eff9ff035252a75aaa51dfa20514b18fd3266d7b752
- filename: README.md
checksum: b6af89e3dea468d56b666f4098a9b2224a5224ca45c9e94f7970d9e6674f3e6e
checksum: 916cd3b37e9c40a5d7d6d38437e279bbc573791588d4729ac741309134187724
allowed_patterns:
- "password-protection"
- "-p, --password"
Expand Down Expand Up @@ -517,4 +517,8 @@ fileignoreconfig:
checksum: 44ff11b9b4f3d7583d5faa4a2df45518c959eb4c29896cafe05ead19f215e4c7
- filename: src/commands/launch/secrets/unset.test.ts
checksum: a00e503cc66876fa1ef37eaab1a91e2e013af44d3add8ecd6fa08f269d550002
- filename: src/core/password-argv.ts
checksum: 22a5aa7d95c6a5113a034d2cc9af4f152f8a366f1c271f86cd317ea2046f5b1d
- filename: src/core/password-argv.test.ts
checksum: df87a19d09ff07b95d8118dd7e6bae5a13a86f0ecb8dce97e6022481d1e11256
version: "1.0"
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -518,11 +518,11 @@ Serve cloud functions

```
USAGE
$ csdx launch:functions:serve [-p <value>] [-d <value>]
$ csdx launch:functions:serve [--port <value>] [-d <value>]

FLAGS
-d, --data-dir=<value> Current working directory
-p, --port=<value> [default: 3000] Port number
--port=<value> [default: 3000] Port number

DESCRIPTION
Serve cloud functions
Expand All @@ -534,7 +534,7 @@ EXAMPLES

$ csdx launch:functions:serve --data-dir <path/of/current/working/dir>

$ csdx launch:functions:serve --data-dir <path/of/current/working/dir> -p <port-number>
$ csdx launch:functions:serve --data-dir <path/of/current/working/dir> --port <port-number>
```

_See code: [src/commands/launch/functions/serve.ts](https://github.com/contentstack/launch-cli/blob/v2.0.0-beta.0/src/commands/launch/functions/serve.ts)_
Expand Down
7 changes: 4 additions & 3 deletions src/commands/launch/functions/serve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ describe('launch:functions:serve flag definitions', () => {
'$ csdx launch:functions:serve',
'$ csdx launch:functions:serve --port <port-number>',
'$ csdx launch:functions:serve --data-dir <path/of/current/working/dir>',
'$ csdx launch:functions:serve --data-dir <path/of/current/working/dir> -p <port-number>',
'$ csdx launch:functions:serve --data-dir <path/of/current/working/dir> --port <port-number>',
]);
});

it('declares a port flag defaulting to 3000 and a data-dir flag', () => {
expect(Functions.flags.port).toMatchObject({ char: 'p', default: '3000', description: 'Port number' });
it('declares a port flag defaulting to 3000 with no short flag, and a data-dir flag', () => {
expect(Functions.flags.port).toMatchObject({ default: '3000', description: 'Port number' });
expect(Functions.flags.port.char).toBeUndefined();
expect(Functions.flags.port.env).toBeUndefined();
expect(Functions.flags['data-dir']).toMatchObject({ char: 'd', description: 'Current working directory' });
expect(Functions.flags['data-dir'].default).toBeUndefined();
Expand Down
2 changes: 1 addition & 1 deletion src/commands/launch/functions/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class Functions extends Command {
'$ csdx launch:functions:serve',
'$ csdx launch:functions:serve --port <port-number>',
'$ csdx launch:functions:serve --data-dir <path/of/current/working/dir>',
'$ csdx launch:functions:serve --data-dir <path/of/current/working/dir> -p <port-number>',
'$ csdx launch:functions:serve --data-dir <path/of/current/working/dir> --port <port-number>',
];

static flags = serveFlags;
Expand Down
10 changes: 10 additions & 0 deletions src/core/launch-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,16 @@ describe('LaunchCommand.init authentication gate', () => {
});
});

describe('LaunchCommand.init password on the command line', () => {
it('leaves -p and its value to the flag parser when the command has no --password flag', async () => {
const instance = new Probe(['-p', 'staging'], {} as never);
const parserRefusal = new Error('Nonexistent flag: -p');
(instance as unknown as { parse: jest.Mock }).parse = jest.fn().mockRejectedValue(parserRefusal);

await expect(instance.init()).rejects.toBe(parserRefusal);
});
});

describe('LaunchCommand.init terminal detection', () => {
it.each([[false], [undefined]])('resolves isTTY to false when process.stdin.isTTY is %p', async (isTTY) => {
const instance = probe();
Expand Down
5 changes: 5 additions & 0 deletions src/core/launch-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { AnyInputs, Resolved } from './inputs';
import { resolveInputsTraced } from './resolve';
import type { Rule } from './rules';
import { cancelOnInterrupt } from './interruptible-ux';
import { refusePasswordOnArgv } from './password-argv';
import type { UxLike } from './prompt';
import { registerSearchList } from './search-list';
import type { FailureOutput } from './failure';
Expand Down Expand Up @@ -119,6 +120,10 @@ export abstract class LaunchCommand<S extends AnyInputs = AnyInputs> extends Com
registerSearchList();
process.stdin.setMaxListeners(STDIN_MAX_LISTENERS);

if ('password' in this.contract.flags) {
refusePasswordOnArgv(this.argv);
}

const { flags } = await this.parse({
flags: this.contract.flags,
baseFlags: LaunchCommand.baseFlags,
Expand Down
38 changes: 38 additions & 0 deletions src/core/password-argv.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { randomBytes } from 'node:crypto';

import { UsageError } from './errors';
import { PASSWORD_ON_ARGV, refusePasswordOnArgv } from './password-argv';

const password = randomBytes(8).toString('hex');

describe('refusePasswordOnArgv', () => {
it.each([
['-p followed by a value', ['--env', 'staging', '-p', password]],
['--password followed by a value', ['--password', password, '--env', 'staging']],
['--password=<value>', [`--password=${password}`]],
['-p<value> written as one word', [`-p${password}`]],
])('refuses %s with a usage error that never repeats the value', (_form, argv) => {
const refusal = captured(() => refusePasswordOnArgv(argv));

expect(refusal).toBeInstanceOf(UsageError);
expect(refusal.message).toBe(PASSWORD_ON_ARGV);
});

it.each([
['-p last', ['--password-protection', 'enable', '-u', 'admin', '-p']],
['-p before another flag', ['-p', '--yes']],
['--password before another flag', ['--password', '-u', 'admin']],
])('lets %s through, so the password can come from the prompt or standard input', (_form, argv) => {
expect(() => refusePasswordOnArgv(argv)).not.toThrow();
});
});

function captured(act: () => void): Error {
try {
act();
} catch (error) {
return error as Error;
}

throw new Error('expected a refusal');
}
20 changes: 20 additions & 0 deletions src/core/password-argv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { UsageError } from './errors';

const PASSWORD_SWITCHES = ['-p', '--password'];

export const PASSWORD_ON_ARGV =
'--password takes no value. Enter the password at the hidden prompt, or pipe it on standard input.';

export function refusePasswordOnArgv(argv: readonly string[]): void {
if (argv.some((arg, index) => carriesPassword(arg, argv[index + 1]))) {
throw new UsageError(PASSWORD_ON_ARGV);
}
}

function carriesPassword(arg: string, next: string | undefined): boolean {
if (PASSWORD_SWITCHES.includes(arg)) {
return next !== undefined && !next.startsWith('-');
}

return arg.startsWith('--password=') || arg.startsWith('-p');
}
1 change: 0 additions & 1 deletion src/functions/function.inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export const DEFAULT_SERVE_PORT = '3000';

export const serveFlags = {
port: Flags.string({
char: 'p',
default: DEFAULT_SERVE_PORT,
description: 'Port number',
}),
Expand Down
13 changes: 11 additions & 2 deletions test/integration/environments-update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,19 @@ describe('integration: launch:env:update on the wire', () => {
expect(onWire).toEqual([]);
});

it('refuses a password value on the command line with exit 2 before any request', async () => {
const { error } = await runCommand(updateArgs('--password-protection', 'enable', '-p', 'on-argv'), config);
it('refuses a password value on the command line with exit 2 before any request, without echoing it', async () => {
const password = randomBytes(8).toString('hex');

const { error, stdout, stderr } = await runCommand(
updateArgs('--password-protection', 'enable', '-p', password),
config,
);

expect(error?.oclif?.exit).toBe(2);
expect(error?.message).toBe(
'--password takes no value. Enter the password at the hidden prompt, or pipe it on standard input.',
);
expect(`${stdout}${stderr}`).not.toContain(password);
expect(onWire).toEqual([]);
});

Expand Down
7 changes: 7 additions & 0 deletions test/integration/functions-serve-port.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,11 @@ describe('integration: launch:functions:serve port handling', () => {
expect(stderr).toBe('');
await expect(errorLog()).resolves.toBe(INVALID_PORT_RECORD);
});

it('rejects V1 -p as a nonexistent flag, so the port is only ever set with --port', async () => {
const { error } = await runCommand(['launch:functions:serve', '-p', '4000', '--data-dir', projectDir], config);

expect(error?.message).toContain('Nonexistent flag: -p');
expect(error?.oclif?.exit).toBe(2);
});
});
Loading