Merge pull request #7 from Microsoft/1-0-9
Update to 1.0.9. Add line location to message output.
This commit is contained in:
Коммит
101835a7bc
|
@ -6,7 +6,7 @@ SETLOCAL
|
|||
|
||||
set MAJOR=1
|
||||
set MINOR=0
|
||||
set PATCH=6
|
||||
set PATCH=9
|
||||
set PRERELEASE=-beta
|
||||
|
||||
set VERSION_CONSTANTS=src\Sarif.Driver\VersionConstants.cs
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<metadata>
|
||||
<id>$id$</id>
|
||||
<version>$major$.$minor$.$patch$$prerelease$</version>
|
||||
<title>Microsoft SARIF Driver Framework</title>
|
||||
<title>Microsoft SARIF Driver Framework (includes SARIF SDK)</title>
|
||||
<authors>Microsoft</authors>
|
||||
<owners>Michael C. Fanning, Billy O'Neal</owners>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>SARIF-based classes and utilities for building static analysis drivers</description>
|
||||
<description>SARIF-based classes and utilities for building static analysis drivers. Includes the SARIF SDK.</description>
|
||||
<releaseNotes>Version $major$.$minor$.$patch$$prerelease$ of the Microsoft SARIF Driver Utilities</releaseNotes>
|
||||
<copyright>Copyright Microsoft 2015</copyright>
|
||||
<licenseUrl>https://github.com/Microsoft/driver-utilities/blob/master/LICENSE</licenseUrl>
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
// 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.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.CodeAnalysis.Sarif.Driver.Sdk;
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
using Microsoft.CodeAnalysis.Sarif.Readers;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class AnalyzeCommandBaseTests
|
||||
{
|
||||
private void ExceptionTestHelper(
|
||||
ExceptionCondition exceptionCondition,
|
||||
RuntimeConditions runtimeConditions,
|
||||
ExitReason expectedExitReason = ExitReason.None,
|
||||
TestAnalyzeOptions analyzeOptions = null)
|
||||
{
|
||||
ExceptionRaisingRule.s_exceptionCondition = exceptionCondition;
|
||||
analyzeOptions = analyzeOptions ?? new TestAnalyzeOptions()
|
||||
{
|
||||
TargetFileSpecifiers = new string[0]
|
||||
};
|
||||
|
||||
var command = new TestAnalyzeCommand();
|
||||
command.DefaultPlugInAssemblies = new Assembly[] { typeof(ExceptionRaisingRule).Assembly };
|
||||
|
||||
int result = command.Run(analyzeOptions);
|
||||
|
||||
int expectedResult =
|
||||
(runtimeConditions & RuntimeConditions.Fatal) == RuntimeConditions.NoErrors ?
|
||||
TestAnalyzeCommand.SUCCESS : TestAnalyzeCommand.FAILURE;
|
||||
|
||||
Assert.Equal(runtimeConditions, command.RuntimeErrors);
|
||||
Assert.Equal(expectedResult, result);
|
||||
|
||||
if (expectedExitReason != ExitReason.None)
|
||||
{
|
||||
Assert.NotNull(command.ExecutionException);
|
||||
|
||||
if (expectedExitReason != ExitReason.UnhandledExceptionInEngine)
|
||||
{
|
||||
var eax = command.ExecutionException as ExitApplicationException<ExitReason>;
|
||||
Assert.NotNull(eax);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(command.ExecutionException);
|
||||
}
|
||||
ExceptionRaisingRule.s_exceptionCondition = ExceptionCondition.None;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionRaisedInstantiatingSkimmers()
|
||||
{
|
||||
ExceptionTestHelper(
|
||||
ExceptionCondition.InvokingConstructor,
|
||||
RuntimeConditions.ExceptionInstantiatingSkimmers,
|
||||
ExitReason.UnhandledExceptionInstantiatingSkimmers);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionRaisedInvokingInitialize()
|
||||
{
|
||||
ExceptionTestHelper(
|
||||
ExceptionCondition.InvokingInitialize,
|
||||
RuntimeConditions.ExceptionInSkimmerInitialize
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionRaisedInvokingCanAnalyze()
|
||||
{
|
||||
var options = new TestAnalyzeOptions()
|
||||
{
|
||||
TargetFileSpecifiers = new string[] { this.GetType().Assembly.Location },
|
||||
};
|
||||
|
||||
ExceptionTestHelper(
|
||||
ExceptionCondition.InvokingCanAnalyze,
|
||||
RuntimeConditions.ExceptionRaisedInSkimmerCanAnalyze,
|
||||
analyzeOptions: options
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionRaisedInvokingAnalyze()
|
||||
{
|
||||
var options = new TestAnalyzeOptions()
|
||||
{
|
||||
TargetFileSpecifiers = new string[] { this.GetType().Assembly.Location },
|
||||
};
|
||||
|
||||
ExceptionTestHelper(
|
||||
ExceptionCondition.InvokingAnalyze,
|
||||
RuntimeConditions.ExceptionInSkimmerAnalyze,
|
||||
analyzeOptions: options
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExceptionRaisedInEngine()
|
||||
{
|
||||
TestAnalyzeCommand.RaiseUnhandledExceptionInDriverCode = true;
|
||||
|
||||
var options = new TestAnalyzeOptions()
|
||||
{
|
||||
TargetFileSpecifiers = new string[] { this.GetType().Assembly.Location },
|
||||
};
|
||||
|
||||
ExceptionTestHelper(
|
||||
ExceptionCondition.None,
|
||||
RuntimeConditions.ExceptionInEngine,
|
||||
ExitReason.UnhandledExceptionInEngine);
|
||||
|
||||
TestAnalyzeCommand.RaiseUnhandledExceptionInDriverCode = false;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IOExceptionRaisedCreatingSarifLog()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
using (var stream = File.OpenWrite(path))
|
||||
{
|
||||
// our log file is locked for write
|
||||
// causing exceptions at analysis time
|
||||
|
||||
var options = new TestAnalyzeOptions()
|
||||
{
|
||||
TargetFileSpecifiers = new string[] { this.GetType().Assembly.Location },
|
||||
OutputFilePath = path,
|
||||
Verbose = true,
|
||||
};
|
||||
|
||||
ExceptionTestHelper(
|
||||
ExceptionCondition.None,
|
||||
RuntimeConditions.ExceptionCreatingLogfile,
|
||||
expectedExitReason: ExitReason.ExceptionCreatingLogFile,
|
||||
analyzeOptions: options);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnauthorizedAccessExceptionCreatingSarifLog()
|
||||
{
|
||||
string path = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
|
||||
path = Path.Combine(path, Guid.NewGuid().ToString());
|
||||
|
||||
try
|
||||
{
|
||||
// attempt to persist to unauthorized location will raise exception
|
||||
var options = new TestAnalyzeOptions()
|
||||
{
|
||||
TargetFileSpecifiers = new string[] { this.GetType().Assembly.Location },
|
||||
OutputFilePath = path,
|
||||
Verbose = true,
|
||||
};
|
||||
|
||||
ExceptionTestHelper(
|
||||
ExceptionCondition.None,
|
||||
RuntimeConditions.ExceptionCreatingLogfile,
|
||||
expectedExitReason: ExitReason.ExceptionCreatingLogFile,
|
||||
analyzeOptions: options);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public RunLog AnalyzeFile(string fileName)
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
RunLog runLog = null;
|
||||
|
||||
try
|
||||
{
|
||||
var options = new TestAnalyzeOptions
|
||||
{
|
||||
TargetFileSpecifiers = new string[] { fileName },
|
||||
Verbose = true,
|
||||
Statistics = true,
|
||||
ComputeTargetsHash = true,
|
||||
PolicyFilePath = "default",
|
||||
Recurse = true,
|
||||
OutputFilePath = path,
|
||||
};
|
||||
|
||||
var command = new TestAnalyzeCommand();
|
||||
command.DefaultPlugInAssemblies = new Assembly[] { this.GetType().Assembly };
|
||||
int result = command.Run(options);
|
||||
|
||||
Assert.Equal(TestAnalyzeCommand.SUCCESS, result);
|
||||
|
||||
JsonSerializerSettings settings = new JsonSerializerSettings()
|
||||
{
|
||||
ContractResolver = SarifContractResolver.Instance
|
||||
};
|
||||
|
||||
ResultLog log = JsonConvert.DeserializeObject<ResultLog>(File.ReadAllText(path), settings);
|
||||
Assert.NotNull(log);
|
||||
Assert.Equal<int>(1, log.RunLogs.Count);
|
||||
|
||||
runLog = log.RunLogs[0];
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
return runLog;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndToEndAnalysisWithNoIssues()
|
||||
{
|
||||
RunLog runLog = AnalyzeFile(this.GetType().Assembly.Location);
|
||||
|
||||
int issueCount = 0;
|
||||
SarifHelpers.ValidateRunLog(runLog, (issue) => { issueCount++; });
|
||||
Assert.Equal(1, issueCount);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public enum ExceptionCondition
|
||||
{
|
||||
None,
|
||||
AccessingId,
|
||||
AccessingName,
|
||||
InvokingConstructor,
|
||||
InvokingAnalyze,
|
||||
InvokingCanAnalyze,
|
||||
InvokingInitialize
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
internal class ExceptionRaisingRule : IRuleDescriptor, ISkimmer<TestAnalysisContext>
|
||||
{
|
||||
internal static ExceptionCondition s_exceptionCondition;
|
||||
|
||||
private ExceptionCondition _exceptionCondition;
|
||||
|
||||
public ExceptionRaisingRule()
|
||||
{
|
||||
_exceptionCondition = s_exceptionCondition;
|
||||
|
||||
if (_exceptionCondition == ExceptionCondition.InvokingConstructor)
|
||||
{
|
||||
throw new InvalidOperationException(nameof(ExceptionCondition.InvokingConstructor));
|
||||
}
|
||||
}
|
||||
|
||||
public string ExceptionRaisingRuleId = "TEST1001";
|
||||
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_exceptionCondition == ExceptionCondition.AccessingId)
|
||||
{
|
||||
throw new InvalidOperationException(nameof(ExceptionCondition.AccessingId));
|
||||
}
|
||||
return ExceptionRaisingRuleId;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_exceptionCondition == ExceptionCondition.AccessingName)
|
||||
{
|
||||
throw new InvalidOperationException(nameof(ExceptionCondition.AccessingName));
|
||||
}
|
||||
return nameof(ExceptionRaisingRule);
|
||||
}
|
||||
}
|
||||
|
||||
public string FullDescription
|
||||
{
|
||||
get { return "Test Rule Description"; }
|
||||
}
|
||||
|
||||
public string ShortDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> Options
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> FormatSpecifiers
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Analyze(TestAnalysisContext context)
|
||||
{
|
||||
if (_exceptionCondition == ExceptionCondition.InvokingAnalyze)
|
||||
{
|
||||
throw new InvalidOperationException(nameof(ExceptionCondition.InvokingAnalyze));
|
||||
}
|
||||
}
|
||||
|
||||
public AnalysisApplicability CanAnalyze(TestAnalysisContext context, out string reasonIfNotApplicable)
|
||||
{
|
||||
reasonIfNotApplicable = null;
|
||||
if (_exceptionCondition == ExceptionCondition.InvokingCanAnalyze)
|
||||
{
|
||||
throw new InvalidOperationException(nameof(ExceptionCondition.InvokingCanAnalyze));
|
||||
}
|
||||
return AnalysisApplicability.ApplicableToSpecifiedTarget;
|
||||
}
|
||||
|
||||
public void Initialize(TestAnalysisContext context)
|
||||
{
|
||||
if (_exceptionCondition == ExceptionCondition.InvokingInitialize)
|
||||
{
|
||||
throw new InvalidOperationException(nameof(ExceptionCondition.InvokingInitialize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Sarif Driver Utilities Unit Tests")]
|
||||
[assembly: AssemblyDescription("Unit tests for the SARIF driver utilities framework")]
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props')" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{66915F7B-9872-4390-AFED-DB8DE7B761BE}</ProjectGuid>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CodeAnalysis.Driver</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Sarif.Driver.UnitTests</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., build.props))\build.props" />
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Sarif, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Sarif.Sdk.1.4.10-beta\lib\net40\Sarif.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections.Immutable, Version=1.1.37.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.1.1.37\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.assert, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.core, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.execution.desktop, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AnalyzeCommandTests.cs" />
|
||||
<Compile Include="ExceptionCondition.cs" />
|
||||
<Compile Include="ExceptionRaisingRule.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SarifHelpers.cs" />
|
||||
<Compile Include="TestAnalysisContext.cs" />
|
||||
<Compile Include="TestAnalyzeCommand.cs" />
|
||||
<Compile Include="TestAnalyzeOptions.cs" />
|
||||
<Compile Include="TestMessageLogger.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sarif.Driver\Sarif.Driver.csproj">
|
||||
<Project>{8ceaea61-b1a2-4777-bcbe-c9a129a5f6c5}</Project>
|
||||
<Name>Sarif.Driver</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="CopyFunctionalTestsData" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<TestFiles Include="$(SolutionDir)FunctionalTestsData\**\*" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(TestFiles)" DestinationFolder="$(OutputPath)\FunctionalTestsData\%(RecursiveDir)" />
|
||||
</Target>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props'))" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,27 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
internal static class SarifHelpers
|
||||
{
|
||||
public static void ValidateRunLog(RunLog runLog, Action<Result> resultAction)
|
||||
{
|
||||
ValidateToolInfo(runLog.ToolInfo);
|
||||
|
||||
foreach (Result result in runLog.Results) { resultAction(result); }
|
||||
}
|
||||
|
||||
public static void ValidateToolInfo(ToolInfo toolInfo)
|
||||
{
|
||||
Assert.Equal("Sarif.Driver", toolInfo.Name);
|
||||
// TODO version, etc
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class TestAnalysisContext : IAnalysisContext
|
||||
{
|
||||
public bool IsValidAnalysisTarget { get; set; }
|
||||
|
||||
public IResultLogger Logger { get; set; }
|
||||
|
||||
public PropertyBag Policy { get; set; }
|
||||
|
||||
public IRuleDescriptor Rule { get; set; }
|
||||
|
||||
public Exception TargetLoadException { get; set; }
|
||||
|
||||
public Uri TargetUri { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class TestAnalyzeCommand : AnalyzeCommandBase<TestAnalysisContext, TestAnalyzeOptions>
|
||||
{
|
||||
public override IEnumerable<Assembly> DefaultPlugInAssemblies { get; set; }
|
||||
|
||||
public override string Prerelease { get { return ""; } }
|
||||
|
||||
protected override TestAnalysisContext CreateContext(TestAnalyzeOptions options, IResultLogger logger, PropertyBag policy, string filePath = null)
|
||||
{
|
||||
var context = base.CreateContext(options, logger, policy, filePath);
|
||||
context.IsValidAnalysisTarget = options.RegardAnalysisTargetAsValid;
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class TestAnalyzeOptions : IAnalyzeOptions
|
||||
{
|
||||
public TestAnalyzeOptions()
|
||||
{
|
||||
RegardAnalysisTargetAsValid = true;
|
||||
}
|
||||
|
||||
public bool ComputeTargetsHash { get; set; }
|
||||
|
||||
public string OutputFilePath { get; set; }
|
||||
|
||||
public IList<string> PlugInFilePaths { get; set; }
|
||||
|
||||
|
||||
public string PolicyFilePath { get; set; }
|
||||
|
||||
public bool Recurse { get; set; }
|
||||
|
||||
public bool Statistics { get; set; }
|
||||
|
||||
public IEnumerable<string> TargetFileSpecifiers { get; set; }
|
||||
|
||||
public bool Verbose { get; set; }
|
||||
|
||||
public bool RegardAnalysisTargetAsValid { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
internal class TestMessageLogger : IResultLogger
|
||||
{
|
||||
public TestMessageLogger()
|
||||
{
|
||||
FailTargets = new HashSet<string>();
|
||||
PassTargets = new HashSet<string>();
|
||||
NotApplicableTargets = new HashSet<string>();
|
||||
}
|
||||
|
||||
public HashSet<string> PassTargets { get; set; }
|
||||
|
||||
public HashSet<string> FailTargets { get; set; }
|
||||
|
||||
public HashSet<string> NotApplicableTargets { get; set; }
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, string message)
|
||||
{
|
||||
switch (messageKind)
|
||||
{
|
||||
case ResultKind.Pass:
|
||||
{
|
||||
PassTargets.Add(context.TargetUri.LocalPath);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Error:
|
||||
{
|
||||
FailTargets.Add(context.TargetUri.LocalPath);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.NotApplicable:
|
||||
{
|
||||
NotApplicableTargets.Add(context.TargetUri.LocalPath);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Note:
|
||||
case ResultKind.InternalError:
|
||||
case ResultKind.ConfigurationError:
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, FormattedMessage message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Sarif.Sdk" version="1.4.10-beta" targetFramework="net452" />
|
||||
<package id="xunit" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.abstractions" version="2.0.0" targetFramework="net452" />
|
||||
<package id="xunit.assert" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.core" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net452" />
|
||||
</packages>
|
|
@ -0,0 +1,50 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class AggregatingLogger : IDisposable, IResultLogger
|
||||
{
|
||||
public AggregatingLogger() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public AggregatingLogger(IEnumerable<IResultLogger> loggers)
|
||||
{
|
||||
this.Loggers = loggers != null ?
|
||||
new List<IResultLogger>(loggers) :
|
||||
new List<IResultLogger>();
|
||||
}
|
||||
|
||||
public IList<IResultLogger> Loggers { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (IResultLogger logger in Loggers)
|
||||
{
|
||||
using (logger as IDisposable) { };
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, FormattedMessage message)
|
||||
{
|
||||
foreach (IResultLogger logger in Loggers)
|
||||
{
|
||||
logger.Log(messageKind, context, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, string message)
|
||||
{
|
||||
foreach (IResultLogger logger in Loggers)
|
||||
{
|
||||
logger.Log(messageKind, context, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public enum AnalysisApplicability
|
||||
{
|
||||
Unknown,
|
||||
NotApplicableToSpecifiedTarget,
|
||||
ApplicableToSpecifiedTarget,
|
||||
NotApplicableToAnyTargetWithoutPolicy
|
||||
}
|
||||
}
|
|
@ -0,0 +1,507 @@
|
|||
// 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.IO;
|
||||
using Microsoft.CodeAnalysis.Sarif.Driver;
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public abstract class AnalyzeCommandBase<TContext, TOptions> : PlugInDriverCommand<TOptions>
|
||||
where TContext : IAnalysisContext, new()
|
||||
where TOptions : IAnalyzeOptions
|
||||
{
|
||||
public Exception ExecutionException { get; set; }
|
||||
|
||||
public RuntimeConditions RuntimeErrors { get; set; }
|
||||
|
||||
public static bool RaiseUnhandledExceptionInDriverCode { get; set; }
|
||||
|
||||
public override int Run(TOptions analyzeOptions)
|
||||
{
|
||||
// 0. Initialize an common logger that drives all outputs. This
|
||||
// object drives logging for console, statistics, etc.
|
||||
using (AggregatingLogger logger = InitializeLogger(analyzeOptions))
|
||||
{
|
||||
try
|
||||
{
|
||||
Analyze(analyzeOptions, logger);
|
||||
}
|
||||
catch (ExitApplicationException<ExitReason> ex)
|
||||
{
|
||||
// These exceptions have already been logged
|
||||
ExecutionException = ex;
|
||||
return FAILURE;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// These exceptions escaped our net and must be logged here
|
||||
TContext context = new TContext();
|
||||
context.Rule = ErrorDescriptors.UnhandledEngineException;
|
||||
context.Logger = logger;
|
||||
LogUnhandledEngineException(ex, context);
|
||||
ExecutionException = ex;
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return ((RuntimeErrors & RuntimeConditions.Fatal) == RuntimeConditions.NoErrors) ? SUCCESS : FAILURE;
|
||||
}
|
||||
|
||||
private void Analyze(TOptions analyzeOptions, AggregatingLogger logger)
|
||||
{
|
||||
// 1. Scrape the analyzer options for settings that alter
|
||||
// behaviors of binary parsers (such as settings for
|
||||
// symbols resolution).
|
||||
InitializeFromOptions(analyzeOptions);
|
||||
|
||||
// 2. Produce a comprehensive set of analysis targets
|
||||
HashSet<string> targets = CreateTargetsSet(analyzeOptions);
|
||||
|
||||
// 3. Proactively validate that we can locate and
|
||||
// access all analysis targets. Helper will return
|
||||
// a list that potentially filters out files which
|
||||
// did not exist, could not be accessed, etc.
|
||||
targets = ValidateTargetsExist(logger, targets);
|
||||
|
||||
// 4. Create our policy, which will be shared across
|
||||
// all context objects that are created during analysis
|
||||
PropertyBag policy = CreatePolicyFromOptions(analyzeOptions);
|
||||
|
||||
// 5. Create short-lived context object to pass to
|
||||
// skimmers during initialization. The logger and
|
||||
// policy objects are common to all context instances
|
||||
// and will be passed on again for analysis.
|
||||
TContext context = CreateContext(analyzeOptions, logger, policy);
|
||||
|
||||
// 6. Initialize report file, if configured.
|
||||
InitializeOutputFile(analyzeOptions, context, targets);
|
||||
|
||||
// 7. Instantiate skimmers.
|
||||
HashSet<ISkimmer<TContext>> skimmers = CreateSkimmers(logger);
|
||||
|
||||
// 8. Initialize skimmers. Initialize occurs a single time only.
|
||||
skimmers = InitializeSkimmers(skimmers, context);
|
||||
|
||||
// 9. Run all analysis
|
||||
AnalyzeTargets(analyzeOptions, skimmers, context, targets);
|
||||
|
||||
// 10. For test purposes, raise an unhandled exception if indicated
|
||||
if (RaiseUnhandledExceptionInDriverCode)
|
||||
{
|
||||
throw new InvalidOperationException(this.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal AggregatingLogger InitializeLogger(IAnalyzeOptions analyzeOptions)
|
||||
{
|
||||
var logger = new AggregatingLogger();
|
||||
logger.Loggers.Add(new ConsoleLogger(analyzeOptions.Verbose));
|
||||
|
||||
if (analyzeOptions.Statistics)
|
||||
{
|
||||
logger.Loggers.Add(new StatisticsLogger());
|
||||
}
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
|
||||
protected virtual void InitializeFromOptions(TOptions analyzeOptions)
|
||||
{
|
||||
}
|
||||
|
||||
private static HashSet<string> CreateTargetsSet(TOptions analyzeOptions)
|
||||
{
|
||||
HashSet<string> targets = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (string specifier in analyzeOptions.TargetFileSpecifiers)
|
||||
{
|
||||
// Currently, we do not filter on any extensions.
|
||||
var fileSpecifier = new FileSpecifier(specifier, recurse: analyzeOptions.Recurse, filter: "*");
|
||||
foreach (string file in fileSpecifier.Files) { targets.Add(file); }
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
private HashSet<string> ValidateTargetsExist(IResultLogger logger, HashSet<string> targets)
|
||||
{
|
||||
return targets;
|
||||
}
|
||||
|
||||
protected virtual TContext CreateContext(TOptions options, IResultLogger logger, PropertyBag policy, string filePath = null)
|
||||
{
|
||||
var context = new TContext();
|
||||
context.Logger = logger;
|
||||
context.Policy = policy;
|
||||
|
||||
if (filePath != null)
|
||||
{
|
||||
context.TargetUri = new Uri(filePath);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private void InitializeOutputFile(TOptions analyzeOptions, TContext context, HashSet<string> targets)
|
||||
{
|
||||
string filePath = analyzeOptions.OutputFilePath;
|
||||
AggregatingLogger aggregatingLogger = (AggregatingLogger)context.Logger;
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
InvokeCatchingRelevantIOExceptions
|
||||
(
|
||||
() => aggregatingLogger.Loggers.Add(
|
||||
new SarifLogger(
|
||||
analyzeOptions.OutputFilePath,
|
||||
analyzeOptions.Verbose,
|
||||
targets,
|
||||
analyzeOptions.ComputeTargetsHash,
|
||||
Prerelease)),
|
||||
(ex) =>
|
||||
{
|
||||
LogExceptionCreatingLogFile(filePath, context, ex);
|
||||
throw new ExitApplicationException<ExitReason>(SdkResources.UnexpectedApplicationExit, ex)
|
||||
{
|
||||
ExitReason = ExitReason.ExceptionCreatingLogFile
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvokeCatchingRelevantIOExceptions(Action action, Action<Exception> exceptionHandler)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
exceptionHandler(ex);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
exceptionHandler(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<ISkimmer<TContext>> CreateSkimmers(IResultLogger logger)
|
||||
{
|
||||
HashSet<ISkimmer<TContext>> result = new HashSet<ISkimmer<TContext>>();
|
||||
try
|
||||
{
|
||||
IEnumerable<ISkimmer<TContext>> skimmers;
|
||||
skimmers = DriverUtilities.GetExports<ISkimmer<TContext>>(DefaultPlugInAssemblies);
|
||||
|
||||
foreach (ISkimmer<TContext> skimmer in skimmers)
|
||||
{
|
||||
result.Add(skimmer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionInstantiatingSkimmers;
|
||||
throw new ExitApplicationException<ExitReason>(SdkResources.UnexpectedApplicationExit, ex)
|
||||
{
|
||||
ExitReason = ExitReason.UnhandledExceptionInstantiatingSkimmers
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void AnalyzeTargets(
|
||||
TOptions options,
|
||||
IEnumerable<ISkimmer<TContext>> skimmers,
|
||||
TContext rootContext,
|
||||
IEnumerable<string> targets)
|
||||
{
|
||||
HashSet<string> disabledSkimmers = new HashSet<string>();
|
||||
|
||||
foreach (string target in targets)
|
||||
{
|
||||
using (TContext context = AnalyzeTarget(options, skimmers, rootContext, target, disabledSkimmers))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual TContext AnalyzeTarget(
|
||||
TOptions options,
|
||||
IEnumerable<ISkimmer<TContext>> skimmers,
|
||||
TContext rootContext,
|
||||
string target,
|
||||
HashSet<string> disabledSkimmers)
|
||||
{
|
||||
var context = CreateContext(options, rootContext.Logger, rootContext.Policy, target);
|
||||
|
||||
if (context.TargetLoadException != null)
|
||||
{
|
||||
LogExceptionLoadingTarget(context);
|
||||
return context;
|
||||
}
|
||||
else if (!context.IsValidAnalysisTarget)
|
||||
{
|
||||
LogExceptionInvalidTarget(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
// Analyzing {0}...
|
||||
context.Rule = NoteDescriptors.AnalyzingTarget;
|
||||
context.Logger.Log(ResultKind.Note, context,
|
||||
string.Format(SdkResources.Analyzing, Path.GetFileName(context.TargetUri.LocalPath)));
|
||||
|
||||
foreach (ISkimmer<TContext> skimmer in skimmers)
|
||||
{
|
||||
if (disabledSkimmers.Contains(skimmer.Id)) { continue; }
|
||||
|
||||
string reasonForNotAnalyzing = null;
|
||||
context.Rule = skimmer;
|
||||
|
||||
AnalysisApplicability applicability = AnalysisApplicability.Unknown;
|
||||
|
||||
try
|
||||
{
|
||||
applicability = skimmer.CanAnalyze(context, out reasonForNotAnalyzing);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogUnhandledRuleExceptionAssessingTargetApplicability(disabledSkimmers, context, skimmer, ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (applicability)
|
||||
{
|
||||
case AnalysisApplicability.NotApplicableToSpecifiedTarget:
|
||||
{
|
||||
// Image '{0}' was not evaluated for check '{1}' as the analysis
|
||||
// is not relevant based on observed binary metadata: {2}.
|
||||
context.Logger.Log(ResultKind.NotApplicable,
|
||||
context,
|
||||
MessageUtilities.BuildTargetNotAnalyzedMessage(
|
||||
context.TargetUri.LocalPath,
|
||||
context.Rule.Name,
|
||||
reasonForNotAnalyzing));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AnalysisApplicability.NotApplicableToAnyTargetWithoutPolicy:
|
||||
{
|
||||
// Check '{0}' was disabled for this run as the analysis was not
|
||||
// configured with required policy ({1}). To resolve this,
|
||||
// configure and provide a policy file on the BinSkim command-line
|
||||
// using the --policy argument (recommended), or pass
|
||||
// '--policy default' to invoke built-in settings. Invoke the
|
||||
// BinSkim.exe 'export' command to produce an initial policy file
|
||||
// that can be edited if required and passed back into the tool.
|
||||
context.Logger.Log(ResultKind.ConfigurationError, context,
|
||||
MessageUtilities.BuildRuleDisabledDueToMissingPolicyMessage(
|
||||
context.Rule.Name,
|
||||
reasonForNotAnalyzing));
|
||||
disabledSkimmers.Add(skimmer.Id);
|
||||
break;
|
||||
}
|
||||
|
||||
case AnalysisApplicability.ApplicableToSpecifiedTarget:
|
||||
{
|
||||
try
|
||||
{
|
||||
skimmer.Analyze(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogUnhandledRuleExceptionAnalyzingTarget(disabledSkimmers, context, skimmer, ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private void LogUnhandledEngineException(Exception ex, IAnalysisContext context)
|
||||
{
|
||||
// An unhandled exception was raised during analysis:
|
||||
// {1}
|
||||
context.Logger.Log(ResultKind.InternalError,
|
||||
context,
|
||||
string.Format(SdkResources.UnhandledEngineException,
|
||||
ex.ToString()));
|
||||
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionInEngine;
|
||||
}
|
||||
|
||||
private void LogExceptionLoadingRoslynAnalyzer(string analyzerFilePath, TContext context, Exception ex)
|
||||
{
|
||||
context.Rule = ErrorDescriptors.InvalidConfiguration;
|
||||
|
||||
// An exception was raised attempting to load Roslyn analyzer '{0}'. Exception information:
|
||||
// {1}
|
||||
context.Logger.Log(ResultKind.ConfigurationError,
|
||||
context,
|
||||
string.Format(SdkResources.ExceptionLoadingAnalysisPlugIn,
|
||||
analyzerFilePath,
|
||||
context.TargetLoadException.ToString()));
|
||||
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionLoadingAnalysisPlugIn;
|
||||
}
|
||||
|
||||
private void LogExceptionInvalidTarget(TContext context)
|
||||
{
|
||||
context.Rule = NoteDescriptors.InvalidTarget;
|
||||
|
||||
// Image '{0}' was not analyzed as the it does not
|
||||
// appear to be a valid portable executable.
|
||||
context.Logger.Log(ResultKind.NotApplicable,
|
||||
context,
|
||||
string.Format(
|
||||
SdkResources.TargetNotAnalyzed_InvalidTarget,
|
||||
context.TargetUri.LocalPath));
|
||||
|
||||
context.Dispose();
|
||||
RuntimeErrors |= RuntimeConditions.OneOrMoreTargetsNotValidToAnalyze;
|
||||
}
|
||||
|
||||
private void LogExceptionLoadingTarget(TContext context)
|
||||
{
|
||||
context.Rule = ErrorDescriptors.InvalidConfiguration;
|
||||
|
||||
// An exception was raised attempting to load analysis target '{0}'. Exception information:
|
||||
// {1}
|
||||
context.Logger.Log(ResultKind.ConfigurationError,
|
||||
context,
|
||||
string.Format(SdkResources.ExceptionLoadingAnalysisTarget,
|
||||
context.TargetUri.LocalPath,
|
||||
context.TargetLoadException.ToString()));
|
||||
|
||||
context.Dispose();
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionLoadingTargetFile;
|
||||
}
|
||||
|
||||
private void LogExceptionCreatingLogFile(string fileName, TContext context, Exception ex)
|
||||
{
|
||||
// An exception was raised attempting to create output file '{0}'. Exception information:
|
||||
// {1}
|
||||
context.Rule = ErrorDescriptors.InvalidConfiguration;
|
||||
context.Logger.Log(ResultKind.ConfigurationError,
|
||||
context,
|
||||
string.Format(SdkResources.ExceptionCreatingLogFile,
|
||||
fileName,
|
||||
ex.ToString()));
|
||||
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionCreatingLogfile;
|
||||
}
|
||||
|
||||
private void LogUnhandledExceptionInitializingRule(TContext context, ISkimmer<TContext> skimmer, Exception ex)
|
||||
{
|
||||
string ruleName = context.Rule.Name;
|
||||
// An unhandled exception was encountered initializing check '{0}', which
|
||||
// has been disabled for the remainder of the analysis. Exception information:
|
||||
// {1}
|
||||
context.Rule = ErrorDescriptors.UnhandledRuleException;
|
||||
context.Logger.Log(ResultKind.InternalError,
|
||||
context,
|
||||
string.Format(SdkResources.ExceptionInitializingRule,
|
||||
ruleName,
|
||||
ex.ToString()));
|
||||
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionInSkimmerInitialize;
|
||||
}
|
||||
|
||||
private void LogUnhandledRuleExceptionAssessingTargetApplicability(HashSet<string> disabledSkimmers, TContext context, ISkimmer<TContext> skimmer, Exception ex)
|
||||
{
|
||||
string ruleName = context.Rule.Name;
|
||||
// An unhandled exception was raised attempting to determine whether '{0}'
|
||||
// is a valid analysis target for check '{1}' (which has been disabled
|
||||
// for the remainder of the analysis). The exception may have resulted
|
||||
// from a problem related to parsing image metadata and not specific to
|
||||
// the rule, however. Exception information:
|
||||
// {2}
|
||||
context.Rule = ErrorDescriptors.UnhandledRuleException;
|
||||
context.Logger.Log(ResultKind.InternalError,
|
||||
context,
|
||||
string.Format(SdkResources.ExceptionCheckingApplicability,
|
||||
context.TargetUri.LocalPath,
|
||||
ruleName,
|
||||
ex.ToString()));
|
||||
|
||||
if (disabledSkimmers != null) { disabledSkimmers.Add(skimmer.Id); }
|
||||
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionRaisedInSkimmerCanAnalyze;
|
||||
}
|
||||
|
||||
private void LogUnhandledRuleExceptionAnalyzingTarget(HashSet<string> disabledSkimmers, TContext context, ISkimmer<TContext> skimmer, Exception ex)
|
||||
{
|
||||
string ruleName = context.Rule.Name;
|
||||
// An unhandled exception was encountered analyzing '{0}' for check '{1}',
|
||||
// which has been disabled for the remainder of the analysis.The
|
||||
// exception may have resulted from a problem related to parsing
|
||||
// image metadata and not specific to the rule, however.
|
||||
// Exception information:
|
||||
// {2}
|
||||
context.Rule = ErrorDescriptors.UnhandledRuleException;
|
||||
context.Logger.Log(ResultKind.InternalError,
|
||||
context,
|
||||
string.Format(SdkResources.ExceptionAnalyzingTarget,
|
||||
context.TargetUri.LocalPath,
|
||||
ruleName,
|
||||
ex.ToString()));
|
||||
|
||||
if (disabledSkimmers != null) { disabledSkimmers.Add(skimmer.Id); }
|
||||
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionInSkimmerAnalyze;
|
||||
}
|
||||
|
||||
protected virtual HashSet<ISkimmer<TContext>> InitializeSkimmers(HashSet<ISkimmer<TContext>> skimmers, TContext context)
|
||||
{
|
||||
HashSet<ISkimmer<TContext>> disabledSkimmers = new HashSet<ISkimmer<TContext>>();
|
||||
|
||||
// ONE-TIME initialization of skimmers. Do not call
|
||||
// Initialize more than once per skimmer instantiation
|
||||
foreach (ISkimmer<TContext> skimmer in skimmers)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.Rule = skimmer;
|
||||
skimmer.Initialize(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RuntimeErrors |= RuntimeConditions.ExceptionInSkimmerInitialize;
|
||||
LogUnhandledExceptionInitializingRule(context, skimmer, ex);
|
||||
disabledSkimmers.Add(skimmer);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ISkimmer<TContext> disabledSkimmer in disabledSkimmers)
|
||||
{
|
||||
skimmers.Remove(disabledSkimmer);
|
||||
}
|
||||
|
||||
return skimmers;
|
||||
}
|
||||
|
||||
|
||||
private static PropertyBag CreatePolicyFromOptions(TOptions analyzeOptions)
|
||||
{
|
||||
PropertyBag policy = null;
|
||||
string policyFilePath = analyzeOptions.PolicyFilePath;
|
||||
|
||||
if (!string.IsNullOrEmpty(policyFilePath))
|
||||
{
|
||||
policy = new PropertyBag();
|
||||
if (!policyFilePath.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
policy.LoadFrom(policyFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
return policy;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class ConsoleLogger : IResultLogger
|
||||
{
|
||||
public ConsoleLogger(bool verbose)
|
||||
{
|
||||
Verbose = verbose;
|
||||
}
|
||||
|
||||
public bool Verbose { get; set; }
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, string message)
|
||||
{
|
||||
switch (messageKind)
|
||||
{
|
||||
|
||||
// These result types are optionally emitted
|
||||
case ResultKind.Pass:
|
||||
case ResultKind.Note:
|
||||
case ResultKind.NotApplicable:
|
||||
{
|
||||
if (Verbose)
|
||||
{
|
||||
Console.WriteLine(GetMessageText(context, message, messageKind));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// These result types are alwayss emitted
|
||||
case ResultKind.Error:
|
||||
case ResultKind.Warning:
|
||||
case ResultKind.InternalError:
|
||||
case ResultKind.ConfigurationError:
|
||||
{
|
||||
Console.WriteLine(GetMessageText(context, message, messageKind));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
public static string GetMessageText(
|
||||
IAnalysisContext context,
|
||||
string message,
|
||||
ResultKind messageKind,
|
||||
Region region = null)
|
||||
{
|
||||
string path = null;
|
||||
Uri uri = context.TargetUri;
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
// If a path refers to a URI of form file://blah, we will convert to the local path
|
||||
if (uri.IsAbsoluteUri && uri.Scheme == Uri.UriSchemeFile)
|
||||
{
|
||||
path = uri.LocalPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = uri.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
string issueType = null;
|
||||
|
||||
switch (messageKind)
|
||||
{
|
||||
case ResultKind.ConfigurationError:
|
||||
{
|
||||
issueType = "CONFIGURATION ERROR";
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.InternalError:
|
||||
{
|
||||
issueType = "INTERNAL ERROR";
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Error:
|
||||
{
|
||||
issueType = "error";
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Warning:
|
||||
{
|
||||
issueType = "warning";
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.NotApplicable:
|
||||
case ResultKind.Note:
|
||||
{
|
||||
issueType = "note";
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException("Unknown message kind:" + messageKind.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
string detailedDiagnosis = NormalizeMessage(message, enquote: false);
|
||||
|
||||
string location = "";
|
||||
|
||||
if (region != null)
|
||||
{
|
||||
// TODO
|
||||
if (region.Offset > 0 || region.StartColumn == 0) { throw new NotImplementedException(); }
|
||||
|
||||
if (region.StartLine == 0) { throw new InvalidOperationException(); }
|
||||
|
||||
// VS supports the following formatting options:
|
||||
// (startLine)
|
||||
// (startLine-endLine)
|
||||
// (startLine,startColumn)
|
||||
// (startLine,startColumn-endColumn)
|
||||
// (startLine,startColumn,endLine,endColumn
|
||||
//
|
||||
// For expedience, we'll convert everything to the most fully qualified format
|
||||
|
||||
string start = region.StartLine.ToString() + "," +
|
||||
(region.StartColumn > 0 ? region.StartColumn.ToString() : "1");
|
||||
|
||||
string end = (region.EndLine > region.StartLine ? region.EndLine.ToString() : region.StartLine.ToString()) + "," +
|
||||
(region.EndColumn > 0 ? region.EndColumn.ToString() : region.StartColumn.ToString());
|
||||
|
||||
location =
|
||||
"(" +
|
||||
start + (end != start ? "," + end : "") +
|
||||
")";
|
||||
}
|
||||
|
||||
return (path != null ? (path + location + ": ") : "") +
|
||||
issueType + " " +
|
||||
context.Rule.Id + ": " +
|
||||
detailedDiagnosis;
|
||||
}
|
||||
|
||||
public static string NormalizeMessage(string message, bool enquote)
|
||||
{
|
||||
return (enquote ? "\"" : "") +
|
||||
message.Replace('"', '\'') +
|
||||
(enquote ? "\"" : "");
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, FormattedMessage message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public static class ErrorDescriptors
|
||||
{
|
||||
public static IRuleDescriptor InvalidConfiguration = new RuleDescriptor()
|
||||
{
|
||||
Id = "ERR0997",
|
||||
Name = nameof(InvalidConfiguration),
|
||||
FullDescription = SdkResources.InvalidConfiguration_Description,
|
||||
FormatSpecifiers = BuildDictionary(new string[] {
|
||||
nameof(SdkResources.ExceptionCreatingLogFile),
|
||||
nameof(SdkResources.ExceptionLoadingAnalysisPlugIn),
|
||||
nameof(SdkResources.ExceptionLoadingAnalysisTarget)
|
||||
})
|
||||
};
|
||||
|
||||
public static IRuleDescriptor UnhandledRuleException = new RuleDescriptor()
|
||||
{
|
||||
Id = "ERR0998",
|
||||
Name = nameof(UnhandledRuleException),
|
||||
FullDescription = SdkResources.ExceptionInRule_Description,
|
||||
FormatSpecifiers = BuildDictionary(new string[] {
|
||||
nameof(SdkResources.ExceptionInitializingRule),
|
||||
nameof(SdkResources.ExceptionAnalyzingTarget)
|
||||
})
|
||||
};
|
||||
|
||||
public static IRuleDescriptor UnhandledEngineException = new RuleDescriptor()
|
||||
{
|
||||
Id = "ERR0999",
|
||||
Name = nameof(UnhandledEngineException),
|
||||
FullDescription = SdkResources.ExceptionInAnalysisEngine_Description,
|
||||
FormatSpecifiers = BuildDictionary(new string[] {
|
||||
nameof(SdkResources.ExceptionInAnalysisEngine)
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
public static IRuleDescriptor ParseError = new RuleDescriptor()
|
||||
{
|
||||
Id = "ERR1001",
|
||||
Name = nameof(ParseError),
|
||||
FullDescription = SdkResources.ParseError_Description,
|
||||
FormatSpecifiers = BuildDictionary(new string[] {
|
||||
nameof(SdkResources.ParseError)
|
||||
})
|
||||
};
|
||||
|
||||
private static Dictionary<string, string> BuildDictionary(IEnumerable<string> resourceNames)
|
||||
{
|
||||
// Note this dictionary provides for case-insensitive keys
|
||||
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (string resourceName in resourceNames)
|
||||
{
|
||||
string resourceValue = SdkResources.ResourceManager.GetString(resourceName);
|
||||
dictionary[resourceName] = resourceValue;
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public enum ExitReason
|
||||
{
|
||||
None,
|
||||
ExceptionCreatingLogFile,
|
||||
UnhandledExceptionInstantiatingSkimmers,
|
||||
UnhandledExceptionInEngine,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// 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.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public static class HashUtilities
|
||||
{
|
||||
public static string ComputeSha256Hash(string fileName)
|
||||
{
|
||||
string sha256Hash = null;
|
||||
|
||||
try
|
||||
{
|
||||
using (FileStream stream = File.OpenRead(fileName))
|
||||
{
|
||||
using (var bufferedStream = new BufferedStream(stream, 1024 * 32))
|
||||
{
|
||||
var sha = new SHA256Cng();
|
||||
byte[] checksum = sha.ComputeHash(bufferedStream);
|
||||
sha256Hash = BitConverter.ToString(checksum).Replace("-", String.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
catch (UnauthorizedAccessException) { }
|
||||
return sha256Hash;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// 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;
|
||||
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public interface IAnalysisContext : IDisposable
|
||||
{
|
||||
Uri TargetUri { get; set; }
|
||||
|
||||
Exception TargetLoadException { get; set; }
|
||||
|
||||
bool IsValidAnalysisTarget { get; }
|
||||
|
||||
IRuleDescriptor Rule { get; set; }
|
||||
|
||||
PropertyBag Policy { get; set; }
|
||||
|
||||
IResultLogger Logger { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public interface IAnalyzeOptions
|
||||
{
|
||||
IEnumerable<string> TargetFileSpecifiers { get; }
|
||||
|
||||
string OutputFilePath { get; }
|
||||
|
||||
bool Verbose { get; }
|
||||
|
||||
bool Recurse { get; }
|
||||
|
||||
string PolicyFilePath { get; }
|
||||
|
||||
bool Statistics { get; }
|
||||
|
||||
bool ComputeTargetsHash { get; }
|
||||
|
||||
IList<string> PlugInFilePaths { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public interface IResultLogger
|
||||
{
|
||||
void Log(ResultKind messageKind, IAnalysisContext context, string message);
|
||||
|
||||
void Log(ResultKind messageKind, IAnalysisContext context, FormattedMessage message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public interface ISkimmer<TContext> : IRuleDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialize method for skimmer instance. This method will only
|
||||
/// only be called a single time per skimmer instantiation.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
void Initialize(TContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether a target is a valid target for analysis.
|
||||
/// May be called from multiple threads.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>An analysis applicability value that indicates whether a check is
|
||||
/// applicable to a specified target, is not applicable to a specified target,
|
||||
/// or is not applicable to any target due to the absence of a configured
|
||||
/// policy. In cases where the analysis is determined not to be applicable,
|
||||
/// the 'reasonIfNotApplicable' property should be set to a string that
|
||||
/// describes the observed state or condition that prevents analysis.
|
||||
/// </returns>
|
||||
AnalysisApplicability CanAnalyze(TContext context, out string reasonIfNotApplicable);
|
||||
|
||||
/// <summary>
|
||||
/// Analyze specified binary target and use context-resident loggers
|
||||
/// to record the results of the analysis. May be called from multiple threads.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
void Analyze(TContext context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// 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 System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public static class MessageUtilities
|
||||
{
|
||||
public static string BuildMessage(IAnalysisContext context, string messageFormatString, params string[] arguments)
|
||||
{
|
||||
// By convention, the first argument is always the target name,
|
||||
// which we retrieve from the context
|
||||
Debug.Assert(File.Exists(context.TargetUri.LocalPath));
|
||||
string targetName = Path.GetFileName(context.TargetUri.LocalPath);
|
||||
|
||||
string[] fullArguments = new string[arguments != null ? arguments.Length + 1 : 1];
|
||||
fullArguments[0] = targetName;
|
||||
|
||||
if (fullArguments.Length > 1)
|
||||
{
|
||||
arguments.CopyTo(fullArguments, 1);
|
||||
}
|
||||
|
||||
return String.Format(CultureInfo.InvariantCulture,
|
||||
messageFormatString, fullArguments);
|
||||
}
|
||||
|
||||
|
||||
public static string BuildTargetNotAnalyzedMessage(string targetPath, string ruleName, string reason)
|
||||
{
|
||||
targetPath = Path.GetFileName(targetPath);
|
||||
|
||||
// Image '{0}' was not evaluated for check '{1}' as the analysis
|
||||
// is not relevant based on observed metadata: {2}
|
||||
return String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
SdkResources.TargetNotAnalyzed_NotApplicable,
|
||||
targetPath,
|
||||
ruleName,
|
||||
reason);
|
||||
}
|
||||
|
||||
public static string BuildRuleDisabledDueToMissingPolicyMessage(string ruleName, string reason)
|
||||
{
|
||||
// BinSkim command-line using the --policy argument (recommended), or
|
||||
// pass --defaultPolicy to invoke built-in settings. Invoke the
|
||||
// BinSkim.exe 'export' command to produce an initial policy file
|
||||
// that can be edited if required and passed back into the tool.
|
||||
return String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
SdkResources.RuleWasDisabledDueToMissingPolicy,
|
||||
ruleName,
|
||||
reason);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// 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 Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public static class NoteDescriptors
|
||||
{
|
||||
public static IRuleDescriptor AnalyzingTarget = new RuleDescriptor()
|
||||
{
|
||||
// A file is being analyzed.
|
||||
Id = "MSG1001",
|
||||
Name = nameof(AnalyzingTarget),
|
||||
FullDescription = SdkResources.InvalidTarget_Description,
|
||||
FormatSpecifiers = BuildDictionary(new string[] {
|
||||
nameof(SdkResources.Analyzing),
|
||||
})
|
||||
};
|
||||
|
||||
public static IRuleDescriptor InvalidTarget = new RuleDescriptor()
|
||||
{
|
||||
// A file was skipped as it does not appear to be a valid target for analysis.
|
||||
Id = "MSG1002",
|
||||
Name = nameof(InvalidTarget),
|
||||
FullDescription = SdkResources.InvalidTarget_Description,
|
||||
FormatSpecifiers = BuildDictionary(new string[] {
|
||||
nameof(SdkResources.TargetNotAnalyzed_InvalidTarget),
|
||||
})
|
||||
};
|
||||
|
||||
private static Dictionary<string, string> BuildDictionary(IEnumerable<string> resourceNames)
|
||||
{
|
||||
// Note this dictionary provides for case-insensitive keys
|
||||
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (string resourceName in resourceNames)
|
||||
{
|
||||
string resourceValue = SdkResources.ResourceManager.GetString(resourceName);
|
||||
dictionary[resourceName] = resourceValue;
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
// 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.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis.Sarif.Writers;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.CodeAnalysis.Sarif;
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class ResultLogger
|
||||
{
|
||||
private FileStream _fileStream;
|
||||
private TextWriter _textWriter;
|
||||
private JsonTextWriter _jsonTextWriter;
|
||||
private ResultLogJsonWriter _issueLogJsonWriter;
|
||||
|
||||
public ResultLogger(
|
||||
Assembly assembly,
|
||||
string outputFilePath,
|
||||
bool verbose,
|
||||
IEnumerable<string> analysisTargets,
|
||||
bool computeTargetsHash,
|
||||
string prereleaseInfo)
|
||||
{
|
||||
Verbose = verbose;
|
||||
|
||||
_fileStream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
_textWriter = new StreamWriter(_fileStream);
|
||||
_jsonTextWriter = new JsonTextWriter(_textWriter);
|
||||
|
||||
// for debugging it is nice to have the following line added.
|
||||
_jsonTextWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
|
||||
|
||||
_issueLogJsonWriter = new ResultLogJsonWriter(_jsonTextWriter);
|
||||
|
||||
var toolInfo = new ToolInfo();
|
||||
toolInfo.InitializeFromAssembly(assembly, prereleaseInfo);
|
||||
|
||||
RunInfo runInfo = new RunInfo();
|
||||
runInfo.AnalysisTargets = new List<FileReference>();
|
||||
|
||||
foreach (string target in analysisTargets)
|
||||
{
|
||||
var fileReference = new FileReference()
|
||||
{
|
||||
Uri = target.CreateUriForJsonSerialization(),
|
||||
};
|
||||
|
||||
if (computeTargetsHash)
|
||||
{
|
||||
string sha256Hash = HashUtilities.ComputeSha256Hash(target) ?? "[could not compute file hash]";
|
||||
fileReference.Hashes = new List<Hash>(new Hash[]
|
||||
{
|
||||
new Hash()
|
||||
{
|
||||
Value = sha256Hash,
|
||||
Algorithm = AlgorithmKind.Sha256,
|
||||
}
|
||||
});
|
||||
}
|
||||
runInfo.AnalysisTargets.Add(fileReference);
|
||||
}
|
||||
runInfo.InvocationInfo = Environment.CommandLine;
|
||||
|
||||
_issueLogJsonWriter.WriteToolAndRunInfo(toolInfo, runInfo);
|
||||
}
|
||||
|
||||
public bool Verbose { get; set; }
|
||||
|
||||
public Uri Uri
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IRuleDescriptor Rule
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Disposing the json writer closes the stream but the textwriter
|
||||
// still needs to be disposed or closed to write the results
|
||||
if (_issueLogJsonWriter != null) { _issueLogJsonWriter.Dispose(); }
|
||||
if (_textWriter != null) { _textWriter.Dispose(); }
|
||||
}
|
||||
|
||||
public void Log(ResultKind resultKind, IAnalysisContext context, string message)
|
||||
{
|
||||
Result result = new Result();
|
||||
|
||||
result.RuleId = context.Rule.Id;
|
||||
result.FullMessage = message;
|
||||
result.Kind = resultKind;
|
||||
result.Locations = new[]{
|
||||
new Sarif.Sdk.Location {
|
||||
AnalysisTarget = new[]
|
||||
{
|
||||
new PhysicalLocationComponent
|
||||
{
|
||||
// Why? When NewtonSoft serializes this Uri, it will use the
|
||||
// original string used to construct the Uri. For a file path,
|
||||
// this will be the local file path. We want to persist this
|
||||
// information using the file:// protocol rendering, however.
|
||||
Uri = context.TargetUri.LocalPath.CreateUriForJsonSerialization(),
|
||||
MimeType = MimeType.Binary
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_issueLogJsonWriter.WriteResult(result);
|
||||
}
|
||||
private void WriteJsonIssue(string binary, string ruleId, string message, ResultKind issueKind)
|
||||
{
|
||||
Result result = new Result();
|
||||
|
||||
result.RuleId = ruleId;
|
||||
result.FullMessage = message;
|
||||
result.Kind = issueKind;
|
||||
result.Locations = new[]{
|
||||
new Sarif.Sdk.Location {
|
||||
AnalysisTarget = new[]
|
||||
{
|
||||
new PhysicalLocationComponent
|
||||
{
|
||||
// Why? When NewtonSoft serializes this Uri, it will use the
|
||||
// original string used to construct the Uri. For a file path,
|
||||
// this will be the local file path. We want to persist this
|
||||
// information using the file:// protocol rendering, however.
|
||||
Uri = binary.CreateUriForJsonSerialization(),
|
||||
MimeType = MimeType.Binary
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_issueLogJsonWriter.WriteResult(result);
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, FormattedMessage message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 System;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
// This enum is used to identify specific runtime conditions
|
||||
// encountered during execution. This mechanism is used by
|
||||
// unit tests to ensure that failure conditions travel expected
|
||||
// code paths. These conditions are a combination of fatal
|
||||
// and non-fatal circumstances
|
||||
[Flags]
|
||||
public enum RuntimeConditions
|
||||
{
|
||||
NoErrors = 0,
|
||||
|
||||
// Not used today but perhaps soon...
|
||||
//CouldNotLoadCustomLoggerAssembly,
|
||||
//CouldNotLoadCustomLoggerType,
|
||||
//UnrecognizedDefaultLoggerExtension,
|
||||
//MalformedCustomLoggersArgument,
|
||||
//LoggerFailedInitialization,
|
||||
//LoggerRaisedExceptionOnInitialization,
|
||||
//LoggerRaisedExceptionOnWrite,
|
||||
//LoggerRaisedExceptionOnClose,
|
||||
|
||||
// Fatal conditions
|
||||
ExceptionInstantiatingSkimmers = 0x01,
|
||||
ExceptionInSkimmerInitialize = 0x02,
|
||||
ExceptionRaisedInSkimmerCanAnalyze = 0x04,
|
||||
ExceptionInSkimmerAnalyze = 0x08,
|
||||
ExceptionCreatingLogfile = 0x10,
|
||||
ExceptionInEngine = 0x20,
|
||||
ExceptionLoadingTargetFile = 0x40,
|
||||
ExceptionLoadingAnalysisPlugIn = 0x80,
|
||||
Fatal = (Int32.MaxValue ^ NonFatal),
|
||||
|
||||
// Non-fatal conditions
|
||||
OneOrMoreTargetsNotValidToAnalyze = 0x80,
|
||||
|
||||
NonFatal = OneOrMoreTargetsNotValidToAnalyze,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
// 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.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.CodeAnalysis.Sarif;
|
||||
using Microsoft.CodeAnalysis.Sarif.Sdk;
|
||||
using Microsoft.CodeAnalysis.Sarif.Writers;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class SarifLogger : IDisposable, IResultLogger
|
||||
{
|
||||
private FileStream _fileStream;
|
||||
private TextWriter _textWriter;
|
||||
private JsonTextWriter _jsonTextWriter;
|
||||
private ResultLogJsonWriter _issueLogJsonWriter;
|
||||
|
||||
public static ToolInfo CreateDefaultToolInfo(string prereleaseInfo = null)
|
||||
{
|
||||
Assembly assembly = typeof(SarifLogger).Assembly;
|
||||
string name = Path.GetFileNameWithoutExtension(assembly.Location);
|
||||
Version version = assembly.GetName().Version;
|
||||
|
||||
ToolInfo toolInfo = new ToolInfo();
|
||||
toolInfo.Name = name;
|
||||
toolInfo.Version = version.Major.ToString() + "." + version.Minor.ToString() + "." + version.Build.ToString();
|
||||
toolInfo.FullName = name + " " + toolInfo.Version + (prereleaseInfo ?? "");
|
||||
|
||||
return toolInfo;
|
||||
}
|
||||
|
||||
public SarifLogger(
|
||||
string outputFilePath,
|
||||
bool verbose,
|
||||
IEnumerable<string> analysisTargets,
|
||||
bool computeTargetsHash,
|
||||
string prereleaseInfo)
|
||||
{
|
||||
Verbose = verbose;
|
||||
|
||||
_fileStream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
_textWriter = new StreamWriter(_fileStream);
|
||||
_jsonTextWriter = new JsonTextWriter(_textWriter);
|
||||
|
||||
// for debugging it is nice to have the following line added.
|
||||
_jsonTextWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
|
||||
|
||||
_issueLogJsonWriter = new ResultLogJsonWriter(_jsonTextWriter);
|
||||
|
||||
var toolInfo = CreateDefaultToolInfo(prereleaseInfo);
|
||||
|
||||
RunInfo runInfo = new RunInfo();
|
||||
runInfo.AnalysisTargets = new List<FileReference>();
|
||||
|
||||
foreach (string target in analysisTargets)
|
||||
{
|
||||
var fileReference = new FileReference()
|
||||
{
|
||||
Uri = target.CreateUriForJsonSerialization(),
|
||||
};
|
||||
|
||||
if (computeTargetsHash)
|
||||
{
|
||||
string sha256Hash = HashUtilities.ComputeSha256Hash(target) ?? "[could not compute file hash]";
|
||||
fileReference.Hashes = new List<Hash>(new Hash[]
|
||||
{
|
||||
new Hash()
|
||||
{
|
||||
Value = sha256Hash,
|
||||
Algorithm = AlgorithmKind.Sha256,
|
||||
}
|
||||
});
|
||||
}
|
||||
runInfo.AnalysisTargets.Add(fileReference);
|
||||
}
|
||||
runInfo.InvocationInfo = Environment.CommandLine;
|
||||
|
||||
_issueLogJsonWriter.WriteToolAndRunInfo(toolInfo, runInfo);
|
||||
}
|
||||
|
||||
public bool Verbose { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Disposing the json writer closes the stream but the textwriter
|
||||
// still needs to be disposed or closed to write the results
|
||||
if (_issueLogJsonWriter != null) { _issueLogJsonWriter.Dispose(); }
|
||||
if (_textWriter != null) { _textWriter.Dispose(); }
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, string message)
|
||||
{
|
||||
switch (messageKind)
|
||||
{
|
||||
case ResultKind.Pass:
|
||||
{
|
||||
if (Verbose)
|
||||
{
|
||||
WriteJsonIssue(context.TargetUri.LocalPath, context.Rule.Id, message, ResultKind.Pass);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Error:
|
||||
case ResultKind.Warning:
|
||||
{
|
||||
WriteJsonIssue(context.TargetUri.LocalPath, context.Rule.Id, message, ResultKind.Error);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.NotApplicable:
|
||||
{
|
||||
if (Verbose)
|
||||
{
|
||||
WriteJsonIssue(context.TargetUri.LocalPath, context.Rule.Id, message, ResultKind.NotApplicable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Note:
|
||||
{
|
||||
if (Verbose)
|
||||
{
|
||||
WriteJsonIssue(context.TargetUri.LocalPath, context.Rule.Id, message, ResultKind.Note);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.ConfigurationError:
|
||||
{
|
||||
WriteJsonIssue(context.TargetUri.LocalPath, context.Rule.Id, message, ResultKind.ConfigurationError);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.InternalError:
|
||||
{
|
||||
WriteJsonIssue(context.TargetUri.LocalPath, context.Rule.Id, message, ResultKind.InternalError);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
private void WriteJsonIssue(string binary, string ruleId, string message, ResultKind issueKind)
|
||||
{
|
||||
Result result = new Result();
|
||||
|
||||
result.RuleId = ruleId;
|
||||
result.FullMessage = message;
|
||||
result.Kind = issueKind;
|
||||
result.Locations = new[]{
|
||||
new Sarif.Sdk.Location {
|
||||
AnalysisTarget = new[]
|
||||
{
|
||||
new PhysicalLocationComponent
|
||||
{
|
||||
// Why? When NewtonSoft serializes this Uri, it will use the
|
||||
// original string used to construct the Uri. For a file path,
|
||||
// this will be the local file path. We want to persist this
|
||||
// information using the file:// protocol rendering, however.
|
||||
Uri = binary.CreateUriForJsonSerialization(),
|
||||
MimeType = MimeType.Binary
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_issueLogJsonWriter.WriteResult(result);
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, FormattedMessage message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SdkResources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SdkResources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CodeAnalysis.Sarif.Driver.Sdk.SdkResources", typeof(SdkResources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Analyzing {0}....
|
||||
/// </summary>
|
||||
internal static string Analyzing {
|
||||
get {
|
||||
return ResourceManager.GetString("Analyzing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to InvalidTarget_Description.
|
||||
/// </summary>
|
||||
internal static string AnalyzingTarget_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("AnalyzingTarget_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised analyzing '{0}' for check '{1}' (which has been disabled for the remainder of the analysis). The exception may have resulted from a problem related to parsing image metadata and not specific to the rule, however. Exception information:
|
||||
///{2}.
|
||||
/// </summary>
|
||||
internal static string ExceptionAnalyzingTarget {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionAnalyzingTarget", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised attempting to determine whether '{0}' is a valid analysis target for check '{1}' (which has been disabled for the remainder of the analysis). The exception may have resulted from a problem related to parsing the analysis target and not specific to the rule, however. Exception information:
|
||||
///{2}.
|
||||
/// </summary>
|
||||
internal static string ExceptionCheckingApplicability {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionCheckingApplicability", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised attempting to create output file '{0}'. Exception information:
|
||||
///{1}.
|
||||
/// </summary>
|
||||
internal static string ExceptionCreatingLogFile {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionCreatingLogFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised during analysis:
|
||||
///{0}.
|
||||
/// </summary>
|
||||
internal static string ExceptionInAnalysisEngine {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionInAnalysisEngine", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised in the analysis engine..
|
||||
/// </summary>
|
||||
internal static string ExceptionInAnalysisEngine_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionInAnalysisEngine_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised initializing check '{0}' (which has been disabled for the remainder of the analysis). Exception information:
|
||||
///{1}.
|
||||
/// </summary>
|
||||
internal static string ExceptionInitializingRule {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionInitializingRule", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised in an analysis rule, indicating an issue with the rule itself or a problem inspecting the binary being analyzed..
|
||||
/// </summary>
|
||||
internal static string ExceptionInRule_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionInRule_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised attempting to load analysis plug-in '{0}'. Exception information:
|
||||
///{1}.
|
||||
/// </summary>
|
||||
internal static string ExceptionLoadingAnalysisPlugIn {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionLoadingAnalysisPlugIn", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised attempting to load analysis target '{0}'. Exception information:
|
||||
///{1}.
|
||||
/// </summary>
|
||||
internal static string ExceptionLoadingAnalysisTarget {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionLoadingAnalysisTarget", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An unhandled exception was raised while configuring analysis for execution..
|
||||
/// </summary>
|
||||
internal static string InvalidConfiguration_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidConfiguration_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A file was skipped as it does not appear to be a valid target for analysis..
|
||||
/// </summary>
|
||||
internal static string InvalidTarget_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidTarget_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No analyzer paths specified.
|
||||
/// </summary>
|
||||
internal static string NoAnalyzerPathsSpecified {
|
||||
get {
|
||||
return ResourceManager.GetString("NoAnalyzerPathsSpecified", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A parse error occurred: {0}.
|
||||
/// </summary>
|
||||
internal static string ParseError {
|
||||
get {
|
||||
return ResourceManager.GetString("ParseError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A parse error occurred..
|
||||
/// </summary>
|
||||
internal static string ParseError_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("ParseError_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Check '{0}' was disabled for this run as the analysis was not configured with required policy ({1}). To resolve this, configure and provide a policy file on the BinSkim command-line using the --policy argument (recommended), or pass --defaultPolicy to invoke built-in settings. Invoke the BinSkim.exe 'export' command to produce an initial policy file that can be edited if required and passed back into the tool..
|
||||
/// </summary>
|
||||
internal static string RuleWasDisabledDueToMissingPolicy {
|
||||
get {
|
||||
return ResourceManager.GetString("RuleWasDisabledDueToMissingPolicy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to '{0}' was not analyzed as it does not appear to be a valid target for analysis..
|
||||
/// </summary>
|
||||
internal static string TargetNotAnalyzed_InvalidTarget {
|
||||
get {
|
||||
return ResourceManager.GetString("TargetNotAnalyzed_InvalidTarget", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to '{0}' was not evaluated for check '{1}' as the analysis is not relevant based on observed binary metadata: {2}..
|
||||
/// </summary>
|
||||
internal static string TargetNotAnalyzed_NotApplicable {
|
||||
get {
|
||||
return ResourceManager.GetString("TargetNotAnalyzed_NotApplicable", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Application exited unexpectedly..
|
||||
/// </summary>
|
||||
internal static string UnexpectedApplicationExit {
|
||||
get {
|
||||
return ResourceManager.GetString("UnexpectedApplicationExit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An exception was raised during analysis:
|
||||
///{0}.
|
||||
/// </summary>
|
||||
internal static string UnhandledEngineException {
|
||||
get {
|
||||
return ResourceManager.GetString("UnhandledEngineException", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Analyzing" xml:space="preserve">
|
||||
<value>Analyzing {0}...</value>
|
||||
</data>
|
||||
<data name="AnalyzingTarget_Description" xml:space="preserve">
|
||||
<value>InvalidTarget_Description</value>
|
||||
</data>
|
||||
<data name="ExceptionAnalyzingTarget" xml:space="preserve">
|
||||
<value>An exception was raised analyzing '{0}' for check '{1}' (which has been disabled for the remainder of the analysis). The exception may have resulted from a problem related to parsing image metadata and not specific to the rule, however. Exception information:
|
||||
{2}</value>
|
||||
</data>
|
||||
<data name="ExceptionCheckingApplicability" xml:space="preserve">
|
||||
<value>An exception was raised attempting to determine whether '{0}' is a valid analysis target for check '{1}' (which has been disabled for the remainder of the analysis). The exception may have resulted from a problem related to parsing the analysis target and not specific to the rule, however. Exception information:
|
||||
{2}</value>
|
||||
</data>
|
||||
<data name="ExceptionCreatingLogFile" xml:space="preserve">
|
||||
<value>An exception was raised attempting to create output file '{0}'. Exception information:
|
||||
{1}</value>
|
||||
</data>
|
||||
<data name="ExceptionInAnalysisEngine" xml:space="preserve">
|
||||
<value>An exception was raised during analysis:
|
||||
{0}</value>
|
||||
</data>
|
||||
<data name="ExceptionInAnalysisEngine_Description" xml:space="preserve">
|
||||
<value>An exception was raised in the analysis engine.</value>
|
||||
</data>
|
||||
<data name="ExceptionInitializingRule" xml:space="preserve">
|
||||
<value>An exception was raised initializing check '{0}' (which has been disabled for the remainder of the analysis). Exception information:
|
||||
{1}</value>
|
||||
</data>
|
||||
<data name="ExceptionInRule_Description" xml:space="preserve">
|
||||
<value>An exception was raised in an analysis rule, indicating an issue with the rule itself or a problem inspecting the binary being analyzed.</value>
|
||||
</data>
|
||||
<data name="ExceptionLoadingAnalysisPlugIn" xml:space="preserve">
|
||||
<value>An exception was raised attempting to load analysis plug-in '{0}'. Exception information:
|
||||
{1}</value>
|
||||
</data>
|
||||
<data name="ExceptionLoadingAnalysisTarget" xml:space="preserve">
|
||||
<value>An exception was raised attempting to load analysis target '{0}'. Exception information:
|
||||
{1}</value>
|
||||
</data>
|
||||
<data name="InvalidConfiguration_Description" xml:space="preserve">
|
||||
<value>An unhandled exception was raised while configuring analysis for execution.</value>
|
||||
</data>
|
||||
<data name="InvalidTarget_Description" xml:space="preserve">
|
||||
<value>A file was skipped as it does not appear to be a valid target for analysis.</value>
|
||||
</data>
|
||||
<data name="NoAnalyzerPathsSpecified" xml:space="preserve">
|
||||
<value>No analyzer paths specified</value>
|
||||
</data>
|
||||
<data name="RuleWasDisabledDueToMissingPolicy" xml:space="preserve">
|
||||
<value>Check '{0}' was disabled for this run as the analysis was not configured with required policy ({1}). To resolve this, configure and provide a policy file on the BinSkim command-line using the --policy argument (recommended), or pass --defaultPolicy to invoke built-in settings. Invoke the BinSkim.exe 'export' command to produce an initial policy file that can be edited if required and passed back into the tool.</value>
|
||||
</data>
|
||||
<data name="TargetNotAnalyzed_InvalidTarget" xml:space="preserve">
|
||||
<value>'{0}' was not analyzed as it does not appear to be a valid target for analysis.</value>
|
||||
</data>
|
||||
<data name="TargetNotAnalyzed_NotApplicable" xml:space="preserve">
|
||||
<value>'{0}' was not evaluated for check '{1}' as the analysis is not relevant based on observed binary metadata: {2}.</value>
|
||||
</data>
|
||||
<data name="UnexpectedApplicationExit" xml:space="preserve">
|
||||
<value>Application exited unexpectedly.</value>
|
||||
</data>
|
||||
<data name="UnhandledEngineException" xml:space="preserve">
|
||||
<value>An exception was raised during analysis:
|
||||
{0}</value>
|
||||
</data>
|
||||
<data name="ParseError" xml:space="preserve">
|
||||
<value>A parse error occurred: {0}</value>
|
||||
</data>
|
||||
<data name="ParseError_Description" xml:space="preserve">
|
||||
<value>A parse error occurred.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,72 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public abstract class SkimmerBase<TContext> : ISkimmer<TContext>
|
||||
{
|
||||
public SkimmerBase()
|
||||
{
|
||||
this.Options = new Dictionary<string, string>();
|
||||
this.FormatSpecifiers = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public Dictionary<string, string> FormatSpecifiers { get; }
|
||||
|
||||
abstract public string Id { get; }
|
||||
|
||||
abstract public string FullDescription { get; }
|
||||
|
||||
public virtual string ShortDescription
|
||||
{
|
||||
get { return FirstSentence(FullDescription); }
|
||||
}
|
||||
|
||||
internal static string FirstSentence(string fullDescription)
|
||||
{
|
||||
int charCount = 0;
|
||||
bool withinApostrophe = false;
|
||||
|
||||
foreach (char ch in fullDescription)
|
||||
{
|
||||
charCount++;
|
||||
switch (ch)
|
||||
{
|
||||
case '\'':
|
||||
{
|
||||
withinApostrophe = !withinApostrophe;
|
||||
continue;
|
||||
}
|
||||
|
||||
case '.':
|
||||
{
|
||||
if (withinApostrophe) { continue; }
|
||||
return fullDescription.Substring(0, charCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
int length = Math.Min(fullDescription.Length, 80);
|
||||
bool truncated = length < fullDescription.Length;
|
||||
return fullDescription.Substring(0, length) + (truncated ? "..." : "");
|
||||
}
|
||||
|
||||
public virtual string Name { get { return this.GetType().Name; } }
|
||||
|
||||
public Dictionary<string, string> Options { get; }
|
||||
|
||||
public Dictionary<string, string> Properties { get; }
|
||||
|
||||
public virtual void Initialize(TContext context) { }
|
||||
|
||||
public virtual AnalysisApplicability CanAnalyze(TContext context, out string reasonIfNotApplicable)
|
||||
{
|
||||
reasonIfNotApplicable = null;
|
||||
return AnalysisApplicability.ApplicableToSpecifiedTarget;
|
||||
}
|
||||
|
||||
public abstract void Analyze(TContext context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
// 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.Sdk;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Sarif.Driver.Sdk
|
||||
{
|
||||
public class StatisticsLogger : IResultLogger
|
||||
{
|
||||
private Stopwatch _stopwatch;
|
||||
private long _targetsCount;
|
||||
private long _invalidTargetsCount;
|
||||
|
||||
public StatisticsLogger()
|
||||
{
|
||||
_stopwatch = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, string message)
|
||||
{
|
||||
switch (messageKind)
|
||||
{
|
||||
|
||||
case ResultKind.Pass:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Error:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Warning:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.NotApplicable:
|
||||
{
|
||||
if (context.Rule.Id == NoteDescriptors.InvalidTarget.Id)
|
||||
{
|
||||
_invalidTargetsCount++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.Note:
|
||||
{
|
||||
if (context.Rule.Id == NoteDescriptors.AnalyzingTarget.Id)
|
||||
{
|
||||
_targetsCount++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.InternalError:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case ResultKind.ConfigurationError:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("# valid targets: " + _targetsCount.ToString());
|
||||
Console.WriteLine("# invalid targets: " + _invalidTargetsCount.ToString());
|
||||
Console.WriteLine("Time elapsed: " + _stopwatch.Elapsed.ToString());
|
||||
}
|
||||
|
||||
public void Log(ResultKind messageKind, IAnalysisContext context, FormattedMessage message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace Microsoft.CodeAnalysis.Sarif
|
|||
public static class VersionConstants
|
||||
{
|
||||
public const string Prerelease = "-beta";
|
||||
public const string AssemblyVersion = "1.0.6";
|
||||
public const string AssemblyVersion = "1.0.9";
|
||||
public const string FileVersion = AssemblyVersion + ".0";
|
||||
public const string Version = AssemblyVersion + Prerelease;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче