Skip to content

Commit 1ec36ab

Browse files
committed
feat: allowing to pass in vault name without path for secrets env
feat: fixed secrets env to allow vault names without secret path fix: actually allow vault to export all secrets by default fix: build chore: changed vaultName:. to vaultName in tests
1 parent 3d6ec55 commit 1ec36ab

14 files changed

+424
-278
lines changed

src/secrets/CommandEnv.ts

Lines changed: 229 additions & 222 deletions
Large diffs are not rendered by default.

src/utils/options.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,13 @@ const envVariables = new commander.Option('-e --env <envs...>', 'specify envs')
214214
.argParser(
215215
(value: string, previous: Array<[string, string, string?]> | undefined) => {
216216
const acc = previous ?? [];
217-
acc.push(binParsers.parseSecretPathEnv(value));
217+
const [vault, secret, val] = binParsers.parseSecretPathEnv(value);
218+
if (secret == null) {
219+
throw new commander.InvalidArgumentError(
220+
'You must provide at least one secret path',
221+
);
222+
}
223+
acc.push([vault, secret, val]);
218224
return acc;
219225
},
220226
);

src/utils/parsers.ts

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import * as gestaltsUtils from 'polykey/dist/gestalts/utils';
88
import * as networkUtils from 'polykey/dist/network/utils';
99
import * as nodesUtils from 'polykey/dist/nodes/utils';
1010

11-
const secretPathRegex = /^([\w-]+)(?::([^\0\\=]+))?$/;
11+
const vaultNameRegex = /^(?!.*[:])[ -~\t\n]*$/s;
12+
const secretPathRegex = /^(?!.*[=])[ -~\t\n]*$/s;
13+
const vaultNameSecretPathRegex = /^([\w\-\.]+)(?::([^\0\\=]+))?$/;
1214
const secretPathValueRegex = /^([a-zA-Z_][\w]+)?$/;
1315
const environmentVariableRegex = /^([a-zA-Z_]+[a-zA-Z0-9_]*)?$/;
1416

