public void SearchSkimmer_AllowFileNameRegexMatchesProperly() { string scanTargetExtension = Guid.NewGuid().ToString(); SearchDefinition definition = null; AnalyzeContext context = CreateGuidMatchingSkimmer( scanTargetExtension: scanTargetExtension, ref definition, out SearchSkimmer skimmer, allowFileExtension: scanTargetExtension, denyFileExtension: null); AnalysisApplicability applicability = skimmer.CanAnalyze(context, out string reasonIfNotApplicable); applicability.Should().Be(AnalysisApplicability.ApplicableToSpecifiedTarget); reasonIfNotApplicable.Should().BeNull(); skimmer.Analyze(context); ValidateResultsAgainstDefinition(((TestLogger)context.Logger).Results, definition, skimmer); context.FileContents = null; ((TestLogger)context.Logger).Results.Clear(); skimmer.Analyze(context); ValidateResultsAgainstDefinition(((TestLogger)context.Logger).Results, definition, skimmer); }
private AnalyzeContext CreateGuidMatchingSkimmer( string scanTargetExtension, ref SearchDefinition definition, out SearchSkimmer skimmer, string allowFileExtension = null, string denyFileExtension = null, ValidatorsCache validators = null) { MatchExpression expression = CreateGuidDetectingMatchExpression( denyFileExtension: denyFileExtension, allowFileExtension: allowFileExtension); definition ??= CreateDefaultSearchDefinition(expression); var logger = new TestLogger(); var context = new AnalyzeContext { TargetUri = new Uri($"file:///c:/{definition.Name}.{definition.FileNameAllowRegex}.{scanTargetExtension}"), FileContents = definition.Id, Logger = logger }; var mockFileSystem = new Mock <IFileSystem>(); mockFileSystem.Setup(x => x.FileReadAllText(context.TargetUri.LocalPath)).Returns(definition.Id); skimmer = CreateSkimmer( definition, validators: validators, fileSystem: mockFileSystem.Object); return(context); }
private void ValidateResultsAgainstDefinition(IList <Result> results, SearchDefinition definition, SearchSkimmer skimmer) { results.Should().NotBeNull(); results.Count.Should().Be(1); results[0].RuleId.Should().Be(definition.Id); results[0].Level.Should().Be(definition.Level); results[0].GetMessageText(skimmer).Should().Be($"{definition.Message}"); }
private SearchSkimmer CreateSkimmer( SearchDefinition definition, IRegex engine = null, ValidatorsCache validators = null, FileRegionsCache fileRegionsCache = null, IFileSystem fileSystem = null) { var definitions = new SearchDefinitions { Definitions = new List <SearchDefinition>(new[] { definition }), }; definitions = AnalyzeCommand.PushInheritedData(definitions, sharedStrings: null); return(new SearchSkimmer( engine: engine ?? RE2Regex.Instance, validators: validators, fileRegionsCache: fileRegionsCache ?? new FileRegionsCache(), definition: definitions.Definitions[0], fileSystem: fileSystem)); }
public void SearchSkimmer_DenyFileNameRegexFiltersProperly() { string scanTargetExtension = Guid.NewGuid().ToString(); SearchDefinition definition = null; AnalyzeContext context = CreateGuidMatchingSkimmer( scanTargetExtension: scanTargetExtension, ref definition, out SearchSkimmer skimmer, allowFileExtension: null, denyFileExtension: scanTargetExtension); AnalysisApplicability applicability = skimmer.CanAnalyze(context, out string reasonIfNotApplicable); applicability.Should().Be(AnalysisApplicability.NotApplicableToSpecifiedTarget); reasonIfNotApplicable.Should().Be(SpamResources.TargetDoesNotMeetFileNameCriteria); skimmer.Analyze(context); ((TestLogger)context.Logger).Results.Should().BeNull(); }
public void SearchSkimmer_NoDetectionWhenMatchIsEmpty() { var expression = new MatchExpression(); SearchDefinition definition = CreateDefaultSearchDefinition(expression); string scanTargetContents = definition.Id; var logger = new TestLogger(); var context = new AnalyzeContext { TargetUri = new Uri($"file:///c:/{definition.Name}.Fake.asc"), FileContents = $"{ definition.Id}", Logger = logger }; SearchSkimmer skimmer = CreateSkimmer(definition); skimmer.Analyze(context); logger.Results.Should().BeNull(); }
public void SearchSkimmer_BothAllowAndDenyFileNameRegexFiltersProperly() { string scanTargetExtension = Guid.NewGuid().ToString(); // Analysis should not occur unless the file name both matches // the allow regex, if present, and does not match the deny // regex, if present. So, if the file name matches both the // allow and deny regex, we should not analyze. SearchDefinition definition = null; AnalyzeContext context = CreateGuidMatchingSkimmer( scanTargetExtension: scanTargetExtension, ref definition, out SearchSkimmer skimmer, allowFileExtension: scanTargetExtension, denyFileExtension: scanTargetExtension); skimmer.Analyze(context); ((TestLogger)context.Logger).Results.Should().BeNull(); }
public void SearchSkimmer_DetectsFilePatternOnly() { string fileExtension = Guid.NewGuid().ToString(); MatchExpression expr = CreateFileDetectingMatchExpression(fileExtension: fileExtension); SearchDefinition definition = CreateDefaultSearchDefinition(expr); string scanTargetContents = definition.Id; var logger = new TestLogger(); var context = new AnalyzeContext { TargetUri = new Uri($"file:///c:/{definition.Name}.Fake.{fileExtension}"), FileContents = definition.Id, Logger = logger }; SearchSkimmer skimmer = CreateSkimmer(definition); skimmer.Analyze(context); ValidateResultsAgainstDefinition(logger.Results, definition, skimmer); }
public void SearchSkimmer_ValidatorResultsAreProperlyPrioritized() { string validatorAssemblyPath = $@"c:\{Guid.NewGuid()}.dll"; string scanTargetExtension = Guid.NewGuid().ToString(); var mockFileSystem = new Mock <IFileSystem>(); mockFileSystem.Setup(x => x.FileExists(validatorAssemblyPath)).Returns(true); mockFileSystem.Setup(x => x.AssemblyLoadFrom(validatorAssemblyPath)).Returns(this.GetType().Assembly); var validators = new ValidatorsCache( new string[] { validatorAssemblyPath }, fileSystem: mockFileSystem.Object); MatchExpression expression = CreateGuidDetectingMatchExpression( allowFileExtension: scanTargetExtension); expression.ContentsRegex = "TestRule"; SearchDefinition definition = CreateDefaultSearchDefinition(expression); // This Id will match us up with the TestRuleValidator type. definition.Id = "TestRule"; AnalyzeContext context = CreateGuidMatchingSkimmer( scanTargetExtension: scanTargetExtension, ref definition, out SearchSkimmer skimmer, validators: validators); skimmer.Analyze(context); //((TestLogger)context.Logger).Results.Should().BeNull(); }
internal static SearchDefinitions PushInheritedData(SearchDefinitions definitions, Dictionary <string, string> sharedStrings) { var idToExpressionsMap = new Dictionary <string, List <MatchExpression> >(); foreach (SearchDefinition definition in definitions.Definitions) { definition.FileNameDenyRegex = PushData(definition.FileNameDenyRegex, definition.SharedStrings, sharedStrings); definition.FileNameAllowRegex = PushData(definition.FileNameAllowRegex, definition.SharedStrings, sharedStrings); foreach (MatchExpression matchExpression in definition.MatchExpressions) { matchExpression.FileNameDenyRegex = PushData(matchExpression.FileNameDenyRegex, definition.SharedStrings, sharedStrings); matchExpression.FileNameDenyRegex ??= definition.FileNameDenyRegex; matchExpression.FileNameAllowRegex = PushData(matchExpression.FileNameAllowRegex, definition.SharedStrings, sharedStrings); matchExpression.FileNameAllowRegex ??= definition.FileNameAllowRegex; matchExpression.ContentsRegex = PushData(matchExpression.ContentsRegex, definition.SharedStrings, sharedStrings); matchExpression.Id ??= definition.Id; matchExpression.Name ??= definition.Name; matchExpression.Message ??= definition.Message; matchExpression.Description ??= definition.Description; if (matchExpression.Level == 0) { matchExpression.Level = definition.Level; } if (!idToExpressionsMap.TryGetValue(matchExpression.Id, out List <MatchExpression> cachedMatchExpressions)) { cachedMatchExpressions = idToExpressionsMap[matchExpression.Id] = new List <MatchExpression>(); } cachedMatchExpressions.Add(matchExpression); } } var searchDefinitions = new SearchDefinitions { Definitions = new List <SearchDefinition>(), }; foreach (KeyValuePair <string, List <MatchExpression> > kv in idToExpressionsMap) { string ruleId = kv.Key; List <MatchExpression> matchExpressions = kv.Value; var definition = new SearchDefinition { Id = matchExpressions[0].Id, Name = matchExpressions[0].Name, MatchExpressions = matchExpressions, Description = matchExpressions[0].Description, }; searchDefinitions.Definitions.Add(definition); } #if DEBUG ValidateSharedStringsExpansion(searchDefinitions); #endif return(searchDefinitions); }
public void SearchSkimmer_DetectsBase64EncodedPattern() { MatchExpression expr = CreateGuidDetectingMatchExpression(); SearchDefinition definition = CreateDefaultSearchDefinition(expr); string originalMessage = definition.Message; // We inject the well-known encoding name that reports with // 'plaintext' or 'base64-encoded' depending on how a match // was made. definition.Message = $"{{0:encoding}}:{definition.Message}"; string scanTargetContents = definition.Id; byte[] bytes = Encoding.UTF8.GetBytes(scanTargetContents); string base64Encoded = Convert.ToBase64String(bytes); var logger = new TestLogger(); var context = new AnalyzeContext { TargetUri = new Uri($"file:///c:/{definition.Name}.{definition.FileNameAllowRegex}"), FileContents = base64Encoded, Logger = logger }; SearchSkimmer skimmer = CreateSkimmer(definition); skimmer.Analyze(context); // Analyzing base64-encoded values with MatchLengthToDecode > 0 succeeds logger.Results.Count.Should().Be(1); logger.Results[0].RuleId.Should().Be(definition.Id); logger.Results[0].Level.Should().Be(definition.Level); logger.Results[0].GetMessageText(skimmer).Should().Be($"base64-encoded:{originalMessage}"); // Analyzing base64-encoded values with MatchLengthToDecode == 0 fails definition.MatchExpressions[0].MatchLengthToDecode = 0; logger.Results.Clear(); skimmer = CreateSkimmer(definition); skimmer.Analyze(context); logger.Results.Count.Should().Be(0); // Analyzing plaintext values with MatchLengthToDecode > 0 succeeds context.FileContents = scanTargetContents; logger.Results.Clear(); skimmer = CreateSkimmer(definition); skimmer.Analyze(context); // But we should see a change in encoding information in message. Note // that when emitting plaintext matches, we elide this information // entirely (i.e., we only explicitly report 'base64-encoded' and // report nothing for plaintext). logger.Results.Count.Should().Be(1); logger.Results[0].RuleId.Should().Be(definition.Id); logger.Results[0].Level.Should().Be(definition.Level); logger.Results[0].GetMessageText(skimmer).Should().Be($":{originalMessage}"); }
public SearchSkimmer(IRegex engine, ValidatorsCache validators, FileRegionsCache fileRegionsCache, SearchDefinition definition, IFileSystem fileSystem = null) : this( engine, validators, fileRegionsCache, definition.Id, definition.Name, definition.Description, definition.MatchExpressions, fileSystem) { }