Skip to content
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

Enable suppress command in the multitool #2394

Merged
merged 5 commits into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions docs/multitool-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ Use the SARIF Multitool to rewrite, enrich, filter, result match, and do other c
| file-work-items | Send SARIF results to a work item tracking system such as GitHub or Azure DevOps |
| match-results-forward | Match Results run over run to identify New, Absent, and Unchanged Results |
| merge | Merge multiple SARIF files into one |
| page | Extract a subset of results from a source SARIF file. |
| query | Find the matching subset of a SARIF file and output it or log it. |
| rebaseuri | Rebase the URIs in one or more sarif files. |
| page | Extract a subset of results from a source SARIF file |
| query | Find the matching subset of a SARIF file and output it or log it |
| rebaseuri | Rebase the URIs in one or more sarif files |
| rewrite | Transform a SARIF file to a reformatted version |
| suppress | Suppress results from a SARIF file |
| validate | Validate a SARIF File against the schema and against additional correctness rules. |
| help | See Usage |
| version | Display version information |
Expand Down Expand Up @@ -60,6 +61,9 @@ Sarif.Multitool merge C:\Input\*.sarif --recurse --output-directory=C:\Output\ -
: Extract new Results only from New Baseline
Sarif.Multitool query NewBaseline.sarif --expression "BaselineState == 'New'" --output Current.NewResults.sarif

: Suppress Results
Sarif.Multitool suppress current.sarif --justification "some justification" --alias "some alias" --guids --timestamps --expiryInDays 5 --output suppressed.sarif
Copy link
Collaborator

@yongyan-gh yongyan-gh Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suppress

missing a parameter "status" #Resolved

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.


