From bc8fcf1a662d12da8da35dff7f8160f66bf72f94 Mon Sep 17 00:00:00 2001 From: Aryan Jassal Date: Fri, 16 Aug 2024 13:03:26 +1000 Subject: [PATCH 1/3] feat: adding unix-like ls for secrets wip: working on adding unix-like ls support feat: added ls for listing vaults with long and short formatting [ci skip] feat: added option to show hidden files and removed redundant metadata [ci skip] feat: added directory traversal, showing hidden files by default [ci skip] chore: simplified pattern formatting code [ci skip] chore: updated long list formatting and added header option [ci skip] chore: updated long list formatting and header formatting [ci skip] chore: updated date formatter to handle old files chore: removed redundant directory rpc call and removed leading slashes from file paths [ci skip] chore: added error handling for invalid patterns [ci skip] chore: updated long list formatting using table formatter [ci skip] chore: added new parser for parsing secret filepaths [ci skip] chore: simplifying functionality for merging [ci skip] chore: cleaning up code [ci skip] chore: fixed tests chore: consolidating redundant maps fix: lint chore: updated tests and added edge cases fix: lint [ci skip] chore: updated parsers [ci skip] --- src/secrets/CommandList.ts | 42 ++++++++------ src/secrets/CommandSecrets.ts | 4 +- src/utils/parsers.ts | 52 ++++++++++++----- src/utils/utils.ts | 2 +- src/vaults/CommandCreate.ts | 7 ++- tests/secrets/list.test.ts | 106 ++++++++++++++++++++++++++++++++-- 6 files changed, 170 insertions(+), 43 deletions(-) diff --git a/src/secrets/CommandList.ts b/src/secrets/CommandList.ts index 641d071c..561dc360 100644 --- a/src/secrets/CommandList.ts +++ b/src/secrets/CommandList.ts @@ -3,18 +3,23 @@ import CommandPolykey from '../CommandPolykey'; import * as binUtils from '../utils'; import * as binOptions from '../utils/options'; import * as binProcessors from '../utils/processors'; +import * as binParsers from '../utils/parsers'; class CommandList extends CommandPolykey { constructor(...args: ConstructorParameters) { super(...args); - this.name('list'); - this.aliases(['ls']); - this.description('List all Available Secrets for a Vault'); - this.argument('', 'Name of the vault to list secrets from'); + this.name('ls'); + this.aliases(['list']); + this.description('List all secrets for a vault within a directory'); + this.argument( + '', + 'Directory to list files from, specified as [:]', + binParsers.parseSecretName, + ); this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); this.addOption(binOptions.clientPort); - this.action(async (vaultName, options) => { + this.action(async (vaultPattern, options) => { const { default: PolykeyClient } = await import( 'polykey/dist/PolykeyClient' ); @@ -40,39 +45,38 @@ class CommandList extends CommandPolykey { nodeId: clientOptions.nodeId, host: clientOptions.clientHost, port: clientOptions.clientPort, - options: { - nodePath: options.nodePath, - }, + options: { nodePath: options.nodePath }, logger: this.logger.getChild(PolykeyClient.name), }); - const data = await binUtils.retryAuthentication(async (auth) => { - const data: Array<{ secretName: string }> = []; + const secretPaths = await binUtils.retryAuthentication(async (auth) => { + const secretPaths: Array = []; const stream = await pkClient.rpcClient.methods.vaultsSecretsList({ metadata: auth, - nameOrId: vaultName, + nameOrId: vaultPattern[0], + secretName: vaultPattern[1] ?? '/', }); for await (const secret of stream) { - data.push({ - secretName: secret.secretName, - }); + // Remove leading slashes + if (secret.path.startsWith('/')) { + secret.path = secret.path.substring(1); + } + secretPaths.push(secret.path); } - return data; + return secretPaths; }, auth); if (options.format === 'json') { process.stdout.write( binUtils.outputFormatter({ type: 'json', - data: data, + data: secretPaths, }), ); } else { process.stdout.write( binUtils.outputFormatter({ type: 'list', - data: data.map( - (secretsListMessage) => secretsListMessage.secretName, - ), + data: secretPaths, }), ); } diff --git a/src/secrets/CommandSecrets.ts b/src/secrets/CommandSecrets.ts index fe24384a..183fbf5f 100644 --- a/src/secrets/CommandSecrets.ts +++ b/src/secrets/CommandSecrets.ts @@ -8,7 +8,7 @@ import CommandList from './CommandList'; import CommandMkdir from './CommandMkdir'; import CommandRename from './CommandRename'; import CommandUpdate from './CommandUpdate'; -import commandStat from './CommandStat'; +import CommandStat from './CommandStat'; import CommandPolykey from '../CommandPolykey'; class CommandSecrets extends CommandPolykey { @@ -26,7 +26,7 @@ class CommandSecrets extends CommandPolykey { this.addCommand(new CommandMkdir(...args)); this.addCommand(new CommandRename(...args)); this.addCommand(new CommandUpdate(...args)); - this.addCommand(new commandStat(...args)); + this.addCommand(new CommandStat(...args)); } } diff --git a/src/utils/parsers.ts b/src/utils/parsers.ts index 376499d1..719affa2 100644 --- a/src/utils/parsers.ts +++ b/src/utils/parsers.ts @@ -8,7 +8,8 @@ import * as gestaltsUtils from 'polykey/dist/gestalts/utils'; import * as networkUtils from 'polykey/dist/network/utils'; import * as nodesUtils from 'polykey/dist/nodes/utils'; -const secretPathRegex = /^([\w-]+)(?::)([^\0\\=]+)$/; +const vaultNameRegex = /^[\w.-]+$/; +const secretPathNameRegex = /^([\w-]+)(?::([^\0\\=]+))?$/; const secretPathValueRegex = /^([a-zA-Z_][\w]+)?$/; const environmentVariableRegex = /^([a-zA-Z_]+[a-zA-Z0-9_]*)?$/; @@ -65,27 +66,47 @@ function parseCoreCount(v: string): number | undefined { } } +function parseVaultName(vaultName: string): string { + // E.g. If 'vault1, 'vault1' is returned + // If 'vault1:a/b/c', an error is thrown + if (!vaultNameRegex.test(vaultName)) { + throw new commander.InvalidArgumentError( + `${vaultName} is not of the format `, + ); + } + // Returns match[1], or the parsed vaultName + return vaultName.match(secretPathNameRegex)![1]; +} + +function parseSecretName(secretPath: string): [string, string?] { + // E.g. If 'vault1:a/b/c', ['vault1', 'a/b/c'] is returned + // If 'vault1', ['vault1, undefined] is returned + if (!secretPathNameRegex.test(secretPath)) { + throw new commander.InvalidArgumentError( + `${secretPath} is not of the format [:]`, + ); + } + // Returns [vaultName, secretName?] + const match = secretPath.match(secretPathNameRegex)!; + return [match[1], match[2] || undefined]; +} + function parseSecretPath(secretPath: string): [string, string, string?] { // E.g. If 'vault1:a/b/c', ['vault1', 'a/b/c'] is returned - // If 'vault1:a/b/c=VARIABLE', ['vault1, 'a/b/c', 'VARIABLE'] is returned - const lastEqualIndex = secretPath.lastIndexOf('='); - const splitSecretPath = - lastEqualIndex === -1 - ? secretPath - : secretPath.substring(0, lastEqualIndex); - const value = - lastEqualIndex === -1 ? '' : secretPath.substring(lastEqualIndex + 1); - if (!secretPathRegex.test(splitSecretPath)) { + // If 'vault1', an error is thrown + const [vaultName, secretName] = parseSecretName(secretPath); + if (secretName === undefined) { throw new commander.InvalidArgumentError( - `${splitSecretPath} is not of the format :`, + `${secretPath} is not of the format :`, ); } - const [, vaultName, directoryPath] = splitSecretPath.match(secretPathRegex)!; - return [vaultName, directoryPath, value]; + return [vaultName, secretName]; } function parseSecretPathValue(secretPath: string): [string, string, string?] { - const [vaultName, directoryPath, value] = parseSecretPath(secretPath); + const [vaultName, directoryPath] = parseSecretPath(secretPath); + const lastEqualIndex = secretPath.lastIndexOf('='); + const value = lastEqualIndex === -1 ? '' : secretPath.substring(lastEqualIndex + 1); if (value != null && !secretPathValueRegex.test(value)) { throw new commander.InvalidArgumentError( `${value} is not a valid value name`, @@ -198,12 +219,13 @@ function parseEnvArgs( } export { - secretPathRegex, secretPathValueRegex, environmentVariableRegex, validateParserToArgParser, validateParserToArgListParser, parseCoreCount, + parseVaultName, + parseSecretName, parseSecretPath, parseSecretPathValue, parseSecretPathEnv, diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 2e97340e..b2ac620d 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -254,7 +254,7 @@ function outputFormatterList(items: Array): string { * If it is `Record`, the `number` values will be used as the initial padding lengths. * The object is also mutated if any cells exceed the initial padding lengths. * This parameter can also be supplied to filter the columns that will be displayed. - * @param options.includeHeaders - Defaults to `True` + * @param options.includeHeaders - Defaults to `True`. * @param options.includeRowCount - Defaults to `False`. * @returns */ diff --git a/src/vaults/CommandCreate.ts b/src/vaults/CommandCreate.ts index 4624955b..163b37ac 100644 --- a/src/vaults/CommandCreate.ts +++ b/src/vaults/CommandCreate.ts @@ -4,6 +4,7 @@ import CommandPolykey from '../CommandPolykey'; import * as binUtils from '../utils'; import * as binOptions from '../utils/options'; import * as binProcessors from '../utils/processors'; +import * as binParsers from '../utils/parsers'; class CommandCreate extends CommandPolykey { constructor(...args: ConstructorParameters) { @@ -11,7 +12,11 @@ class CommandCreate extends CommandPolykey { this.name('create'); this.aliases(['touch']); this.description('Create a new Vault'); - this.argument('', 'Name of the new vault to be created'); + this.argument( + '', + 'Name of the new vault to be created', + binParsers.parseVaultName, + ); this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); this.addOption(binOptions.clientPort); diff --git a/tests/secrets/list.test.ts b/tests/secrets/list.test.ts index 406fd008..648aed70 100644 --- a/tests/secrets/list.test.ts +++ b/tests/secrets/list.test.ts @@ -41,6 +41,21 @@ describe('commandListSecrets', () => { }); }); + test( + 'should fail when vault does not exist', + async () => { + command = ['secrets', 'ls', '-np', dataDir, 'DoesntExist']; + const result = await testUtils.pkStdio([...command], { + env: { + PK_PASSWORD: password, + }, + cwd: dataDir, + }); + expect(result.exitCode).toBe(64); // Sysexits.USAGE + }, + globalThis.defaultTimeout * 2, + ); + test( 'should list secrets', async () => { @@ -48,13 +63,12 @@ describe('commandListSecrets', () => { const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret(vault, 'MySecret1', 'this is the secret 1'); - await vaultOps.addSecret(vault, 'MySecret2', 'this is the secret 2'); - await vaultOps.addSecret(vault, 'MySecret3', 'this is the secret 3'); + await vaultOps.addSecret(vault, 'MySecret1', ''); + await vaultOps.addSecret(vault, 'MySecret2', ''); + await vaultOps.addSecret(vault, 'MySecret3', ''); }); - command = ['secrets', 'list', '-np', dataDir, vaultName]; - + command = ['secrets', 'ls', '-np', dataDir, vaultName]; const result = await testUtils.pkStdio([...command], { env: { PK_PASSWORD: password, @@ -62,6 +76,88 @@ describe('commandListSecrets', () => { cwd: dataDir, }); expect(result.exitCode).toBe(0); + expect(result.stdout).toBe('MySecret1\nMySecret2\nMySecret3\n'); + }, + globalThis.defaultTimeout * 2, + ); + + test( + 'should fail when path is not a directory', + async () => { + const vaultName = 'Vault5' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.mkdir(vault, 'SecretDir'); + await vaultOps.addSecret(vault, 'SecretDir/MySecret1', ''); + await vaultOps.addSecret(vault, 'SecretDir/MySecret2', ''); + await vaultOps.addSecret(vault, 'SecretDir/MySecret3', ''); + }); + + command = ['secrets', 'ls', '-np', dataDir, `${vaultName}:WrongDirName`]; + let result = await testUtils.pkStdio([...command], { + env: { + PK_PASSWORD: password, + }, + cwd: dataDir, + }); + expect(result.exitCode).toBe(64); + + command = [ + 'secrets', + 'ls', + '-np', + dataDir, + `${vaultName}:SecretDir/MySecret1`, + ]; + result = await testUtils.pkStdio([...command], { + env: { + PK_PASSWORD: password, + }, + cwd: dataDir, + }); + expect(result.exitCode).toBe(64); + }, + globalThis.defaultTimeout * 2, + ); + + test( + 'should list secrets within directories', + async () => { + const vaultName = 'Vault6' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.mkdir(vault, 'SecretDir/NestedDir', { recursive: true }); + await vaultOps.addSecret(vault, 'SecretDir/MySecret1', ''); + await vaultOps.addSecret(vault, 'SecretDir/NestedDir/MySecret2', ''); + }); + + command = ['secrets', 'ls', '-np', dataDir, `${vaultName}:SecretDir`]; + let result = await testUtils.pkStdio([...command], { + env: { + PK_PASSWORD: password, + }, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + expect(result.stdout).toBe('SecretDir/MySecret1\nSecretDir/NestedDir\n'); + + command = [ + 'secrets', + 'ls', + '-np', + dataDir, + `${vaultName}:SecretDir/NestedDir`, + ]; + result = await testUtils.pkStdio([...command], { + env: { + PK_PASSWORD: password, + }, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + expect(result.stdout).toBe('SecretDir/NestedDir/MySecret2\n'); }, globalThis.defaultTimeout * 2, ); From b7871a45ace9dede05a4c557fcec51476bee3198 Mon Sep 17 00:00:00 2001 From: Brian Botha Date: Fri, 30 Aug 2024 13:27:38 +1000 Subject: [PATCH 2/3] chore: chore: updated `polykey` to `^1.10.0` [ci skip] --- npmDepsHash | 2 +- package-lock.json | 12 ++++++------ package.json | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/npmDepsHash b/npmDepsHash index 76808a7a..7b3f322a 100644 --- a/npmDepsHash +++ b/npmDepsHash @@ -1 +1 @@ -sha256-NF/FIaXGr8EAxGRp+e9KP9+Vcb3KFzJBir/bZqU0J+w= +sha256-N6qZhhoqgktogCaRmQO1/9dFbSL9SISI5y/37kjD95U= diff --git a/package-lock.json b/package-lock.json index 67b9ca2b..73161c83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "polykey-cli", - "version": "0.6.5", + "version": "0.6.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "polykey-cli", - "version": "0.6.5", + "version": "0.6.3", "license": "GPL-3.0", "bin": { "pk": "dist/polykey.js", @@ -42,7 +42,7 @@ "nexpect": "^0.6.0", "node-gyp-build": "^4.4.0", "nodemon": "^3.0.1", - "polykey": "^1.9.0", + "polykey": "^1.10.0", "prettier": "^3.0.0", "shelljs": "^0.8.5", "shx": "^0.3.4", @@ -7602,9 +7602,9 @@ } }, "node_modules/polykey": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/polykey/-/polykey-1.9.0.tgz", - "integrity": "sha512-ijdunJR66xylrA2g408zItwPKNEh5wiofk9Fj1q8QRuV1ZHsrVl1q+4/mxQXvpDmAq7fOH3YppwvD52oZd9o+w==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/polykey/-/polykey-1.10.0.tgz", + "integrity": "sha512-An0lrW2KK+zO0J1Ig7j5cZ7ZwaEc1XGdsCNx0bx8a7UxusBc+A76WtdLuEhH27yiVNfBVgruMny+4htTE8uDiw==", "dev": true, "dependencies": { "@matrixai/async-cancellable": "^1.1.1", diff --git a/package.json b/package.json index 2d92cfe6..3b99c7d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "polykey-cli", - "version": "0.6.5", + "version": "0.6.3", "homepage": "https://polykey.com", "author": "Roger Qiu", "contributors": [ @@ -150,7 +150,7 @@ "nexpect": "^0.6.0", "node-gyp-build": "^4.4.0", "nodemon": "^3.0.1", - "polykey": "^1.9.0", + "polykey": "^1.10.0", "prettier": "^3.0.0", "shelljs": "^0.8.5", "shx": "^0.3.4", From c2d70c170583992e6fc92b481f9c2f67654e8390 Mon Sep 17 00:00:00 2001 From: Brian Botha Date: Fri, 30 Aug 2024 13:38:25 +1000 Subject: [PATCH 3/3] fix: fixed parsing secret path not parsing the optional value --- src/secrets/CommandList.ts | 2 +- src/utils/parsers.ts | 52 +++++++++++++++++-------------------- src/vaults/CommandCreate.ts | 7 +---- tests/secrets/env.test.ts | 3 +-- tests/utils/utils.ts | 6 ++--- 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/src/secrets/CommandList.ts b/src/secrets/CommandList.ts index 561dc360..ba88511d 100644 --- a/src/secrets/CommandList.ts +++ b/src/secrets/CommandList.ts @@ -14,7 +14,7 @@ class CommandList extends CommandPolykey { this.argument( '', 'Directory to list files from, specified as [:]', - binParsers.parseSecretName, + binParsers.parseSecretPathOptional, ); this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); diff --git a/src/utils/parsers.ts b/src/utils/parsers.ts index 719affa2..b0d026fe 100644 --- a/src/utils/parsers.ts +++ b/src/utils/parsers.ts @@ -8,8 +8,7 @@ import * as gestaltsUtils from 'polykey/dist/gestalts/utils'; import * as networkUtils from 'polykey/dist/network/utils'; import * as nodesUtils from 'polykey/dist/nodes/utils'; -const vaultNameRegex = /^[\w.-]+$/; -const secretPathNameRegex = /^([\w-]+)(?::([^\0\\=]+))?$/; +const secretPathRegex = /^([\w-]+)(?::([^\0\\=]+))?$/; const secretPathValueRegex = /^([a-zA-Z_][\w]+)?$/; const environmentVariableRegex = /^([a-zA-Z_]+[a-zA-Z0-9_]*)?$/; @@ -66,47 +65,44 @@ function parseCoreCount(v: string): number | undefined { } } -function parseVaultName(vaultName: string): string { - // E.g. If 'vault1, 'vault1' is returned - // If 'vault1:a/b/c', an error is thrown - if (!vaultNameRegex.test(vaultName)) { - throw new commander.InvalidArgumentError( - `${vaultName} is not of the format `, - ); - } - // Returns match[1], or the parsed vaultName - return vaultName.match(secretPathNameRegex)![1]; -} - -function parseSecretName(secretPath: string): [string, string?] { +function parseSecretPathOptional( + secretPath: string, +): [string, string?, string?] { // E.g. If 'vault1:a/b/c', ['vault1', 'a/b/c'] is returned // If 'vault1', ['vault1, undefined] is returned - if (!secretPathNameRegex.test(secretPath)) { + // splits out everything after an `=` separator + const lastEqualIndex = secretPath.lastIndexOf('='); + const splitSecretPath = + lastEqualIndex === -1 + ? secretPath + : secretPath.substring(0, lastEqualIndex); + const value = + lastEqualIndex === -1 + ? undefined + : secretPath.substring(lastEqualIndex + 1); + if (!secretPathRegex.test(splitSecretPath)) { throw new commander.InvalidArgumentError( - `${secretPath} is not of the format [:]`, + `${secretPath} is not of the format [:][=]`, ); } - // Returns [vaultName, secretName?] - const match = secretPath.match(secretPathNameRegex)!; - return [match[1], match[2] || undefined]; + const [, vaultName, directoryPath] = splitSecretPath.match(secretPathRegex)!; + return [vaultName, directoryPath, value]; } function parseSecretPath(secretPath: string): [string, string, string?] { // E.g. If 'vault1:a/b/c', ['vault1', 'a/b/c'] is returned // If 'vault1', an error is thrown - const [vaultName, secretName] = parseSecretName(secretPath); + const [vaultName, secretName, value] = parseSecretPathOptional(secretPath); if (secretName === undefined) { throw new commander.InvalidArgumentError( - `${secretPath} is not of the format :`, + `${secretPath} is not of the format :[=]`, ); } - return [vaultName, secretName]; + return [vaultName, secretName, value]; } function parseSecretPathValue(secretPath: string): [string, string, string?] { - const [vaultName, directoryPath] = parseSecretPath(secretPath); - const lastEqualIndex = secretPath.lastIndexOf('='); - const value = lastEqualIndex === -1 ? '' : secretPath.substring(lastEqualIndex + 1); + const [vaultName, directoryPath, value] = parseSecretPath(secretPath); if (value != null && !secretPathValueRegex.test(value)) { throw new commander.InvalidArgumentError( `${value} is not a valid value name`, @@ -219,13 +215,13 @@ function parseEnvArgs( } export { + secretPathRegex, secretPathValueRegex, environmentVariableRegex, validateParserToArgParser, validateParserToArgListParser, parseCoreCount, - parseVaultName, - parseSecretName, + parseSecretPathOptional, parseSecretPath, parseSecretPathValue, parseSecretPathEnv, diff --git a/src/vaults/CommandCreate.ts b/src/vaults/CommandCreate.ts index 163b37ac..4624955b 100644 --- a/src/vaults/CommandCreate.ts +++ b/src/vaults/CommandCreate.ts @@ -4,7 +4,6 @@ import CommandPolykey from '../CommandPolykey'; import * as binUtils from '../utils'; import * as binOptions from '../utils/options'; import * as binProcessors from '../utils/processors'; -import * as binParsers from '../utils/parsers'; class CommandCreate extends CommandPolykey { constructor(...args: ConstructorParameters) { @@ -12,11 +11,7 @@ class CommandCreate extends CommandPolykey { this.name('create'); this.aliases(['touch']); this.description('Create a new Vault'); - this.argument( - '', - 'Name of the new vault to be created', - binParsers.parseVaultName, - ); + this.argument('', 'Name of the new vault to be created'); this.addOption(binOptions.nodeId); this.addOption(binOptions.clientHost); this.addOption(binOptions.clientPort); diff --git a/tests/secrets/env.test.ts b/tests/secrets/env.test.ts index af560777..103860c5 100644 --- a/tests/secrets/env.test.ts +++ b/tests/secrets/env.test.ts @@ -764,7 +764,7 @@ describe('commandEnv', () => { const jsonOut = JSON.parse(result.stdout); expect(jsonOut['SECRET']).toBe('this is a secret\nit has multiple lines\n'); }); - test.only.prop([ + test.prop([ testUtils.secretPathEnvArrayArb, fc.string().noShrink(), testUtils.cmdArgsArrayArb, @@ -773,7 +773,6 @@ describe('commandEnv', () => { async (secretPathEnvArray, cmd, cmdArgsArray) => { // If we don't use the optional `--` delimiter then we can't include `:` in vault names fc.pre(!cmd.includes(':')); - let output: | [Array<[string, string, string?]>, Array] | undefined = undefined; diff --git a/tests/utils/utils.ts b/tests/utils/utils.ts index 4599b4f2..a9fa9eb5 100644 --- a/tests/utils/utils.ts +++ b/tests/utils/utils.ts @@ -88,9 +88,9 @@ async function nodesConnect(localNode: PolykeyAgent, remoteNode: PolykeyAgent) { ); } -const secretPathWithoutEnvArb = fc - .stringMatching(binParsers.secretPathRegex) - .noShrink(); +// This regex defines a vault secret path that always includes the secret path +const secretPathRegex = /^([\w-]+)(?::)([^\0\\=]+)$/; +const secretPathWithoutEnvArb = fc.stringMatching(secretPathRegex).noShrink(); const environmentVariableAre = fc .stringMatching(binParsers.environmentVariableRegex) .filter((v) => v.length > 0)