-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AvoidUsingBrokenHashAlgorithms #1787
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
93a3ada
Add AvoidUsingBrokenHashAlgorithms rule
MJVL 458d761
Add AvoidUsingBrokenHashAlgorithms strings
MJVL d9fae07
Add AvoidUsingBrokenhashAlgorithms test suite
MJVL 157c162
Fix tests and improve style
MJVL de7a0d0
Add AvoidUsingBrokenHashAlgorithms to docs
MJVL fc09bd8
Fix severity and unit tests
MJVL 1a35e45
Fix error severity count in unit test
MJVL b79a48e
Adding frontmatter
sdwheeler 5f0f9a6
Merge branch 'master' into hashRule
bergmeister f252ecd
Update Tests/Engine/GetScriptAnalyzerRule.tests.ps1
bergmeister 62d9ae9
Merge branch 'master' into hashRule
bergmeister File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Management.Automation.Language; | ||
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; | ||
#if !CORECLR | ||
using System.ComponentModel.Composition; | ||
#endif | ||
using System.Globalization; | ||
using System.Linq; | ||
|
||
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules | ||
{ | ||
/// <summary> | ||
/// AvoidUsingBrokenHashAlgorithms: Avoid using the broken algorithms MD5 or SHA-1. | ||
/// </summary> | ||
#if !CORECLR | ||
[Export(typeof(IScriptRule))] | ||
#endif | ||
public class AvoidUsingBrokenHashAlgorithms : AvoidParameterGeneric | ||
{ | ||
private readonly string[] brokenAlgorithms = new string[] | ||
{ | ||
"MD5", | ||
"SHA1", | ||
}; | ||
|
||
private string algorithm; | ||
|
||
/// <summary> | ||
/// Condition on the cmdlet that must be satisfied for the error to be raised | ||
/// </summary> | ||
/// <param name="CmdAst"></param> | ||
/// <returns></returns> | ||
public override bool CommandCondition(CommandAst CmdAst) | ||
{ | ||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// Condition on the parameter that must be satisfied for the error to be raised. | ||
/// </summary> | ||
/// <param name="CmdAst"></param> | ||
/// <param name="CeAst"></param> | ||
/// <returns></returns> | ||
public override bool ParameterCondition(CommandAst CmdAst, CommandElementAst CeAst) | ||
{ | ||
if (CeAst is CommandParameterAst) | ||
{ | ||
CommandParameterAst cmdParamAst = CeAst as CommandParameterAst; | ||
|
||
if (String.Equals(cmdParamAst.ParameterName, "algorithm", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
Ast hashAlgorithmArgument = cmdParamAst.Argument; | ||
if (hashAlgorithmArgument is null) | ||
{ | ||
hashAlgorithmArgument = GetHashAlgorithmArg(CmdAst, cmdParamAst.Extent.StartOffset); | ||
if (hashAlgorithmArgument is null) | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
var constExprAst = hashAlgorithmArgument as ConstantExpressionAst; | ||
if (constExprAst != null) | ||
{ | ||
algorithm = constExprAst.Value as string; | ||
return IsBrokenAlgorithm(algorithm); | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private bool IsBrokenAlgorithm(string algorithm) | ||
{ | ||
if (algorithm != null) | ||
{ | ||
return brokenAlgorithms.Contains<string>( | ||
algorithm, | ||
StringComparer.OrdinalIgnoreCase); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private Ast GetHashAlgorithmArg(CommandAst CmdAst, int StartIndex) | ||
{ | ||
int small = int.MaxValue; | ||
Ast hashAlgorithmArg = null; | ||
foreach (Ast ast in CmdAst.CommandElements) | ||
{ | ||
if (ast.Extent.StartOffset > StartIndex && ast.Extent.StartOffset < small) | ||
{ | ||
hashAlgorithmArg = ast; | ||
small = ast.Extent.StartOffset; | ||
} | ||
} | ||
|
||
return hashAlgorithmArg; | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the error message | ||
/// </summary> | ||
/// <param name="FileName"></param> | ||
/// <param name="CmdAst"></param> | ||
/// <returns></returns> | ||
public override string GetError(string FileName, CommandAst CmdAst) | ||
{ | ||
if (CmdAst is null) | ||
{ | ||
return string.Empty; | ||
} | ||
|
||
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingBrokenHashAlgorithmsError, CmdAst.GetCommandName(), algorithm); | ||
} | ||
|
||
/// <summary> | ||
/// GetName: Retrieves the name of this rule. | ||
/// </summary> | ||
/// <returns>The name of this rule</returns> | ||
public override string GetName() | ||
{ | ||
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidUsingBrokenHashAlgorithmsName); | ||
} | ||
|
||
/// <summary> | ||
/// GetCommonName: Retrieves the common name of this rule. | ||
/// </summary> | ||
/// <returns>The common name of this rule</returns> | ||
public override string GetCommonName() | ||
{ | ||
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingBrokenHashAlgorithmsCommonName); | ||
} | ||
|
||
/// <summary> | ||
/// GetDescription: Retrieves the description of this rule. | ||
/// </summary> | ||
/// <returns>The description of this rule</returns> | ||
public override string GetDescription() | ||
{ | ||
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingBrokenHashAlgorithmsDescription); | ||
} | ||
|
||
/// <summary> | ||
/// GetSourceType: Retrieves the type of the rule: builtin, managed or module. | ||
/// </summary> | ||
public override SourceType GetSourceType() | ||
{ | ||
return SourceType.Builtin; | ||
} | ||
|
||
/// <summary> | ||
/// GetSeverity: Retrieves the severity of the rule: error, warning or information. | ||
/// </summary> | ||
/// <returns></returns> | ||
public override RuleSeverity GetSeverity() | ||
{ | ||
return RuleSeverity.Warning; | ||
} | ||
|
||
/// <summary> | ||
/// DiagnosticSeverity: Retrieves the severity of the rule of type DiagnosticSeverity: error, warning or information. | ||
/// </summary> | ||
/// <returns></returns> | ||
public override DiagnosticSeverity GetDiagnosticSeverity() | ||
{ | ||
return DiagnosticSeverity.Warning; | ||
} | ||
|
||
/// <summary> | ||
/// GetSourceName: Retrieves the module/assembly name the rule is from. | ||
/// </summary> | ||
public override string GetSourceName() | ||
{ | ||
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName); | ||
} | ||
} | ||
} | ||
|
||
|
||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. | ||
|
||
BeforeAll { | ||
$settings = @{ | ||
IncludeRules = @('PSAvoidUsingBrokenHashAlgorithms') | ||
Rules = @{ | ||
PSAvoidUsingBrokenHashAlgorithms = @{ | ||
Enable = $true | ||
} | ||
} | ||
} | ||
|
||
$violationMessageMD5 = [regex]::Escape("The Algorithm parameter of cmdlet 'Get-FileHash' was used with the broken algorithm 'MD5'.") | ||
$violationMessageSHA1 = [regex]::Escape("The Algorithm parameter of cmdlet 'Get-FileHash' was used with the broken algorithm 'SHA1'.") | ||
} | ||
|
||
Describe "AvoidUsingBrokenHashAlgorithms" { | ||
Context "When there are violations" { | ||
It "detects broken hash algorithms violations" { | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo -Algorithm MD5' -Settings $settings).Count | Should -Be 1 | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo -Algorithm SHA1' -Settings $settings).Count | Should -Be 1 | ||
} | ||
|
||
It "has the correct description message for MD5" { | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo -Algorithm MD5' -Settings $settings).Message | Should -Match $violationMessageMD5 | ||
} | ||
|
||
It "has the correct description message for SHA-1" { | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo -Algorithm SHA1' -Settings $settings).Message | Should -Match $violationMessageSHA1 | ||
} | ||
|
||
It "detects arbitrary cmdlets" { | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Fake-Cmdlet foo -Algorithm MD5' -Settings $settings).Count | Should -Be 1 | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Fake-Cmdlet foo -Algorithm SHA1' -Settings $settings).Count | Should -Be 1 | ||
} | ||
|
||
} | ||
|
||
Context "When there are no violations" { | ||
It "does not flag default algorithm" { | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo' -Settings $settings).Count | Should -Be 0 | ||
} | ||
|
||
It "does not flag safe algorithms" { | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo -Algorithm SHA256' -Settings $settings).Count | Should -Be 0 | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo -Algorithm SHA384' -Settings $settings).Count | Should -Be 0 | ||
(Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo -Algorithm SHA512' -Settings $settings).Count | Should -Be 0 | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
--- | ||
description: Cmdlet Verbs | ||
ms.custom: PSSA v1.21.0 | ||
ms.date: 05/31/2022 | ||
ms.topic: reference | ||
title: AvoidUsingBrokenHashAlgorithms | ||
--- | ||
# AvoidUsingBrokenHashAlgorithms | ||
|
||
**Severity Level: Warning** | ||
|
||
## Description | ||
|
||
Avoid using the broken algorithms MD5 or SHA-1. | ||
|
||
## How | ||
|
||
Replace broken algorithms with secure alternatives. MD5 and SHA-1 should be replaced with SHA256, SHA384, SHA512, or other safer algorithms when possible, with MD5 and SHA-1 only being utilized by necessity for backwards compatibility. | ||
|
||
## Example 1 | ||
|
||
### Wrong | ||
|
||
```powershell | ||
Get-FileHash foo.txt -Algorithm MD5 | ||
``` | ||
|
||
### Correct | ||
|
||
```powershell | ||
Get-FileHash foo.txt -Algorithm SHA256 | ||
``` | ||
|
||
## Example 2 | ||
|
||
### Wrong | ||
|
||
```powershell | ||
Get-FileHash foo.txt -Algorithm SHA1 | ||
``` | ||
|
||
### Correct | ||
|
||
```powershell | ||
Get-FileHash foo.txt | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An optional improvement would be to make it also work when no named parameters are used:
Invoke-ScriptAnalyzer -ScriptDefinition 'Get-FileHash foo SHA1'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Can you see of any cases where this might false flag? Ie, someone opening a file that's named the same as a flagged hashing algorithm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because what you would do is check whether the first and second item in the array are not named parameters, i.e. do not start with dash and therefore you know for sure that the first one has to be the file name and the second one the algorithm