static int Main(string[] args) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.Error.WriteLine("AuthenticodeLint is only supported on Windows."); return(ExitCodes.PlatformNotSupported); } var cli = Environment.CommandLine; List <CommandLineParameter>?parsedCommandLine; try { var commandLine = CommandLineParser.LexCommandLine(cli).Skip(1); parsedCommandLine = CommandLineParser.CreateCommandLineParametersWithValues(commandLine).ToList(); } catch (InvalidOperationException) { parsedCommandLine = null; } if (parsedCommandLine == null || parsedCommandLine.Count == 0 || parsedCommandLine.Any(cl => cl.Name == "help")) { ShowHelp(); //Avoid returning success for printing help so that automated build systems do not interpret "show the help" //As a successful build incase the build system is incorrectly passing arguments. return(ExitCodes.InvalidInputOrConfig); } var inputs = new List <string>(); var suppress = new HashSet <int>(); bool quiet = false; bool verbose = false; string?report = null; string?extract = null; var revocation = RevocationChecking.None; var ruleSet = RuleSet.Modern; foreach (var parameter in parsedCommandLine) { if (parameter.Name == "in") { if (string.IsNullOrWhiteSpace(parameter.Value)) { Console.Error.WriteLine("A value is required for input."); return(ExitCodes.InvalidInputOrConfig); } var filePattern = Path.GetFileName(parameter.Value); //The value contains a pattern. if (filePattern.Contains('*') || filePattern.Contains('?')) { var directory = Path.GetDirectoryName(parameter.Value); if (Directory.Exists(directory)) { var files = Directory.GetFiles(directory, filePattern, SearchOption.TopDirectoryOnly); inputs.AddRange(files); } } else { inputs.Add(parameter.Value); } } else if (parameter.Name == "suppress") { if (string.IsNullOrWhiteSpace(parameter.Value)) { ShowInvalidSuppression(); return(ExitCodes.InvalidInputOrConfig); } foreach (var idString in parameter.Value.Split(',').Select(p => p.Trim())) { int id; if (int.TryParse(idString, out id)) { suppress.Add(id); } else { Console.Error.WriteLine($"{idString} is not a valid error ID."); return(ExitCodes.InvalidInputOrConfig); } } } else if (parameter.Name == "q" || parameter.Name == "quiet") { if (!string.IsNullOrWhiteSpace(parameter.Value)) { Console.Error.WriteLine($"-{parameter.Name} does not expect a value."); return(ExitCodes.InvalidInputOrConfig); } quiet = true; } else if (parameter.Name == "verbose") { if (!string.IsNullOrWhiteSpace(parameter.Value)) { Console.Error.WriteLine($"-{parameter.Name} does not expect a value."); return(ExitCodes.InvalidInputOrConfig); } verbose = true; } else if (parameter.Name == "report") { report = parameter.Value; } else if (parameter.Name == "extract") { extract = parameter.Value; } else if (parameter.Name == "revocation") { if (string.IsNullOrWhiteSpace(parameter.Value)) { Console.Error.WriteLine($"-{parameter.Name} requires a value if specified."); return(ExitCodes.InvalidInputOrConfig); } if (!Enum.TryParse(parameter.Value, true, out revocation)) { Console.Error.WriteLine($"-{parameter.Value} is an unrecognized revocation mode."); return(ExitCodes.InvalidInputOrConfig); } } else if (parameter.Name == "ruleset") { if (string.IsNullOrWhiteSpace(parameter.Value)) { Console.Error.WriteLine($"-{parameter.Name} requires a value if specified."); return(ExitCodes.InvalidInputOrConfig); } if (!Enum.TryParse(parameter.Value, true, out ruleSet) || parameter.Value.Equals("all", StringComparison.OrdinalIgnoreCase)) { Console.Error.WriteLine($"-{parameter.Value} is an unrecognized ruleset."); return(ExitCodes.InvalidInputOrConfig); } } else { Console.Error.WriteLine($"-{parameter.Name} is an unknown parameter."); return(ExitCodes.InvalidInputOrConfig); } } if (inputs.Count == 0) { Console.Error.WriteLine("Input is expected. See -help for usage."); return(ExitCodes.InvalidInputOrConfig); } var configuration = new CheckConfiguration(inputs, report, quiet, suppress, verbose, revocation, extract, ruleSet); if (!ConfigurationValidator.ValidateAndPrint(configuration, Console.Error)) { return(ExitCodes.InvalidInputOrConfig); } var collectors = new List <IRuleResultCollector>(); if (!quiet) { collectors.Add(new StdOutRuleResultCollector()); } if (!string.IsNullOrWhiteSpace(report)) { collectors.Add(new XmlRuleResultCollector(report)); } var result = ExitCodes.Success; foreach (var file in inputs) { var signatures = SignatureTreeInspector.Extract(file); if (CheckEngine.Instance.RunAllRules(file, signatures, collectors, configuration) != RuleEngineResult.AllPass) { result = ExitCodes.ChecksFailed; } } collectors.ForEach(c => c.Flush()); return(result); }
public RuleEngineResult RunAllRules(string file, IReadOnlyList <ISignature> signatures, List <IRuleResultCollector> collectors, CheckConfiguration configuration) { var verbose = configuration.Verbose; var suppressedRuleIDs = configuration.SuppressErrorIDs; var rules = GetRules(); var engineResult = RuleEngineResult.AllPass; collectors.ForEach(c => c.BeginSet(file)); foreach (var rule in rules) { RuleResult result; var verboseWriter = verbose ? new MemorySignatureLogger() : SignatureLogger.Null; if (signatures.Count == 0) { result = RuleResult.Fail; verboseWriter.LogMessage("File is not Authenticode signed."); } else { if (suppressedRuleIDs.Contains(rule.RuleId)) { result = RuleResult.Skip; } else if (rule is IAuthenticodeFileRule) { result = ((IAuthenticodeFileRule)rule).Validate(file, verboseWriter, configuration); } else if (rule is IAuthenticodeSignatureRule) { result = ((IAuthenticodeSignatureRule)rule).Validate(signatures, verboseWriter, configuration); } else { throw new NotSupportedException("Rule type is not supported."); } } if (result == RuleResult.Fail) { engineResult = RuleEngineResult.NotAllPass; } collectors.ForEach(c => c.CollectResult(rule, result, verboseWriter.Messages)); } if (configuration.ExtractPath != null) { Extraction.ExtractToDisk(file, configuration, signatures); } collectors.ForEach(c => c.CompleteSet()); return(engineResult); }