in DevSkim-DotNet/Microsoft.DevSkim.CLI/Commands/AnalyzeCommand.cs [237:391]
public int RunFileEntries(IEnumerable<FileEntry> fileListing, StreamWriter? outputStreamWriter = null)
{
Verifier? verifier = null;
if (opts.Rulespath.Length > 0)
{
// Setup the rules
verifier = new Verifier(opts.Rulespath);
if (!verifier.Verify())
return (int)ExitCode.CriticalError;
if (verifier.CompiledRuleset.Count() == 0 && opts.IgnoreDefaultRules)
{
Debug.WriteLine("Error: No rules were loaded. ");
return (int)ExitCode.CriticalError;
}
}
RuleSet rules = new RuleSet();
if (verifier != null)
rules = verifier.CompiledRuleset;
if (!opts.IgnoreDefaultRules)
{
Assembly? assembly = Assembly.GetAssembly(typeof(Boundary));
string filePath = "Microsoft.DevSkim.Resources.devskim-rules.json";
Stream? resource = assembly?.GetManifestResourceStream(filePath);
if (resource is Stream)
{
using (StreamReader file = new StreamReader(resource))
{
var rulesString = file.ReadToEnd();
rules.AddString(rulesString, filePath, null);
}
}
}
// Initialize the processor
RuleProcessor processor = new RuleProcessor(rules);
processor.EnableSuppressions = !opts.DisableSuppression;
if (opts.Severities.Count() > 0)
{
processor.SeverityLevel = 0;
foreach (string severityText in opts.Severities)
{
Severity severity;
if (ParseSeverity(severityText, out severity))
{
processor.SeverityLevel |= severity;
}
else
{
Debug.WriteLine("Invalid severity: {0}", severityText);
return (int)ExitCode.CriticalError;
}
}
}
Writer outputWriter = WriterFactory.GetWriter(string.IsNullOrEmpty(opts.OutputFileFormat) ? "text" : opts.OutputFileFormat,
opts.OutputTextFormat,
(outputStreamWriter is null)?(string.IsNullOrEmpty(opts.OutputFile) ? Console.Out : File.CreateText(opts.OutputFile)):outputStreamWriter,
(outputStreamWriter is null)?opts.OutputFile:null);
int filesAnalyzed = 0;
int filesSkipped = 0;
int filesAffected = 0;
int issuesCount = 0;
void parseFileEntry(FileEntry fileEntry)
{
Uri baseUri = new Uri(Path.GetFullPath(opts.Path));
string language = Language.FromFileName(fileEntry.FullPath);
// Skip files written in unknown language
if (string.IsNullOrEmpty(language))
{
Interlocked.Increment(ref filesSkipped);
}
else
{
string fileText = string.Empty;
try
{
using (StreamReader reader = new StreamReader(fileEntry.Content))
{
fileText = reader.ReadToEnd();
}
Interlocked.Increment(ref filesAnalyzed);
}
catch (Exception)
{
// Skip files we can't parse
Interlocked.Increment(ref filesSkipped);
return;
}
Issue[] issues = processor.Analyze(fileText, language);
bool issuesFound = issues.Any(iss => !iss.IsSuppressionInfo) || opts.DisableSuppression && issues.Any();
if (issuesFound)
{
Interlocked.Increment(ref filesAffected);
Debug.WriteLine("file:{0}", fileEntry.FullPath);
// Iterate through each issue
foreach (Issue issue in issues)
{
if (!issue.IsSuppressionInfo || opts.DisableSuppression)
{
Interlocked.Increment(ref issuesCount);
Debug.WriteLine("\tregion:{0},{1},{2},{3} - {4} [{5}] - {6}",
issue.StartLocation.Line,
issue.StartLocation.Column,
issue.EndLocation.Line,
issue.EndLocation.Column,
issue.Rule.Id,
issue.Rule.Severity,
issue.Rule.Name);
IssueRecord record = new IssueRecord(
Filename: TryRelativizePath(opts.BasePath, fileEntry.FullPath),
Filesize: fileText.Length,
TextSample: fileText.Substring(issue.Boundary.Index, issue.Boundary.Length),
Issue: issue,
Language: language);
outputWriter.WriteIssue(record);
}
}
}
}
}
//Iterate through all files
if (opts.DisableParallel)
{
foreach (var fileEntry in fileListing)
{
parseFileEntry(fileEntry);
}
}
else
{
Parallel.ForEach(fileListing, parseFileEntry);
}
outputWriter.FlushAndClose();
Debug.WriteLine("Issues found: {0} in {1} files", issuesCount, filesAffected);
Debug.WriteLine("Files analyzed: {0}", filesAnalyzed);
Debug.WriteLine("Files skipped: {0}", filesSkipped);
return opts.ExitCodeIsNumIssues ? (issuesCount > 0 ? issuesCount : (int)ExitCode.NoIssues) : (int)ExitCode.NoIssues;
}