Skip to content

Commit de850fc

Browse files
committed
Merge pull request #169 from huangpf/dev
Dev
2 parents f9fdbbb + c2e7484 commit de850fc

28 files changed

+1350
-29
lines changed

AzurePowershell.Test.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<AsmXUnitTests Include=".\src\ServiceManagement\Network\Commands.Network.Test\bin\Debug\Microsoft.WindowsAzure.Commands.ServiceManagement.Network.Test.dll"/>
6767
</ItemGroup>
6868
<ItemGroup Condition=" '$(scope)' == 'all' ">
69+
<XUnitTests Include=".\tools\StaticAnalysis\StaticAnalysis.Test\bin\Debug\StaticAnalysis.Test.dll"/>
6970
<XUnitTests Include=".\src\ResourceManager\SiteRecovery\Commands.SiteRecovery.Test\bin\Debug\Microsoft.Azure.Commands.SiteRecovery.Test.dll"/>
7071
<XUnitTests Include=".\src\ResourceManager\Sql\Commands.Sql.Test\bin\Debug\Microsoft.Azure.Commands.Sql.Test.dll"/>
7172
<XUnitTests Include=".\src\ResourceManager\Resources\Commands.Resources.Test\bin\Debug\Microsoft.Azure.Commands.Resources.Test.dll"/>

build.proj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@
137137
<!-- Restore packages -->
138138
<Exec Command="$(NuGetCommand) restore %(CmdletSolutionsToBuild.FullPath) $(NuGetRestoreConfigSwitch)"
139139
ContinueOnError="false" />
140+
<!-- Restore packages for static analysis-->
141+
<Exec Command="$(NuGetCommand) restore %(StaticAnalysis.FullPath) $(NuGetRestoreConfigSwitch)"
142+
ContinueOnError="false" />
140143

141144
<!--Restore the xunit runner needed to run unit tests-->
142145
<Exec Command="$(NuGetCommand) restore $(MSBuildProjectDirectory)\packages.config -PackagesDirectory $(MSBuildProjectDirectory)\packages" />

tools/StaticAnalysis/AnalysisLogger.cs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,27 @@ namespace StaticAnalysis
2626
public abstract class AnalysisLogger
2727
{
2828
string _baseDirectory;
29+
string _exceptionsDirectory;
2930

3031
/// <summary>
3132
/// Create an analysis logger that will write reports to the given directory
3233
/// </summary>
33-
/// <param name="baseDirectory"></param>
34+
/// <param name="baseDirectory">The base directory for reports</param>
35+
/// <param name="exceptionsDirectory">The directory containing exceptions form static analysis rules.</param>
36+
public AnalysisLogger(string baseDirectory, string exceptionsDirectory)
37+
{
38+
_baseDirectory = baseDirectory;
39+
_exceptionsDirectory = exceptionsDirectory;
40+
}
41+
42+
/// <summary>
43+
/// Create an analysis logger without exceptions
44+
/// </summary>
45+
/// <param name="baseDirectory">The base directory for reports</param>
3446
public AnalysisLogger(string baseDirectory)
3547
{
3648
_baseDirectory = baseDirectory;
49+
_exceptionsDirectory = null;
3750
}
3851

3952
IList<ReportLogger> _loggers = new List<ReportLogger>();
@@ -86,15 +99,27 @@ public virtual void WriteWarning(string format, params object[] args)
8699
/// <param name="fileName">The filename (without file path) where the report will be written</param>
87100
/// <returns>The given logger. Analyzer may write records to this logger and they will be written to
88101
/// the report file.</returns>
89-
public virtual ReportLogger<T> CreateLogger<T>(string fileName) where T : IReportRecord, new()
102+
public virtual ReportLogger<T> CreateLogger<T>(string fileName) where T : class, IReportRecord, new()
90103
{
91104
if (string.IsNullOrWhiteSpace(fileName))
92105
{
93106
throw new ArgumentNullException("fileName");
94107
}
95108

96109
var filePath = Path.Combine(_baseDirectory, fileName);
97-
var logger = new ReportLogger<T>(filePath, this);
110+
ReportLogger<T> logger;
111+
if (_exceptionsDirectory != null && Directory.Exists(_exceptionsDirectory))
112+
{
113+
var exceptionsPath = Path.Combine(_exceptionsDirectory, fileName);
114+
WriteWarning("Using exceptions file {0}", exceptionsPath);
115+
logger = new ReportLogger<T>(filePath, exceptionsPath, this);
116+
}
117+
else
118+
{
119+
WriteWarning("Using no exceptions file.");
120+
logger = new ReportLogger<T>(filePath, this);
121+
}
122+
98123
Loggers.Add(logger);
99124
return logger;
100125
}
@@ -116,5 +141,31 @@ public void WriteReports()
116141
WriteReport(logger.FileName, reportText.ToString());
117142
}
118143
}
144+
145+
public void CheckForIssues(int maxSeverity)
146+
{
147+
var hasErrors = false;
148+
foreach (var logger in Loggers.Where(l => l.Records.Any(r => r.Severity < maxSeverity)))
149+
{
150+
hasErrors = true;
151+
StringBuilder errorText = new StringBuilder();
152+
errorText.AppendLine(logger.Records.First().PrintHeaders());
153+
foreach (var reportRecord in logger.Records)
154+
{
155+
errorText.AppendLine(reportRecord.FormatRecord());
156+
}
157+
158+
WriteError("{0} Errors", logger.FileName);
159+
WriteError(errorText.ToString());
160+
WriteError("");
161+
}
162+
163+
if (hasErrors)
164+
{
165+
throw new InvalidOperationException(string.Format("One or more errors occurred in validation. " +
166+
"See the analysis repots at {0} for details",
167+
_baseDirectory));
168+
}
169+
}
119170
}
120171
}

