Skip to content

Commit

Permalink
KSM-542 added escape char '\' for dots in title for Powershell (#630)
Browse files Browse the repository at this point in the history
  • Loading branch information
idimov-keeper authored Jul 30, 2024
1 parent 0659820 commit 924fb89
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ function Get-Config {
[string] $LocalVaultName
)
$vaults = Microsoft.Powershell.SecretManagement\Get-SecretVault
$localVault = $vaults.Where( { $_.Name -eq $LocalVaultName } )
$localVault = $vaults.Where( { $_.Name -eq $LocalVaultName } ) # SecretStore/LocalStore
if (!$localVault) {
return $null
}

$moduleInstance = Import-Module -Name $localVault.ModuleName -PassThru
$configSecretName = 'KeeperVault.' + $VaultName
$configSecretName = 'KeeperVault.' + $VaultName # passed by SecretStore while enumerating registered vaults
$config = & $moduleInstance Get-Secret -Name $configSecretName -VaultName $localVault.Name
if ($config -isnot [Hashtable]) {
if ($config -isnot [Hashtable]) {
$config = $config[0] # SecretStore returns a List
}
return $config
Expand Down
14 changes: 12 additions & 2 deletions sdk/dotNet/SecretManagement.Keeper/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ if ($Package) {
}

@(
'./bin/Release/netstandard2.0/SecretManagement.Keeper.dll'
'./bin/Release/netstandard2.0/SecretsManager.dll'
'./bin/Release/netstandard2.0/SecretManagement.Keeper.dll'
'./bin/Release/netstandard2.0/SecretManagement.Keeper.deps.json'
'./bin/Release/netstandard2.0/BouncyCastle.Cryptography.dll'
'./bin/Release/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll'
'./bin/Release/netstandard2.0/System.Buffers.dll'
'./bin/Release/netstandard2.0/System.Management.Automation.dll'
'./bin/Release/netstandard2.0/System.Memory.dll'
'./bin/Release/netstandard2.0/System.Numerics.Vectors.dll'
'./bin/Release/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll'
'./bin/Release/netstandard2.0/System.Text.Encodings.Web.dll'
'./bin/Release/netstandard2.0/System.Text.Json.dll'
'./bin/Release/netstandard2.0/System.Threading.Tasks.Extensions.dll'
) | ForEach-Object {
Copy-Item -Path $_ -Destination $outDir -Force
}
Expand All @@ -45,4 +55,4 @@ if ($Publish) {
Publish-Module -Path ./out/SecretManagement.Keeper -NuGetApiKey $APIKey -Verbose
}

Pop-Location
Pop-Location
53 changes: 49 additions & 4 deletions sdk/dotNet/SecretManagement.Keeper/src/SecretManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

Expand All @@ -19,7 +21,7 @@ public static async Task<KeeperResult> GetVaultConfigFromToken(string oneTimeTok
SecretsManagerClient.InitializeStorage(storage, oneTimeToken);
try
{
await SecretsManagerClient.GetSecrets(new SecretsManagerOptions(storage));
await SecretsManagerClient.GetSecrets(new SecretsManagerOptions(storage), new string[] { new string('A', 22) });
}
catch (Exception e)
{
Expand All @@ -34,7 +36,7 @@ public static async Task<KeeperResult> GetVaultConfigFromConfigString(string con
var storage = new InMemoryStorage(config);
try
{
await SecretsManagerClient.GetSecrets(new SecretsManagerOptions(storage));
await SecretsManagerClient.GetSecrets(new SecretsManagerOptions(storage), new string[] { new string('A', 22) });
}
catch (Exception e)
{
Expand Down Expand Up @@ -75,9 +77,52 @@ public static KeeperResult Error(string errorMsg)
}
}

private const char EscapeChar = '\\';
private static readonly char[] EscapedChars = { '\\', '.' };
private static string[] ParseQuery(string query)
{
// escape char to be used only in title and only when title has dot(s)
if (string.IsNullOrEmpty(query)) return new string[] { "" };
if (!query.Contains('.') || !query.Contains(EscapeChar)) return query.Split(new[] { '.' }, 2);

int pos = 0;
bool inEscSequence = query[pos].Equals(EscapeChar);
StringBuilder title = inEscSequence ? new StringBuilder() : new StringBuilder(query[pos].ToString());
while (++pos <= query.Length)
{
if (pos >= query.Length)
{
// unfinished escape sequence - treat last EscapeChar as regular character
if (inEscSequence) title.Append(EscapeChar);
break;
}
if (inEscSequence)
{
// \N (where N not in EscapedChars) is bad esc sequence but treat \ as single non esc char
if (!EscapedChars.Contains(query[pos]))
title.Append(EscapeChar);
title.Append(query[pos]);
inEscSequence = false;
}
else
{
inEscSequence = query[pos].Equals(EscapeChar);
if (!inEscSequence)
{
if ('.'.Equals(query[pos])) break;
title.Append(query[pos]);
}
}
}

// return cleaned up title with escape chars removed
if (pos >= query.Length) return new string[] { title.ToString() };
return new string[] { title.ToString(), query.Substring(pos + 1) };
}

public static async Task<object> GetSecret(string name, Hashtable config)
{
var parts = name.Split(new[] { '.' }, 2);
var parts = ParseQuery(name);
var (records, _) = await GetKeeperSecrets(config);
var found = records.FirstOrDefault(x => x.RecordUid == parts[0] || x.Data.title == parts[0]);
if (found == null)
Expand Down Expand Up @@ -159,7 +204,7 @@ public static async Task<string[]> GetSecretsInfo(string filter, Hashtable confi

public static async Task<KeeperResult> SetSecret(string name, object secret, Hashtable config)
{
var parts = name.Split('.');
var parts = ParseQuery(name);
if (parts.Length == 1)
{
return KeeperResult.Error("Set-Secret can be used only on a single field");
Expand Down

0 comments on commit 924fb89

Please sign in to comment.