@@ -80,15 +82,26 @@ function parseSecretPathOptional(
8082
lastEqualIndex === -1
8183
? undefined
8284
: secretPath.substring(lastEqualIndex + 1);
83-
if (!secretPathRegex.test(splitSecretPath)) {
85+
if (!vaultNameSecretPathRegex.test(splitSecretPath)) {
8486
throw new commander.InvalidArgumentError(
8587
`${secretPath} is not of the format <vaultName>[:<directoryPath>][=<value>]`,
8688
);
8789
}
88-
const [, vaultName, directoryPath] = splitSecretPath.match(secretPathRegex)!;
90+
const [, vaultName, directoryPath] = splitSecretPath.match(
91+
vaultNameSecretPathRegex,
92+
)!;
8993
return [vaultName, directoryPath, value];
9094
}
9195

96+
function parseVaultName(vaultName: string): string {
97+
if (!vaultNameRegex.test(vaultName)) {
98+
throw new commander.InvalidArgumentError(
99+
`${vaultName} is not a valid vault name`,
100+
);
101+
}
102+
return vaultName;
103+
}
104+
92105
function parseSecretPath(secretPath: string): [string, string, string?] {
93106
// E.g. If 'vault1:a/b/c', ['vault1', 'a/b/c'] is returned
94107
// If 'vault1', an error is thrown
@@ -111,8 +124,37 @@ function parseSecretPathValue(secretPath: string): [string, string, string?] {
111124
return [vaultName, directoryPath, value];
112125
}
113126

114-
function parseSecretPathEnv(secretPath: string): [string, string, string?] {
115-
const [vaultName, directoryPath, value] = parseSecretPath(secretPath);
127+
function parseSecretPathEnv(secretPath: string): [string, string?, string?] {
128+
// The colon character `:` is prohibited in vaultName, so it's first occurence
129+
// means that this is the delimiter between vaultName and secretPath.
130+
const colonIndex = secretPath.indexOf(':');
131+
// If no colon exists, treat entire string as vault name
132+
if (colonIndex === -1) {
133+
return [parseVaultName(secretPath), undefined, undefined];
134+
}
135+
// Calculate contents before the `=` separator
136+
const vaultNamePart = secretPath.substring(0, colonIndex);
137+
const secretPathPart = secretPath.substring(colonIndex + 1);
138+
// Calculate contents after the `=` separator
139+
const equalIndex = secretPathPart.indexOf('=');
140+
const splitSecretPath =
141+
equalIndex === -1
142+
? secretPathPart
143+
: secretPathPart.substring(0, equalIndex);
144+
const valueData =
145+
equalIndex === -1 ? undefined : secretPathPart.substring(equalIndex + 1);
146+
if (splitSecretPath != null && !secretPathRegex.test(splitSecretPath)) {
147+
throw new commander.InvalidArgumentError(
148+
`${secretPath} is not of the format <vaultName>[:<secretPath>][=<value>]`,
149+
);
150+
}
151+
const parsedVaultName = parseVaultName(vaultNamePart);
152+
const parsedSecretPath = splitSecretPath.match(secretPathRegex)?.[0] ?? '/';
153+
const [vaultName, directoryPath, value] = [
154+
parsedVaultName,
155+
parsedSecretPath,
156+
valueData,
157+
];
116158
if (value != null && !environmentVariableRegex.test(value)) {
117159
throw new commander.InvalidArgumentError(
118160
`${value} is not a valid environment variable name`,
@@ -182,25 +224,24 @@ const parseSeedNodes: (data: string) => [SeedNodes, boolean] =
182224

183225
/**
184226
* This parses the arguments used for the env command. It should be formatted as
185-
* <secretPath...> [--] [cmd] [cmdArgs...]
186-
* The cmd part of the list is separated in two ways, either the user explicitly uses `--` or the first non-secret path separates it.
227+
* <secretPath...> [-- cmd [cmdArgs...]]
228+
* The cmd part of the list is separated by using `--`.
187229
*/
188230
function parseEnvArgs(
189231
value: string,
190-
prev: [Array<[string, string, string?]>, Array<string>] | undefined,
191-
): [Array<[string, string, string?]>, Array<string>] {
192-
const current: [Array<[string, string, string?]>, Array<string>] = prev ?? [
232+
prev: [Array<[string, string?, string?]>, Array<string>] | undefined,
233+
): [Array<[string, string?, string?]>, Array<string>] {
234+
const current: [Array<[string, string?, string?]>, Array<string>] = prev ?? [
193235
[],
194236
[],
195237
];
196238
if (current[1].length === 0) {
197239
// Parse a secret path
198-
try {
240+
if (value !== '--') {
199241
current[0].push(parseSecretPathEnv(value));
200-
} catch (e) {
201-
if (!(e instanceof commander.InvalidArgumentError)) throw e;
202-
// If we get an invalid argument error then we switch over to parsing args verbatim
242+
} else {
203243
current[1].push(value);
244+
return current;
204245
}
205246
} else {
206247
// Otherwise we just have the cmd args
@@ -215,13 +256,15 @@ function parseEnvArgs(
215256
}
216257

217258
export {
259+
vaultNameRegex,
218260
secretPathRegex,
219261
secretPathValueRegex,
220262
environmentVariableRegex,
221263
validateParserToArgParser,
222264
validateParserToArgListParser,
223265
parseCoreCount,
224266
parseSecretPathOptional,
267+
parseVaultName,
225268
parseSecretPath,
226269
parseSecretPathValue,
227270
parseSecretPathEnv,

src/vaults/CommandClone.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ class CommandClone extends CommandPolykey {
1111
super(...args);
1212
this.name('clone');
1313
this.description('Clone a Vault from Another Node');
14-
this.argument('<vaultNameOrId>', 'Name or Id of the vault to be cloned');
14+
this.argument(
15+
'<vaultName>',
16+
'Name of the vault to be cloned',
17+
binParsers.parseVaultName,
18+
);
1519
this.argument(
1620
'<nodeId>',
1721
'Id of the node to clone the vault from',

src/vaults/CommandCreate.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@ import CommandPolykey from '../CommandPolykey';
44
import * as binUtils from '../utils';
55
import * as binOptions from '../utils/options';
66
import * as binProcessors from '../utils/processors';
7+
import * as binParsers from '../utils/parsers';
78

89
class CommandCreate extends CommandPolykey {
910
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
1011
super(...args);
1112
this.name('create');
1213
this.aliases(['touch']);
1314
this.description('Create a new Vault');
14-
this.argument('<vaultName>', 'Name of the new vault to be created');
15+
this.argument(
16+
'<vaultName>',
17+
'Name of the new vault to be created',
18+
binParsers.parseVaultName,
19+
);
1520
this.addOption(binOptions.nodeId);
1621
this.addOption(binOptions.clientHost);
1722
this.addOption(binOptions.clientPort);

src/vaults/CommandDelete.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ import CommandPolykey from '../CommandPolykey';
33
import * as binUtils from '../utils';
44
import * as binOptions from '../utils/options';
55
import * as binProcessors from '../utils/processors';
6+
import * as binParsers from '../utils/parsers';
67

78
class CommandDelete extends CommandPolykey {
89
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
910
super(...args);
1011
this.name('delete');
1112
this.description('Delete an Existing Vault');
12-
this.argument('<vaultName>', 'Name of the vault to be deleted');
13+
this.argument(
14+
'<vaultName>',
15+
'Name of the vault to be deleted',
16+
binParsers.parseVaultName,
17+
);
1318
this.addOption(binOptions.nodeId);
1419
this.addOption(binOptions.clientHost);
1520
this.addOption(binOptions.clientPort);

src/vaults/CommandLog.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ import CommandPolykey from '../CommandPolykey';
44
import * as binUtils from '../utils';
55
import * as binOptions from '../utils/options';
66
import * as binProcessors from '../utils/processors';
7+
import * as binParsers from '../utils/parsers';
78

89
class CommandLog extends CommandPolykey {
910
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
1011
super(...args);
1112
this.name('log');
1213
this.description('Get the Version History of a Vault');
13-
this.argument('<vaultName>', 'Name of the vault to obtain the log from');
14+
this.argument(
15+
'<vaultName>',
16+
'Name of the vault to obtain the log from',
17+
binParsers.parseVaultName,
18+
);
1419
this.addOption(binOptions.commitId);
1520
this.addOption(binOptions.depth);
1621
this.addOption(binOptions.nodeId);

src/vaults/CommandPermissions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import * as binProcessors from '../utils/processors';
33
import * as binUtils from '../utils';
44
import CommandPolykey from '../CommandPolykey';
55
import * as binOptions from '../utils/options';
6+
import * as binParsers from '../utils/parsers';
67

78
class CommandPermissions extends CommandPolykey {
89
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
910
super(...args);
1011
this.name('permissions');
1112
this.alias('perms');
1213
this.description('Sets the permissions of a vault for Node Ids');
13-
this.argument('<vaultName>', 'Name or ID of the vault');
14+
this.argument('<vaultName', 'Name of the vault', binParsers.parseVaultName);
1415
this.addOption(binOptions.nodeId);
1516
this.addOption(binOptions.clientHost);
1617
this.addOption(binOptions.clientPort);

src/vaults/CommandPull.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ class CommandPull extends CommandPolykey {
1111
super(...args);
1212
this.name('pull');
1313
this.description('Pull a Vault from Another Node');
14-
this.argument('<vaultNameOrId>', 'Name of the vault to be pulled into');
14+
this.argument(
15+
'<vaultName>',
16+
'Name of the vault to be pulled into',
17+
binParsers.parseVaultName,
18+
);
1519
this.argument(
1620
'[targetNodeId]',
1721
'(Optional) target node to pull from',

src/vaults/CommandRename.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,23 @@ import CommandPolykey from '../CommandPolykey';
33
import * as binUtils from '../utils';
44
import * as binOptions from '../utils/options';
55
import * as binProcessors from '../utils/processors';
6+
import * as binParsers from '../utils/parsers';
67

78
class CommandRename extends CommandPolykey {
89
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
910
super(...args);
1011
this.name('rename');
1112
this.description('Rename an Existing Vault');
12-
this.argument('<vaultName>', 'Name of the vault to be renamed');
13-
this.argument('<newVaultName>', 'New name of the vault');
13+
this.argument(
14+
'<vaultName>',
15+
'Name of the vault to be renamed',
16+
binParsers.parseVaultName,
17+
);
18+
this.argument(
19+
'<newVaultName>',
20+
'New name of the vault',
21+
binParsers.parseVaultName,
22+
);
1423
this.addOption(binOptions.nodeId);
1524
this.addOption(binOptions.clientHost);
1625
this.addOption(binOptions.clientPort);

0 commit comments

Comments
 (0)