Skip to content

Commit 3d20df7

Browse files
authored
Improve the sensitive history scrubbing to allow retrieving token from az, gcloud, and kubectl (#3641)
1 parent d6efcab commit 3d20df7

File tree

2 files changed

+81
-8
lines changed

2 files changed

+81
-8
lines changed

PSReadLine/History.cs

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ public class HistoryItem
128128
"Set-SecretVaultDefault",
129129
"Test-SecretVault",
130130
"Unlock-SecretVault",
131-
"Unregister-SecretVault"
131+
"Unregister-SecretVault",
132+
"Get-AzAccessToken",
132133
};
133134

134135
private void ClearSavedCurrentLine()
@@ -511,15 +512,32 @@ private static bool IsOnLeftSideOfAnAssignment(Ast ast, out Ast rhs)
511512
return result;
512513
}
513514

515+
private static bool IsRightSideOfAnAssignmentSafe(Ast rhs)
516+
{
517+
if (rhs is PipelineAst)
518+
{
519+
// Right hand side is a pipeline.
520+
return true;
521+
}
522+
523+
if (rhs is CommandExpressionAst cmdExprAst && cmdExprAst.Expression is MemberExpressionAst or InvokeMemberExpressionAst)
524+
{
525+
// Right hand side is a member access, or method invocation.
526+
return true;
527+
}
528+
529+
return false;
530+
}
531+
514532
private static bool IsSecretMgmtCommand(StringConstantExpressionAst strConst, out CommandAst command)
515533
{
534+
command = null;
516535
bool result = false;
517-
command = strConst.Parent as CommandAst;
518536

519-
if (command is not null)
537+
if (strConst.Parent is CommandAst cmdAst && ReferenceEquals(cmdAst.CommandElements[0], strConst) && s_SecretMgmtCommands.Contains(strConst.Value))
520538
{
521-
result = ReferenceEquals(command.CommandElements[0], strConst)
522-
&& s_SecretMgmtCommands.Contains(strConst.Value);
539+
result = true;
540+
command = cmdAst;
523541
}
524542

525543
return result;
@@ -568,6 +586,45 @@ private static ExpressionAst GetArgumentForParameter(CommandParameterAst param)
568586
return null;
569587
}
570588

589+
private static bool IsCloudTokenOrSecretAccess(StringConstantExpressionAst arg2Ast, out CommandAst command)
590+
{
591+
bool result = false;
592+
command = arg2Ast.Parent as CommandAst;
593+
594+
if (command is not null && command.CommandElements.Count >= 3
595+
&& command.CommandElements[0] is StringConstantExpressionAst nameAst
596+
&& command.CommandElements[1] is StringConstantExpressionAst arg1Ast
597+
&& command.CommandElements[2] == arg2Ast)
598+
{
599+
string name = nameAst.Value;
600+
string arg1 = arg1Ast.Value;
601+
string arg2 = arg2Ast.Value;
602+
603+
if (string.Equals(name, "gcloud", StringComparison.OrdinalIgnoreCase))
604+
{
605+
result = string.Equals(arg1, "auth", StringComparison.OrdinalIgnoreCase) &&
606+
string.Equals(arg2, "print-access-token", StringComparison.OrdinalIgnoreCase);
607+
}
608+
else if (string.Equals(name, "az", StringComparison.OrdinalIgnoreCase))
609+
{
610+
result = string.Equals(arg1, "account", StringComparison.OrdinalIgnoreCase) &&
611+
string.Equals(arg2, "get-access-token", StringComparison.OrdinalIgnoreCase);
612+
}
613+
else if (string.Equals(name, "kubectl", StringComparison.OrdinalIgnoreCase))
614+
{
615+
result = (string.Equals(arg1, "get", StringComparison.OrdinalIgnoreCase) || string.Equals(arg1, "describe", StringComparison.OrdinalIgnoreCase))
616+
&& (string.Equals(arg2, "secrets", StringComparison.OrdinalIgnoreCase) || string.Equals(arg2, "secret", StringComparison.OrdinalIgnoreCase));
617+
}
618+
}
619+
620+
if (!result)
621+
{
622+
command = null;
623+
}
624+
625+
return result;
626+
}
627+
571628
public static AddToHistoryOption GetDefaultAddToHistoryOption(string line)
572629
{
573630
if (string.IsNullOrEmpty(line))
@@ -618,8 +675,7 @@ public static AddToHistoryOption GetDefaultAddToHistoryOption(string line)
618675
// If it appears on the left-hand-side of an assignment, and the right-hand-side is
619676
// not a command invocation, we consider it sensitive.
620677
// e.g. `$token = Get-Secret` vs. `$token = 'token-text'` or `$token, $url = ...`
621-
isSensitive = IsOnLeftSideOfAnAssignment(innerAst, out Ast rhs)
622-
&& rhs is not PipelineAst;
678+
isSensitive = IsOnLeftSideOfAnAssignment(innerAst, out Ast rhs) && !IsRightSideOfAnAssignmentSafe(rhs);
623679

624680
if (!isSensitive)
625681
{
@@ -629,7 +685,8 @@ public static AddToHistoryOption GetDefaultAddToHistoryOption(string line)
629685

630686
case StringConstantExpressionAst strConst:
631687
isSensitive = true;
632-
if (IsSecretMgmtCommand(strConst, out CommandAst command))
688+
if (IsSecretMgmtCommand(strConst, out CommandAst command)
689+
|| IsCloudTokenOrSecretAccess(strConst, out command))
633690
{
634691
// If it's one of the secret management commands that we can ignore, we consider it safe.
635692
isSensitive = false;

test/HistoryTest.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,14 @@ public void SensitiveHistoryDefaultBehavior_Two()
212212
"$a.Password.Secret | Set-Value",
213213
"Write-Host $a.Password.Secret",
214214
"($a.Password, $b) = ('aa', 'bb')", // setting the 'Password' property with string value. Not saved to file.
215+
"kubectl get secrets",
216+
"kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode",
217+
"kubectl describe secret db-user-pass",
218+
"(Get-AzAccessToken -ResourceUrl 'https://abc.com').Token",
219+
"$token = (Get-AzAccessToken -ResourceUrl 'abc').Token",
220+
"az account get-access-token --resource=https://abc.com --query accessToken --output tsv",
221+
"curl -X GET --header \"Authorization: Bearer $token\" https://abc.com",
222+
"$env:PGPASS = gcloud auth print-access-token",
215223
};
216224

217225
string[] expectedSavedItems = new[] {
@@ -232,6 +240,14 @@ public void SensitiveHistoryDefaultBehavior_Two()
232240
"$a.Secret = $secret",
233241
"$a.Password.Secret | Set-Value",
234242
"Write-Host $a.Password.Secret",
243+
"kubectl get secrets",
244+
"kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode",
245+
"kubectl describe secret db-user-pass",
246+
"(Get-AzAccessToken -ResourceUrl 'https://abc.com').Token",
247+
"$token = (Get-AzAccessToken -ResourceUrl 'abc').Token",
248+
"az account get-access-token --resource=https://abc.com --query accessToken --output tsv",
249+
"curl -X GET --header \"Authorization: Bearer $token\" https://abc.com",
250+
"$env:PGPASS = gcloud auth print-access-token",
235251
};
236252

237253
try

0 commit comments

Comments
 (0)