public List<RiskLevelRule> getRiskLevelRules() { return threatTriageConfig.getRiskLevelRules(); }
@Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (getTriageConfig() != null ? getTriageConfig().hashCode() : 0); return result; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; ThreatIntelConfig that = (ThreatIntelConfig) o; return getTriageConfig() != null ? getTriageConfig().equals(that.getTriageConfig()) : that.getTriageConfig() == null; }
@Override public Object apply(List<Object> args, Context context) throws ParseException { SensorEnrichmentConfig config = getSensorEnrichmentConfig(args, 0); ThreatIntelConfig tiConfig = (ThreatIntelConfig) getConfig(config, EnrichmentConfigFunctions.Type.THREAT_INTEL); if(tiConfig == null) { return ""; } org.apache.metron.common.configuration.enrichment.threatintel.ThreatTriageConfig triageConfig = tiConfig.getTriageConfig(); if(triageConfig == null) { return ""; } // print each rule List<RiskLevelRule> triageRules = ListUtils.emptyIfNull(triageConfig.getRiskLevelRules()); String[] headers = new String[] {"Name", "Comment", "Triage Rule", "Score", "Reason"}; String[][] data = new String[triageRules.size()][5]; int i = 0; for(RiskLevelRule rule : triageRules) { String score = rule.getScoreExpression(); String name = Optional.ofNullable(rule.getName()).orElse(""); String comment = Optional.ofNullable(rule.getComment()).orElse(""); String reason = Optional.ofNullable(rule.getReason()).orElse(""); data[i++] = new String[] {name, comment, rule.getRule(), score, reason}; } String ret = FlipTable.of(headers, data); // print the aggregation if(!triageRules.isEmpty()) { ret += "Aggregation: " + triageConfig.getAggregator().name(); } return ret; }
for(RuleScore ruleScore: score.getRuleScores()) { if(ruleScore.getRule().getName() != null) { map.put(RULE_NAME_KEY, ruleScore.getRule().getName()); if(ruleScore.getRule().getRule() != null) { map.put(RULE_EXPR_KEY, ruleScore.getRule().getRule()); if(ruleScore.getRule().getScoreExpression() != null) { map.put(RULE_SCORE_KEY, ruleScore.getRule().getScoreExpression()); if(ruleScore.getReason() != null) { map.put(RULE_REASON_KEY, ruleScore.getReason()); if(ruleScore.getRule().getComment() != null) { map.put(RULE_COMMENT_KEY, ruleScore.getRule().getComment()); result.put(SCORE_KEY, score.getScore()); result.put(RULES_KEY, scores); result.put(AGG_KEY, config.getThreatIntel().getTriageConfig().getAggregator().toString()); return result;
/** * @param message The message being triaged. */ @Nullable @Override public ThreatScore apply(@Nullable Map message) { ThreatScore threatScore = new ThreatScore(); StellarPredicateProcessor predicateProcessor = new StellarPredicateProcessor(); StellarProcessor processor = new StellarProcessor(); VariableResolver variableResolver = new MapVariableResolver(message, sensorConfig.getConfiguration(), threatIntelConfig.getConfig()); // attempt to apply each rule to the threat for(RiskLevelRule rule : threatTriageConfig.getRiskLevelRules()) { if(predicateProcessor.parse(rule.getRule(), variableResolver, functionResolver, context)) { // add the rule's score to the overall threat score String reason = execute(rule.getReason(), processor, variableResolver, String.class); Double score = execute(rule.getScoreExpression(), processor, variableResolver, Double.class); threatScore.addRuleScore(new RuleScore(rule, reason, score)); } } // calculate the aggregate threat score List<Number> ruleScores = new ArrayList<>(); for(RuleScore ruleScore: threatScore.getRuleScores()) { ruleScores.add(ruleScore.getScore()); } Aggregators aggregators = threatTriageConfig.getAggregator(); Double aggregateScore = aggregators.aggregate(ruleScores, threatTriageConfig.getAggregationConfig()); threatScore.setScore(aggregateScore); return threatScore; }
/** * Appends the threat score to the telemetry message. * @param threatScore The threat triage score * @param message The telemetry message being triaged. */ private static void appendThreatScore(ThreatScore threatScore, JSONObject message) { // append the overall threat score message.put(THREAT_TRIAGE_SCORE_KEY, threatScore.getScore()); // append each of the rules - each rule is 'flat' Joiner joiner = Joiner.on("."); int i = 0; for(RuleScore score: threatScore.getRuleScores()) { message.put(joiner.join(THREAT_TRIAGE_RULES_KEY, i, THREAT_TRIAGE_RULE_NAME), score.getRule().getName()); message.put(joiner.join(THREAT_TRIAGE_RULES_KEY, i, THREAT_TRIAGE_RULE_COMMENT), score.getRule().getComment()); message.put(joiner.join(THREAT_TRIAGE_RULES_KEY, i, THREAT_TRIAGE_RULE_SCORE), score.getRule().getScoreExpression()); message.put(joiner.join(THREAT_TRIAGE_RULES_KEY, i++, THREAT_TRIAGE_RULE_REASON), score.getReason()); } } }
@Override public Object apply(List<Object> args, Context context) throws ParseException { SensorEnrichmentConfig config = getSensorEnrichmentConfig(args, 0); ThreatIntelConfig tiConfig = (ThreatIntelConfig) getConfig(config, EnrichmentConfigFunctions.Type.THREAT_INTEL); if(tiConfig == null) { tiConfig = new ThreatIntelConfig(); config.setThreatIntel(tiConfig); } org.apache.metron.common.configuration.enrichment.threatintel.ThreatTriageConfig triageConfig = tiConfig.getTriageConfig(); if(triageConfig == null) { triageConfig = new org.apache.metron.common.configuration.enrichment.threatintel.ThreatTriageConfig(); tiConfig.setTriageConfig(triageConfig); } List<RiskLevelRule> triageRules = triageConfig.getRiskLevelRules(); if(triageRules == null) { triageRules = new ArrayList<>(); triageConfig.setRiskLevelRules(triageRules); } String aggregator = (String) args.get(1); triageConfig.setAggregator(aggregator); if(args.size() > 2) { Map<String, Object> aggConfig = (Map<String, Object>) args.get(2); triageConfig.setAggregationConfig(aggConfig); } return toJSON(config); }
/** * Each individual rule that was applied when scoring a threat should * be captured in the overall threat score. */ @Test public void testThreatScoreWithMultipleRules() throws Exception { Map<Object, Object> message = new HashMap<Object, Object>() {{ put("user.type", "admin"); put("asset.type", "web"); }}; ThreatScore score = getProcessor(smokeTestProcessorConfig).apply(message); // expect rules 1 and 2 to have been applied List<String> expectedNames = ImmutableList.of("rule 1", "rule 2"); Assert.assertEquals(2, score.getRuleScores().size()); score.getRuleScores().forEach(ruleScore -> Assert.assertTrue(expectedNames.contains(ruleScore.getRule().getName())) ); }
/** * If the 'reason' expression refers to a missing variable (the result * of a data quality issue) it should not throw an exception. */ @Test public void testInvalidReason() throws Exception { Map<Object, Object> message = new HashMap<Object, Object>() {{ // there is no 'variable.name' in the message }}; ThreatScore score = getProcessor(testReasonConfig).apply(message); assertEquals(1, score.getRuleScores().size()); for(RuleScore ruleScore : score.getRuleScores()) { // the 'reason' is the result of executing the rule's 'reason' expression assertEquals(null, ruleScore.getReason()); } }
public ThreatTriageProcessor( SensorEnrichmentConfig config , FunctionResolver functionResolver , Context context ) { this.threatIntelConfig = config.getThreatIntel(); this.sensorConfig = config; this.threatTriageConfig = config.getThreatIntel().getTriageConfig(); this.functionResolver = functionResolver; this.context = context; }
@Override public Map<String, Object> getFieldMap(String sourceType) { SensorEnrichmentConfig config = getConfigurations().getSensorEnrichmentConfig(sourceType); if(config != null) { return config.getThreatIntel().getFieldMap(); } else { LOG.debug("Unable to retrieve sensor config: {}", sourceType); return null; } }
@Test public void shouldAllowNumericRuleScore() throws Exception { Map<String, Object> message = new HashMap<>(); ThreatTriageProcessor threatTriageProcessor = getProcessor(shouldAllowNumericRuleScore); Assert.assertEquals(10d, threatTriageProcessor.apply(message).getScore(), 1e-10); }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SensorEnrichmentConfig that = (SensorEnrichmentConfig) o; if (getEnrichment() != null ? !getEnrichment().equals(that.getEnrichment()) : that.getEnrichment() != null) return false; if (getThreatIntel() != null ? !getThreatIntel().equals(that.getThreatIntel()) : that.getThreatIntel() != null) return false; return getConfiguration() != null ? getConfiguration().equals(that.getConfiguration()) : that.getConfiguration() == null; }
@Override public int hashCode() { int result = getEnrichment() != null ? getEnrichment().hashCode() : 0; result = 31 * result + (getEnrichment() != null ? getEnrichment().hashCode() : 0); result = 31 * result + (getThreatIntel() != null ? getThreatIntel().hashCode() : 0); result = 31 * result + (getConfiguration() != null ? getConfiguration().hashCode() : 0); return result; }
@Test public void shouldAllowNumericRuleScore() throws Exception { // deserialize SensorEnrichmentConfig enrichment = (SensorEnrichmentConfig) ENRICHMENT.deserialize(triageRuleWithNumericScore); ThreatTriageConfig threatTriage = enrichment.getThreatIntel().getTriageConfig(); assertNotNull(threatTriage); List<RiskLevelRule> rules = threatTriage.getRiskLevelRules(); assertEquals(1, rules.size()); RiskLevelRule rule = rules.get(0); assertEquals("Rule Name", rule.getName()); assertEquals("Rule Comment", rule.getComment()); assertEquals("ip_src_addr == '10.0.2.3'", rule.getRule()); assertEquals("'Rule Reason'", rule.getReason()); assertEquals("10", rule.getScoreExpression()); }
/** * Each individual rule that was applied when scoring a threat should * be captured in the overall threat score. */ @Test public void testThreatScoreWithOneRule() throws Exception { Map<Object, Object> message = new HashMap<Object, Object>() {{ put("user.type", "abnormal"); put("asset.type", "invalid"); }}; ThreatScore score = getProcessor(smokeTestProcessorConfig).apply(message); // expect rule 4 to have been applied List<String> expectedNames = ImmutableList.of("rule 4"); Assert.assertEquals(1, score.getRuleScores().size()); score.getRuleScores().forEach(ruleScore -> Assert.assertTrue(expectedNames.contains(ruleScore.getRule().getName())) ); }
/** * The 'reason' field contained within a rule is a Stellar expression that is * executed within the context of the message that the rule is applied to. */ @Test public void testReason() throws Exception { Map<Object, Object> message = new HashMap<Object, Object>() {{ put("variable.name", "variable.value"); }}; ThreatScore score = getProcessor(testReasonConfig).apply(message); assertEquals(1, score.getRuleScores().size()); for(RuleScore ruleScore : score.getRuleScores()) { // the 'reason' is the result of executing the rule's 'reason' expression assertEquals("variable.value", ruleScore.getReason()); } }
@Override public String toString() { return String.format("ThreatTriage{%d rule(s)}", threatTriageConfig.getRiskLevelRules().size()); } }
@Test public void shouldAllowScoreAsStellarExpression() throws Exception { // deserialize the enrichment configuration SensorEnrichmentConfig enrichment = (SensorEnrichmentConfig) ENRICHMENT.deserialize(triageRuleWithScoreExpression); ThreatTriageConfig threatTriage = enrichment.getThreatIntel().getTriageConfig(); assertNotNull(threatTriage); List<RiskLevelRule> rules = threatTriage.getRiskLevelRules(); assertEquals(1, rules.size()); RiskLevelRule rule = rules.get(0); assertEquals("Rule Name", rule.getName()); assertEquals("Rule Comment", rule.getComment()); assertEquals("'Rule Reason'", rule.getReason()); assertEquals("ip_src_addr == '10.0.2.3'", rule.getRule()); assertEquals("10 + 10", rule.getScoreExpression()); } }