: Validate a SARIF file conforms to the schema
Sarif.Multitool validate Other.sarif
```
Expand Down
7 changes: 7 additions & 0 deletions src/ReleaseHistory.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# SARIF Package Release History (SDK, Driver, Converters, and Multitool)

## Unreleased

* FEATURE: `MultithreadCommandBase` will use cache when hashing is enabled. [#2388](https://github.com/microsoft/sarif-sdk/pull/2388)
* FEATURE: Flow suppressions when baselining. [#2390](https://github.com/microsoft/sarif-sdk/pull/2390)
* BUGFIX: Fix number of results when filing work item. [#2391](https://github.com/microsoft/sarif-sdk/pull/2391)
* FEATURE: Add `suppress` command to multitool. [#2394](https://github.com/microsoft/sarif-sdk/pull/2394)

## **v2.4.11** [Sdk](https://www.nuget.org/packages/Sarif.Sdk/2.4.11) | [Driver](https://www.nuget.org/packages/Sarif.Driver/2.4.11) | [Converters](https://www.nuget.org/packages/Sarif.Converters/2.4.11) | [Multitool](https://www.nuget.org/packages/Sarif.Multitool/2.4.11) | [Multitool Library](https://www.nuget.org/packages/Sarif.Multitool.Library/2.4.11)

* BUGFIX: Fix partitioning visitor log duplication. [#2369](https://github.com/microsoft/sarif-sdk/pull/2369)
Expand Down
18 changes: 12 additions & 6 deletions src/Sarif.Multitool.Library/OptionsInterpretter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public class OptionsInterpretter
// Poor man's dependency injection
public OptionsInterpretter() : this(new EnvironmentVariableGetter())
{

}

public OptionsInterpretter(IEnvironmentVariableGetter environmentVariableGetter)
Expand All @@ -24,7 +23,7 @@ public OptionsInterpretter(IEnvironmentVariableGetter environmentVariableGetter)

private readonly IEnvironmentVariableGetter _environmentVariableGetter;

// Protected methods for abstract classes and public methods for concrete classes to ensure proper roll up and no
// Protected methods for abstract classes and public methods for concrete classes to ensure proper roll up and no
// redundant execution. Only leaves of the class diagram should have public methods and be called outside this class
protected void ConsumeEnvVarsAndInterpretOptions(CommonOptionsBase commonOptionsBase)
{
Expand All @@ -38,17 +37,17 @@ protected void ConsumeEnvVarsAndInterpretOptions(CommonOptionsBase commonOptions
}

#pragma warning disable IDE0060 // Ignore unused parameter for now

protected void ConsumeEnvVarsAndInterpretOptions(ExportConfigurationOptions exportConfigurationOptions)
#pragma warning restore IDE0060
{

}

#pragma warning disable IDE0060 // Ignore unused parameter for now

protected void ConsumeEnvVarsAndInterpretOptions(ExportRulesMetadataOptions exportConfigurationOptions)
#pragma warning restore IDE0060
{

}

protected void ConsumeEnvVarsAndInterpretOptions(AnalyzeOptionsBase analyzeOptionsBase)
Expand All @@ -73,16 +72,23 @@ protected void ConsumeEnvVarsAndInterpretOptions(SingleFileOptionsBase singleFil
ConsumeEnvVarsAndInterpretOptions((CommonOptionsBase)singleFileOptionsBase);
}

public void ConsumeEnvVarsAndInterpretOptions(SuppressOptions options)
{
ConsumeEnvVarsAndInterpretOptions((SingleFileOptionsBase)options);
}

public void ConsumeEnvVarsAndInterpretOptions(AbsoluteUriOptions absoluteUriOptions)
{
ConsumeEnvVarsAndInterpretOptions((MultipleFilesOptionsBase)absoluteUriOptions);
}

#if DEBUG

public void ConsumeEnvVarsAndInterpretOptions(AnalyzeTestOptions analyzeTestOptions)
{
ConsumeEnvVarsAndInterpretOptions((AnalyzeOptionsBase)analyzeTestOptions);
}

#endif

public void ConsumeEnvVarsAndInterpretOptions(ApplyPolicyOptions applyPolicyOptions)
Expand Down Expand Up @@ -121,17 +127,17 @@ public void ConsumeEnvVarsAndInterpretOptions(MergeOptions mergeOptions)
}

#pragma warning disable IDE0060 // Ignore unused parameter for now

public void ConsumeEnvVarsAndInterpretOptions(PageOptions pageOptions)
#pragma warning restore IDE0060
{

}

#pragma warning disable IDE0060 // Ignore unused parameter for now

public void ConsumeEnvVarsAndInterpretOptions(QueryOptions queryOptions)
#pragma warning restore IDE0060
{

}

public void ConsumeEnvVarsAndInterpretOptions(RebaseUriOptions rebaseUriOptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@
<ProjectReference Include="..\Sarif.WorkItems\Sarif.WorkItems.csproj" />
<ProjectReference Include="..\Sarif\Sarif.csproj" />
</ItemGroup>
</Project>
</Project>
82 changes: 82 additions & 0 deletions src/Sarif.Multitool.Library/SuppressCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Diagnostics;

using Microsoft.CodeAnalysis.Sarif.Driver;
using Microsoft.CodeAnalysis.Sarif.Readers;
using Microsoft.CodeAnalysis.Sarif.Visitors;
using Microsoft.CodeAnalysis.Sarif.Writers;

namespace Microsoft.CodeAnalysis.Sarif.Multitool
{
public class SuppressCommand : CommandBase
{
public SuppressCommand(IFileSystem fileSystem = null) : base(fileSystem)
{
}

public int Run(SuppressOptions options)
{
try
{
Console.WriteLine($"Suppress '{options.InputFilePath}' => '{options.OutputFilePath}'...");
var w = Stopwatch.StartNew();

bool valid = ValidateOptions(options);
if (!valid)
{
return FAILURE;
}

SarifLog currentSarifLog = PrereleaseCompatibilityTransformer.UpdateToCurrentVersion(FileSystem.FileReadAllText(options.InputFilePath),
options.Formatting,
out string _);

SarifLog reformattedLog = new SuppressVisitor(options.Justification,
options.Alias,
options.Guids,
options.Timestamps,
options.ExpiryInDays,
options.Status).VisitSarifLog(currentSarifLog);

string actualOutputPath = CommandUtilities.GetTransformedOutputFileName(options);
if (options.SarifOutputVersion == SarifVersion.OneZeroZero)
{
var visitor = new SarifCurrentToVersionOneVisitor();
visitor.VisitSarifLog(reformattedLog);

WriteSarifFile(FileSystem, visitor.SarifLogVersionOne, actualOutputPath, options.Formatting, SarifContractResolverVersionOne.Instance);
}
else
{
WriteSarifFile(FileSystem, reformattedLog, actualOutputPath, options.Formatting);
}

w.Stop();
Console.WriteLine($"Supress completed in {w.Elapsed}.");
Copy link
Collaborator

@yongyan-gh yongyan-gh Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like the idea to capture the execution time, not all commands implemented this, maybe we can move it to command base to work for all commands? #WontFix

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can create an issue to improve this.
won't do this in this PR.
but good suggestion.

}
catch (Exception ex)
{
Console.WriteLine(ex);
return FAILURE;
}

return SUCCESS;
}

private bool ValidateOptions(SuppressOptions options)
{
bool valid = true;

valid &= options.Validate();
valid &= options.ExpiryInDays >= 0;
valid &= !string.IsNullOrWhiteSpace(options.Justification);
valid &= (options.Status == SuppressionStatus.Accepted || options.Status == SuppressionStatus.UnderReview);
valid &= DriverUtilities.ReportWhetherOutputFileCanBeCreated(options.OutputFilePath, options.Force, FileSystem);
Copy link

@Bpendragon Bpendragon Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if every other item is false, this will return true if the last line is true, is this intended behavior?

Conversely, if every other item is true, this will return false if the last item is false. I can see why that might be useful in this case, but again, is that intended behavior? #Resolved

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I improved the tests.
it will only return true if all are true.


return valid;
}
}
}
44 changes: 44 additions & 0 deletions src/Sarif.Multitool.Library/SuppressOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using CommandLine;

using Microsoft.CodeAnalysis.Sarif.Driver;

namespace Microsoft.CodeAnalysis.Sarif.Multitool
{
[Verb("suppress", HelpText = "Enrich a SARIF file with additional data.")]
public class SuppressOptions : SingleFileOptionsBase
{
[Option(
"justification",
HelpText = "A string that provides the rationale for the suppressions",
Required = true)]
public string Justification { get; set; }

[Option(
"alias",
HelpText = "The account name associated with the suppression.")]
public string Alias { get; set; }
Copy link
Collaborator

@shaopeng-gh shaopeng-gh Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alias

just a thought, outside of Microsoft user may not immediately recognize Alias as Alias for user. do we like to change it to more common like user name, user alias, account name #Closed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question.
not sure yet the answer.
let's keep this way and we can improve in another iteration.


[Option(
"guids",
HelpText = "A UUID that will be associated with a suppression.")]
public bool Guids { get; set; }

[Option(
"timestamps",
HelpText = "The property 'timeUtc' that will be associated with a suppression.")]
public bool Timestamps { get; set; }

[Option(
"expiryInDays",
HelpText = "The property 'expiryUtc' that will be associated with a suppression from the 'timeUtc'.")]
public int ExpiryInDays { get; set; }

[Option(
"status",
HelpText = "The status that will be used in the suppression. Valid values include Accepted and UnderReview.")]
public SuppressionStatus Status { get; set; }
}
}
3 changes: 3 additions & 0 deletions src/Sarif.Multitool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static int Main(string[] args)
QueryOptions,
RebaseUriOptions,
RewriteOptions,
SuppressOptions,
ValidateOptions>(args)
.WithParsed<AbsoluteUriOptions>(x => { optionsInterpretter.ConsumeEnvVarsAndInterpretOptions(x); })
#if DEBUG
Expand All @@ -53,6 +54,7 @@ public static int Main(string[] args)
.WithParsed<QueryOptions>(x => { optionsInterpretter.ConsumeEnvVarsAndInterpretOptions(x); })
.WithParsed<RebaseUriOptions>(x => { optionsInterpretter.ConsumeEnvVarsAndInterpretOptions(x); })
.WithParsed<RewriteOptions>(x => { optionsInterpretter.ConsumeEnvVarsAndInterpretOptions(x); })
.WithParsed<SuppressOptions>(x => { optionsInterpretter.ConsumeEnvVarsAndInterpretOptions(x); })
.WithParsed<ValidateOptions>(x => { optionsInterpretter.ConsumeEnvVarsAndInterpretOptions(x); })
.MapResult(
(AbsoluteUriOptions absoluteUriOptions) => new AbsoluteUriCommand().Run(absoluteUriOptions),
Expand All @@ -71,6 +73,7 @@ public static int Main(string[] args)
(QueryOptions queryOptions) => new QueryCommand().Run(queryOptions),
(RebaseUriOptions rebaseOptions) => new RebaseUriCommand().Run(rebaseOptions),
(RewriteOptions rewriteOptions) => new RewriteCommand().Run(rewriteOptions),
(SuppressOptions options) => new SuppressCommand().Run(options),
(ValidateOptions validateOptions) => new ValidateCommand().Run(validateOptions),
_ => HandleParseError(args));
}
Expand Down
78 changes: 78 additions & 0 deletions src/Sarif/Visitors/SuppressVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Globalization;

namespace Microsoft.CodeAnalysis.Sarif.Visitors
{
public class SuppressVisitor : SarifRewritingVisitor
{
private readonly bool guids;
private readonly string alias;
private readonly bool timestamps;
private readonly DateTime timeUtc;
private readonly DateTime expiryUtc;
private readonly int expiryInDays;
private readonly string justification;
private readonly SuppressionStatus suppressionStatus;

public SuppressVisitor(string justification,
string alias,
bool guids,
bool timestamps,
int expiryInDays,
SuppressionStatus suppressionStatus)
{
this.alias = alias;
this.guids = guids;
this.timestamps = timestamps;
this.timeUtc = DateTime.UtcNow;
this.expiryInDays = expiryInDays;
this.justification = justification;
this.suppressionStatus = suppressionStatus;
this.expiryUtc = this.timeUtc.AddDays(expiryInDays);
}

public override Result VisitResult(Result node)
{
if (node.Suppressions == null)
{
node.Suppressions = new List<Suppression>();
}

var suppression = new Suppression
{
Status = suppressionStatus,
Justification = justification
};

if (!string.IsNullOrWhiteSpace(alias))
{
suppression.SetProperty(nameof(alias), alias);
Copy link
Collaborator

@yongyan-gh yongyan-gh Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetProperty

where will these property values be stored? In current spec Suppression doesn't have these properties right? #WontFix

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will be stored in the property bag

}

if (guids)
{
suppression.SetProperty("guid", Guid.NewGuid());
}

if (timestamps)
{
//suppression.SetProperty("timeUtc", timeUtc.ToString(SarifUtilities.SarifDateTimeFormatMillisecondsPrecision,
// CultureInfo.InvariantCulture));
Copy link
Collaborator

@shaopeng-gh shaopeng-gh Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need this comment #Closed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no we don't.
just removed!
thanks!

suppression.SetProperty("timeUtc", timeUtc);
Copy link
Collaborator

@shaopeng-gh shaopeng-gh Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timeUtc

I see the expiryUtc below use a format, if we dont use the same fromat fuction, will the result format differnt #Closed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, they will be printed in the same format.
just improved the code below.

}

if (expiryInDays > 0)
{
suppression.SetProperty("expiryUtc", expiryUtc.ToString(SarifUtilities.SarifDateTimeFormatMillisecondsPrecision,
Copy link
Collaborator

@yongyan-gh yongyan-gh Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"expiryUtc"

we can also use nameof() just like setting property for alias #Resolved

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just updated!
thanks

CultureInfo.InvariantCulture));
}

node.Suppressions.Add(suppression);
return base.VisitResult(node);
}
}
}
Loading