tools/StaticAnalysis/ConsoleLogger.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ namespace StaticAnalysis
2323
public class ConsoleLogger : AnalysisLogger
2424
{
2525

26+
public ConsoleLogger(string baseDirectory, string exceptionsDirectory)
27+
: base(baseDirectory, exceptionsDirectory)
28+
{
29+
}
30+
2631
public ConsoleLogger(string baseDirectory)
2732
: base(baseDirectory)
2833
{
2934
}
30-
3135
public override void WriteError(string error)
3236
{
3337
Console.WriteLine("### ERROR {0}", error);

tools/StaticAnalysis/DependencyAnalyzer/AssemblyRecord.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ public override string ToString()
114114

115115
}
116116

117+
public override int GetHashCode()
118+
{
119+
return string.Format("{0}-{1}-{2}", AssemblyName, AssemblyFileMajorVersion, AssemblyFileMinorVersion).GetHashCode();
120+
}
121+
122+
117123
/// <summary>
118124
/// Get all the ancestors in the ancestor tree
119125
/// </summary>

tools/StaticAnalysis/DependencyAnalyzer/AssemblyVersionConflict.cs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
// ----------------------------------------------------------------------------------
1414

1515
using System;
16+
using System.IO;
17+
using System.Text.RegularExpressions;
1618

1719
namespace StaticAnalysis.DependencyAnalyzer
1820
{
@@ -46,6 +48,11 @@ public class AssemblyVersionConflict : IReportRecord
4648
/// </summary>
4749
public string ParentAssembly { get; set; }
4850

51+
/// <summary>
52+
/// Machine readable identity of the problem
53+
/// </summary>
54+
public int ProblemId { get; set; }
55+
4956
/// <summary>
5057
/// A textual description of the problem
5158
/// </summary>
@@ -62,16 +69,52 @@ public string PrintHeaders()
6269
{
6370
return
6471
"\"Directory\",\"AssemblyName\",\"Expected Version\",\"Actual Version\",\"Parent Assembly\",\"Severity\"," +
65-
"\"Description\",\"Remediation\"";
72+
"\"ProblemId\",\"Description\",\"Remediation\"";
6673
}
6774

6875
public string FormatRecord()
6976
{
7077
return
7178
string.Format(
72-
"\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\"",
79+
"\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\"",
7380
Directory, AssemblyName, ExpectedVersion, ActualVersion, ParentAssembly, Severity,
74-
Description, Remediation);
81+
ProblemId, Description, Remediation);
82+
}
83+
public bool Match(IReportRecord other)
84+
{
85+
var result = false;
86+
var record = other as AssemblyVersionConflict;
87+
if (record != null)
88+
{
89+
result = string.Equals(EnvironmentHelpers.GetDirectoryName(record.Directory),
90+
EnvironmentHelpers.GetDirectoryName(Directory), StringComparison.OrdinalIgnoreCase)
91+
&& string.Equals(record.AssemblyName, AssemblyName, StringComparison.OrdinalIgnoreCase)
92+
&& string.Equals(record.ParentAssembly, ParentAssembly, StringComparison.OrdinalIgnoreCase)
93+
&&record.ProblemId == ProblemId;
94+
}
95+
96+
return result;
97+
}
98+
99+
public IReportRecord Parse(string line)
100+
{
101+
var matcher = "\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\"";
102+
var match = Regex.Match(line, matcher);
103+
if (!match.Success || match.Groups.Count < 10)
104+
{
105+
throw new InvalidOperationException(string.Format("Could not parse '{0}' as AssemblyVersionConflict record", line));
106+
}
107+
108+
Directory = match.Groups[1].Value;
109+
AssemblyName = match.Groups[2].Value;
110+
ExpectedVersion = Version.Parse(match.Groups[3].Value);
111+
ActualVersion = Version.Parse(match.Groups[4].Value);
112+
ParentAssembly = match.Groups[5].Value;
113+
Severity = int.Parse(match.Groups[6].Value);
114+
ProblemId = int.Parse(match.Groups[7].Value);
115+
Description = match.Groups[8].Value;
116+
Remediation = match.Groups[9].Value;
117+
return this;
75118
}
76119

77120
public override string ToString()

tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// ----------------------------------------------------------------------------------
1414

1515
using System;
16+
using System.CodeDom;
1617
using System.Collections.Generic;
1718
using System.Diagnostics;
1819
using System.IO;
@@ -26,6 +27,21 @@ namespace StaticAnalysis.DependencyAnalyzer
2627
/// </summary>
2728
public class DependencyAnalyzer : IStaticAnalyzer
2829
{
30+
const int NoAssemblyVersionEvidence = 1000;
31+
const int ReferenceDoesNotMatchAssemblyVersion = 1010;
32+
const int ExtraAssemblyRecord = 2000;
33+
const int MissingAssemblyRecord = 3000;
34+
const int AssemblyVersionFileVersionMismatch = 7000;
35+
const int CommonAuthenticationMismatch = 7010;
36+
37+
static List<string> FrameworkAssemblies = new List<string>
38+
{
39+
"Microsoft.CSharp",
40+
"Microsoft.Management.Infrastructure",
41+
"Microsoft.Build",
42+
"Microsoft.Build.Framework"
43+
};
44+
2945
private Dictionary<string, AssemblyRecord> _assemblies =
3046
new Dictionary<string, AssemblyRecord>(StringComparer.OrdinalIgnoreCase);
3147
private Dictionary<AssemblyName, AssemblyRecord> _sharedAssemblyReferences =
@@ -130,6 +146,7 @@ private bool AddSharedAssembly(AssemblyRecord assembly)
130146
},
131147
AssemblyVersion = assembly.Version,
132148
Severity = 0,
149+
ProblemId = AssemblyVersionFileVersionMismatch,
133150
Description = "Shared assembly conflict, shared assemblies with the same assembly " +
134151
"version have differing file versions",
135152
Remediation = string.Format("Update the assembly reference for {0} in one of the " +
@@ -170,6 +187,7 @@ private bool AddSharedAssemblyExactVersion(AssemblyRecord record)
170187
AssemblyName = record.Name,
171188
AssemblyVersion = record.Version,
172189
Severity = 0,
190+
ProblemId = CommonAuthenticationMismatch,
173191
AssemblyPathsAndFileVersions = new List<Tuple<string, Version>>()
174192
{
175193
new Tuple<string, Version>(record.Location, new Version(record.AssemblyFileMajorVersion,
@@ -202,14 +220,15 @@ private static bool RequiresExactVersionMatch(AssemblyRecord name)
202220

203221
private static bool IsFrameworkAssembly(AssemblyName name)
204222
{
205-
return name.Name.StartsWith("System") || name.Name.Equals("mscorlib");
223+
return name.Name.StartsWith("System") || name.Name.Equals("mscorlib")
224+
|| FrameworkAssemblies.Contains(name.Name);
206225
}
207226

208227
private void ProcessDirectory(string directoryPath)
209228
{
210229
var savedDirectory = Directory.GetCurrentDirectory();
211230
Directory.SetCurrentDirectory(directoryPath);
212-
_loader = AppDomainHelpers.CreateProxy<AssemblyLoader>(directoryPath, out _testDomain);
231+
_loader = EnvironmentHelpers.CreateProxy<AssemblyLoader>(directoryPath, out _testDomain);
213232
foreach (var file in Directory.GetFiles(directoryPath).Where(file => file.EndsWith(".dll")))
214233
{
215234
AssemblyRecord assembly = CreateAssemblyRecord(file);
@@ -258,6 +277,7 @@ var assembly in
258277
{
259278
AssemblyName = assembly.Name,
260279
Severity = 2,
280+
ProblemId = ExtraAssemblyRecord,
261281
Description = string.Format("Assembly {0} is not referenced from any cmdlets assembly",
262282
assembly.Name),
263283
Remediation = string.Format("Remove assembly {0} from the project and regenerate the Wix " +
@@ -286,6 +306,7 @@ private void CheckAssemblyReference(AssemblyName reference, AssemblyRecord paren
286306
ActualVersion = stored.Version,
287307
ExpectedVersion = reference.Version,
288308
ParentAssembly = parent.Name,
309+
ProblemId = NoAssemblyVersionEvidence,
289310
Severity = 2,
290311
Description = string.Format("Assembly {0} referenced from {1}.dll does not specify any " +
291312
"assembly version evidence. The assembly will use version " +
@@ -304,6 +325,7 @@ private void CheckAssemblyReference(AssemblyName reference, AssemblyRecord paren
304325
ActualVersion = stored.Version,
305326
ExpectedVersion = reference.Version,
306327
ParentAssembly = parent.Name,
328+
ProblemId = ReferenceDoesNotMatchAssemblyVersion,
307329
Severity = 1,
308330
Description = string.Format("Assembly {0} version {1} referenced from {2}.dll does " +
309331
"not match assembly version on disk: {3}",
@@ -321,6 +343,7 @@ private void CheckAssemblyReference(AssemblyName reference, AssemblyRecord paren
321343
AssemblyVersion = reference.Version.ToString(),
322344
ReferencingAssembly = parent.Name,
323345
Severity = 0,
346+
ProblemId = MissingAssemblyRecord,
324347
Description = string.Format("Missing assembly {0} referenced from {1}", reference.Name,
325348
parent.Name),
326349
Remediation = "Ensure that the assembly is included in the Wix file or directory"

tools/StaticAnalysis/DependencyAnalyzer/ExtraAssembly.cs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15+
using System;
16+
using System.Text.RegularExpressions;
17+
1518
namespace StaticAnalysis.DependencyAnalyzer
1619
{
1720
/// <summary>
@@ -25,25 +28,61 @@ public class ExtraAssembly : IReportRecord
2528

2629
public int Severity { get; set; }
2730

31+
public int ProblemId { get; set; }
32+
2833
public string Description { get; set; }
2934

3035
public string Remediation { get; set; }
3136

3237

3338
public string PrintHeaders()
3439
{
35-
return "\"Directory\",\"AssemblyName\",\"Severity\",\"Description\",\"Remediation\"";
40+
return "\"Directory\",\"AssemblyName\",\"Severity\",\"ProblemId\",\"Description\",\"Remediation\"";
3641
}
3742

3843
public string FormatRecord()
3944
{
40-
return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\"",
41-
Directory, AssemblyName, Severity, Description, Remediation);
45+
return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\"",
46+
Directory, AssemblyName, Severity, ProblemId, Description, Remediation);
4247
}
4348

4449
public override string ToString()
4550
{
4651
return FormatRecord();
4752
}
53+
54+
55+
public bool Match(IReportRecord other)
56+
{
57+
var result = false;
58+
var record = other as ExtraAssembly;
59+
if (record != null)
60+
{
61+
result = string.Equals(EnvironmentHelpers.GetDirectoryName(record.Directory),
62+
EnvironmentHelpers.GetDirectoryName(Directory), StringComparison.OrdinalIgnoreCase)
63+
&& string.Equals(record.AssemblyName, AssemblyName, StringComparison.OrdinalIgnoreCase)
64+
&& record.ProblemId == ProblemId;
65+
}
66+
67+
return result;
68+
}
69+
70+
public IReportRecord Parse(string line)
71+
{
72+
var matcher = "\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\"";
73+
var match = Regex.Match(line, matcher);
74+
if (!match.Success || match.Groups.Count < 7)
75+
{
76+
throw new InvalidOperationException(string.Format("Could not parse '{0}' as ExtraAssembly record", line));
77+
}
78+
79+
Directory = match.Groups[1].Value;
80+
AssemblyName = match.Groups[2].Value;
81+
Severity = int.Parse(match.Groups[3].Value);
82+
ProblemId = int.Parse(match.Groups[4].Value);
83+
Description = match.Groups[5].Value;
84+
Remediation = match.Groups[6].Value;
85+
return this;
86+
}
4887
}
4988
}

0 commit comments

Comments
 (0)