From d75a17778e451b6d3deec0c08d2b9fd6211e6331 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 20 Sep 2021 17:09:15 -0400 Subject: [PATCH 01/21] Add FingerprintExample class This provides access to the example and its associated data. --- .../com/rapid7/recog/FingerprintExample.java | 22 +++++++++++++++++++ .../java/com/rapid7/recog/RecogMatcher.java | 9 ++++---- .../com/rapid7/recog/parser/RecogParser.java | 19 ++++++++++++++-- .../com/rapid7/recog/RecogIntegration.java | 4 ++-- 4 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/rapid7/recog/FingerprintExample.java diff --git a/src/main/java/com/rapid7/recog/FingerprintExample.java b/src/main/java/com/rapid7/recog/FingerprintExample.java new file mode 100644 index 0000000..6f56e09 --- /dev/null +++ b/src/main/java/com/rapid7/recog/FingerprintExample.java @@ -0,0 +1,22 @@ +package com.rapid7.recog; + +import java.util.Map; + +// Represents a fingerprint example and associated data. +public class FingerprintExample { + private final String text; + private final Map attributeMap; + + public FingerprintExample(String text, Map attributeMap) { + this.text = text; + this.attributeMap = attributeMap; + } + + public String getText() { + return text; + } + + public Map getAttributeMap() { + return attributeMap; + } +} diff --git a/src/main/java/com/rapid7/recog/RecogMatcher.java b/src/main/java/com/rapid7/recog/RecogMatcher.java index c78e6e1..9606419 100644 --- a/src/main/java/com/rapid7/recog/RecogMatcher.java +++ b/src/main/java/com/rapid7/recog/RecogMatcher.java @@ -45,7 +45,7 @@ public class RecogMatcher implements Serializable { private String description; /** Optional examples that illustrate the matcher (or that can be used to test the matcher). */ - private Set examples; + private Set examples; /** * Creates a new RecogMatcher using a {@link JavaRegexRecogPatternMatcher} to @@ -98,9 +98,10 @@ public String getDescription() { * {@code null}. * @return A reference to this matcher to allow for method chaining. */ - public RecogMatcher addExample(String example) { - if (example != null) + public RecogMatcher addExample(FingerprintExample example) { + if (example != null) { examples.add(example); + } return this; } @@ -111,7 +112,7 @@ public RecogMatcher addExample(String example) { * * @return A non-null, immutable {@link Set} of examples. May be empty. */ - public Set getExamples() { + public Set getExamples() { return examples == null ? emptySet() : unmodifiableSet(examples); } diff --git a/src/main/java/com/rapid7/recog/parser/RecogParser.java b/src/main/java/com/rapid7/recog/parser/RecogParser.java index c29c6c2..109eb90 100644 --- a/src/main/java/com/rapid7/recog/parser/RecogParser.java +++ b/src/main/java/com/rapid7/recog/parser/RecogParser.java @@ -1,5 +1,6 @@ package com.rapid7.recog.parser; +import com.rapid7.recog.FingerprintExample; import com.rapid7.recog.RecogMatcher; import com.rapid7.recog.RecogMatchers; import com.rapid7.recog.pattern.JavaRegexRecogPatternMatcher; @@ -8,6 +9,7 @@ import java.io.FileReader; import java.io.IOException; import java.io.Reader; +import java.util.HashMap; import java.util.StringTokenizer; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilderFactory; @@ -16,6 +18,8 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -162,8 +166,19 @@ public RecogMatchers parse(Reader reader, String name) if ("base64".equals(example.getAttribute("_encoding"))) { // TODO: these are currently ignored as the Base64 decoding isn't working properly - } else - fingerprintPattern.addExample(exampleContent); + } else { + HashMap attributeMap = new HashMap<>(); + NamedNodeMap exAttributes = example.getAttributes(); + + for (int i = 0; i < exAttributes.getLength(); i++) { + Node attr = exAttributes.item(i); + String attrName = attr.getNodeName(); + String attrValue = attr.getNodeValue(); + attributeMap.put(attrName, attrValue); + } + + fingerprintPattern.addExample(new FingerprintExample(exampleContent, attributeMap)); + } } // parse and add parameter specifications diff --git a/src/test/java/com/rapid7/recog/RecogIntegration.java b/src/test/java/com/rapid7/recog/RecogIntegration.java index 4f2637d..7190b5b 100644 --- a/src/test/java/com/rapid7/recog/RecogIntegration.java +++ b/src/test/java/com/rapid7/recog/RecogIntegration.java @@ -30,9 +30,9 @@ public void allRecogContentParses() throws FileNotFoundException, ParseException RecogMatchers matchers = parser.parse(file); for (RecogMatcher matcher : matchers) { // when - the matcher has examples - for (String example : matcher.getExamples()) + for (FingerprintExample example : matcher.getExamples()) // then - the example matches - assertThat("Matcher in " + file + " with pattern '" + matcher.getPattern() + "' does not match example '" + example + "'.", matcher.matches(example), is(true)); + assertThat("Matcher in " + file + " with pattern '" + matcher.getPattern() + "' does not match example '" + example.getText() + "'.", matcher.matches(example.getText()), is(true)); } } } From 8329523279b028a00558553c67eb27e6c2d5ea7b Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 20 Sep 2021 17:24:51 -0400 Subject: [PATCH 02/21] Indentation correction --- .../parser/FingerprintMatcherParserTest.java | 196 +++++++++--------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java b/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java index 9693d1c..d24b075 100644 --- a/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java +++ b/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java @@ -43,17 +43,17 @@ public void invalidXmlInputCausesExceptionToBeThrown() throws ParseException { public void validFingerprint() throws ParseException { // given String xml = "\n" - + "" - + " \n" - + " Apache returning only its major version number\n" - + " Apache 1\n" - + " Apache 2\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + ""; + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache 1\n" + + " Apache 2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; // when RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString()); @@ -67,24 +67,24 @@ public void validFingerprint() throws ParseException { public void twoValidFingerprints() throws ParseException { // given String xml = "\n" - + "" - + " \n" - + " Apache returning only its major version number\n" - + " Apache/1\n" - + " Apache/2\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " Apache returning no version information\n" - + " Apache\n" - + " apache\n" - + " \n" - + " \n" - + " \n" - + " " - + ""; + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache/1\n" + + " Apache/2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " Apache returning no version information\n" + + " Apache\n" + + " apache\n" + + " \n" + + " \n" + + " \n" + + " " + + ""; // when RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString()); @@ -92,37 +92,37 @@ public void twoValidFingerprints() throws ParseException { // then assertThat(patterns.size(), is(2)); assertThat(patterns, hasItems( - new RecogMatcher(pattern("^Apache/\\d$", CASE_INSENSITIVE)).addValue("service.vendor", "Apache").addValue("service.product", "HTTPD").addValue("service.family", "Apache"), - new RecogMatcher(pattern("^Apache$", MULTILINE)).addValue("service.vendor", "Apache").addValue("service.product", "HTTPD").addValue("service.family", "Apache"))); + new RecogMatcher(pattern("^Apache/\\d$", CASE_INSENSITIVE)).addValue("service.vendor", "Apache").addValue("service.product", "HTTPD").addValue("service.family", "Apache"), + new RecogMatcher(pattern("^Apache$", MULTILINE)).addValue("service.vendor", "Apache").addValue("service.product", "HTTPD").addValue("service.family", "Apache"))); } @Test public void invalidFingerprintsIgnored() throws ParseException { // given String xml = "\n" - + "" - + " \n" - + " Apache returning only its major version number\n" - + " Apache/1\n" - + " Apache/2\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " Apache returning only its major version number\n" - + " Apache/1\n" - + " \n" - + " \n" - + " \n" - + " Apache returning no version information\n" - + " Apache\n" - + " apache\n" - + " \n" - + " \n" - + " \n" - + " " - + ""; + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache/1\n" + + " Apache/2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " Apache returning only its major version number\n" + + " Apache/1\n" + + " \n" + + " \n" + + " \n" + + " Apache returning no version information\n" + + " Apache\n" + + " apache\n" + + " \n" + + " \n" + + " \n" + + " " + + ""; // when RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString()); @@ -135,17 +135,17 @@ public void invalidFingerprintsIgnored() throws ParseException { public void patternIsRequired() throws ParseException { // given String xml = "\n" - + "" - + " \n" - + " Apache returning only its major version number\n" - + " Apache 1\n" - + " Apache 2\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + ""; + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache 1\n" + + " Apache 2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; // when RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString()); @@ -158,17 +158,17 @@ public void patternIsRequired() throws ParseException { public void patternMustBeValid() throws ParseException { // given String xml = "\n" - + "" - + " \n" - + " Apache returning only its major version number\n" - + " Apache 1\n" - + " Apache 2\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + ""; + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache 1\n" + + " Apache 2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; // when RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString()); @@ -181,17 +181,17 @@ public void patternMustBeValid() throws ParseException { public void emptyFlagsIgnored() throws ParseException { // given String xml = "\n" - + "" - + " \n" - + " Apache returning only its major version number\n" - + " Apache 1\n" - + " Apache 2\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + ""; + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache 1\n" + + " Apache 2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; // when RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString()); @@ -205,17 +205,17 @@ public void emptyFlagsIgnored() throws ParseException { public void unknownFlagIgnored() throws ParseException { // given String xml = "\n" - + "" - + " \n" - + " Apache returning only its major version number\n" - + " Apache 1\n" - + " Apache 2\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + ""; + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache 1\n" + + " Apache 2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; // when RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString()); From cb1e63d52092895bf354fd27f2ca4684d7aadd50 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 20 Sep 2021 17:27:04 -0400 Subject: [PATCH 03/21] Cover checks done in ruby verify_params method --- .../com/rapid7/recog/parser/RecogParser.java | 9 ++- .../parser/FingerprintMatcherParserTest.java | 57 ++++++++++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/rapid7/recog/parser/RecogParser.java b/src/main/java/com/rapid7/recog/parser/RecogParser.java index 109eb90..5d4f877 100644 --- a/src/main/java/com/rapid7/recog/parser/RecogParser.java +++ b/src/main/java/com/rapid7/recog/parser/RecogParser.java @@ -193,9 +193,12 @@ public RecogMatchers parse(Reader reader, String name) if (position == 0) { String paramValue = getRequiredAttribute(parameter, "value"); fingerprintPattern.addValue(paramName, paramValue); - } - // otherwise the position indicates a group match result - else { + } else { + // otherwise the position indicates a group match result + String value = parameter.getAttribute("value"); + if (!value.isEmpty()) { + throw new ParseException(String.format("Attribute \"%s\" has a non-zero position but specifies a value of \"%s\"", paramName, value)); + } fingerprintPattern.addParam(position, paramName); } } diff --git a/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java b/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java index d24b075..faee403 100644 --- a/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java +++ b/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java @@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; public class FingerprintMatcherParserTest { @@ -224,4 +225,58 @@ public void unknownFlagIgnored() throws ParseException { assertThat(patterns.size(), is(1)); assertThat(patterns, hasItems(new RecogMatcher(pattern("^Apache (\\d)$")).addValue("service.vendor", "Apache").addValue("service.product", "HTTPD").addValue("service.family", "Apache").addParam(1, "service.version"))); } -} + + @Test + public void paramZeroPositionWithNoValueFailsWhenStrict() { + // given + String xml = "\n" + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache 1\n" + + " Apache 2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + String expectedMessage = "Attribute \"value\" does not exist."; + + // when + Exception exception = assertThrows(ParseException.class, () -> { + new RecogParser(true).parse(new StringReader(xml), anyString()); + }, expectedMessage); + + // then + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + public void paramNonZeroPositionWithValueFailsWhenStrict() { + // given + String paramName = "service.version"; + String paramValue = "1"; + String xml = String.format("\n" + + "" + + " \n" + + " Apache returning only its major version number\n" + + " Apache 1\n" + + " Apache 2\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "", paramName, paramValue); + String expectedMessage = String.format("Attribute \"%s\" has a non-zero position but specifies a value of \"%s\"", paramName, paramValue); + + // when + Exception exception = assertThrows(ParseException.class, () -> { + new RecogParser(true).parse(new StringReader(xml), anyString()); + }, expectedMessage); + + // then + assertEquals(expectedMessage, exception.getMessage()); + } +} \ No newline at end of file From 0e63219e8785ec501a7f51540bb629a4dec71b12 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 20 Sep 2021 18:47:51 -0400 Subject: [PATCH 04/21] Add recog verifier tool --- pom.xml | 7 + .../java/com/rapid7/recog/RecogMatcher.java | 72 ++++++++ .../java/com/rapid7/recog/verify/Color.java | 20 +++ .../com/rapid7/recog/verify/Formatter.java | 40 +++++ .../rapid7/recog/verify/RecogVerifier.java | 165 ++++++++++++++++++ .../java/com/rapid7/recog/verify/Status.java | 8 + .../rapid7/recog/verify/VerifierOptions.java | 47 +++++ .../rapid7/recog/verify/VerifyReporter.java | 112 ++++++++++++ 8 files changed, 471 insertions(+) create mode 100644 src/main/java/com/rapid7/recog/verify/Color.java create mode 100644 src/main/java/com/rapid7/recog/verify/Formatter.java create mode 100644 src/main/java/com/rapid7/recog/verify/RecogVerifier.java create mode 100644 src/main/java/com/rapid7/recog/verify/Status.java create mode 100644 src/main/java/com/rapid7/recog/verify/VerifierOptions.java create mode 100644 src/main/java/com/rapid7/recog/verify/VerifyReporter.java diff --git a/pom.xml b/pom.xml index 5395d91..92428e0 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ 1.8 UTF-8 + 1.4 1.7.25 2.6 @@ -60,6 +61,12 @@ + + commons-cli + commons-cli + ${commons.cli.version} + compile + commons-io commons-io diff --git a/src/main/java/com/rapid7/recog/RecogMatcher.java b/src/main/java/com/rapid7/recog/RecogMatcher.java index 9606419..58ca8a0 100644 --- a/src/main/java/com/rapid7/recog/RecogMatcher.java +++ b/src/main/java/com/rapid7/recog/RecogMatcher.java @@ -3,6 +3,7 @@ import com.rapid7.recog.pattern.JavaRegexRecogPatternMatcher; import com.rapid7.recog.pattern.RecogPatternMatchResult; import com.rapid7.recog.pattern.RecogPatternMatcher; +import com.rapid7.recog.verify.Status; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; @@ -11,6 +12,7 @@ import java.util.Objects; import java.util.Set; import java.util.StringJoiner; +import java.util.function.BiConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.util.Collections.emptySet; @@ -170,6 +172,76 @@ public Map match(String input) { return null; } + public void verifyExamples(BiConsumer consumer) { + // look for the presence of test cases + if (examples.size() == 0) { + consumer.accept(Status.Warn, String.format("'%s' has no test cases", description)); + } + + // make sure each test case passes + for (FingerprintExample example : examples) { + Map result = match(example.getText()); + if (result == null || result.isEmpty()) { + consumer.accept(Status.Fail, String.format("'%s' failed to match \"%s\" with '%s'", + description, example.getText(), matcher.getPattern())); + continue; + } + + Status status = Status.Success; + String message = example.getText(); + // Ensure that all the attributes as provided by the example were parsed + // out correctly and match the capture group values we expect. + for (Map.Entry entry : example.getAttributeMap().entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (key.equals("_encoding")) { + continue; + } + + if (!result.containsKey(key) || !result.get(key).equals(value)) { + status = Status.Fail; + message = String.format("'%s' failed to find expected capture group %s '%s'. Result was %s", + description, key, value, result.get(key)); + break; + } + } + consumer.accept(status, message); + } + + verifyExamplesHaveCaptureGroups(consumer); + } + + private void verifyExamplesHaveCaptureGroups(BiConsumer consumer) { + Map captureGroupUsed = new HashMap<>(); + // get a list of parameters that are defined by capture groups + for (Entry parameter : positionalParameters.entrySet()) { + if (parameter.getValue() > 0 && !parameter.getKey().isEmpty()) { + captureGroupUsed.put(parameter.getKey(), false); + } + } + + // match up the fingerprint parameters with test attributes + for (FingerprintExample example : examples) { + Map result = match(example.getText()); + for (Entry entry : example.getAttributeMap().entrySet()) { + String key = entry.getKey(); + if (captureGroupUsed.containsKey(key)) { + captureGroupUsed.replace(key, true); + } + } + } + + // alert on untested parameters + for (Entry entry : captureGroupUsed.entrySet()) { + String paramName = entry.getKey(); + Boolean paramUsed = entry.getValue(); + if (!paramUsed) { + String message = String.format("'%s' is missing an example that checks for parameter '%s' which is derived from a capture group", description, paramName); + consumer.accept(Status.Warn, message); + } + } + } + /** * Adds a constant parameter value with the given name. If the matcher matches, this key-value * pair is guaranteed to be returned in the result of {@link #match(String)}. diff --git a/src/main/java/com/rapid7/recog/verify/Color.java b/src/main/java/com/rapid7/recog/verify/Color.java new file mode 100644 index 0000000..a2fd459 --- /dev/null +++ b/src/main/java/com/rapid7/recog/verify/Color.java @@ -0,0 +1,20 @@ +package com.rapid7.recog.verify; + +// ANSI color escape codes +enum Color { + Reset(0), + Red(31), + Yellow(33), + Green(32), + White(15); + + private final int code; + + Color(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/com/rapid7/recog/verify/Formatter.java b/src/main/java/com/rapid7/recog/verify/Formatter.java new file mode 100644 index 0000000..151285e --- /dev/null +++ b/src/main/java/com/rapid7/recog/verify/Formatter.java @@ -0,0 +1,40 @@ +package com.rapid7.recog.verify; + +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; + +public class Formatter { + + private final VerifierOptions options; + private final PrintWriter writer; + + public Formatter(VerifierOptions options, java.io.OutputStream output) { + this.options = options; + this.writer = new PrintWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8), true); + } + + public void statusMessage(String text) { + writer.println(color(text, Color.White)); + } + + public void successMessage(String text) { + writer.println(color(text, Color.Green)); + } + + public void warningMessage(String text) { + writer.println(color(text, Color.Yellow)); + } + + public void failureMessage(String text) { + writer.println(color(text, Color.Red)); + } + + private String color(String text, Color color) { + return options.isColor() ? colorize(text, color) : text; + } + + private String colorize(String text, Color color) { + return String.format("\u001B[%dm%s\u001B[%dm", color.getCode(), text, Color.Reset.getCode()); + } +} diff --git a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java new file mode 100644 index 0000000..e6f92d1 --- /dev/null +++ b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java @@ -0,0 +1,165 @@ +package com.rapid7.recog.verify; + +import com.rapid7.recog.RecogMatcher; +import com.rapid7.recog.RecogMatchers; +import com.rapid7.recog.parser.ParseException; +import com.rapid7.recog.parser.RecogParser; +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import static java.util.Objects.requireNonNull; + +public class RecogVerifier { + + private final RecogMatchers fingerprints; + private final VerifyReporter reporter; + + public static RecogVerifier create(VerifierOptions verifierOpts, RecogMatchers matchers) { + return create(verifierOpts, matchers, System.out); + } + + public static RecogVerifier create(VerifierOptions verifierOpts, RecogMatchers matchers, java.io.OutputStream output) { + requireNonNull(verifierOpts); + + Formatter formatter = new Formatter(verifierOpts, requireNonNull(output)); + VerifyReporter reporter = new VerifyReporter(verifierOpts, formatter); + return new RecogVerifier(requireNonNull(matchers), reporter); + } + + public RecogVerifier(RecogMatchers fingerprints, VerifyReporter reporter) { + this.fingerprints = fingerprints; + this.reporter = reporter; + } + + public RecogMatchers getFingerprints() { + return fingerprints; + } + + public VerifyReporter getReporter() { + return reporter; + } + + public void verify() { + for (RecogMatcher matcher : fingerprints) { + reporter.printName(matcher); + + // NOTE: RecogParser.parse ensures all parameters are valid + matcher.verifyExamples((status, message) -> { + switch (status) { + case Warn: + reporter.warning(String.format("WARN: %s", message)); + break; + case Fail: + reporter.failure(String.format("FAIL: %s", message)); + break; + case Success: + reporter.success(message); + break; + default: + break; + } + }); + } + reporter.report(fingerprints.size()); + } + + public static void main(String[] args) { + CommandLineParser parser = new DefaultParser(); + Options options = new Options(); + options.addOption(Option.builder("f") + .longOpt("format") + .hasArg() + .argName("FORMATTER") + .type(Character.class) + .desc("Choose a formatter.\n [s]ummary (default - failure/warning msgs and summary\n [q]uiet (configured failure/warning msgs only)\n [d]etail (fingerprint name with tests and expanded summary)") + .build()); + options.addOption(new Option("c", "color", false, "Enable color in the output.")); + options.addOption(new Option(null, "warnings", false, "Do not track warnings")); + options.addOption(new Option(null, "no-warnings", false, "Track warnings")); + + + VerifierOptions verifierOpts = null; + String[] filePaths = {}; + try { + // parse the command line arguments + CommandLine line = parser.parse(options, args); + + if (line.getArgs().length == 0) { + System.err.println("Missing XML fingerprint files"); + // generate the help statement + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("recog_verify [options] XML_FINGERPRINT_FILE1 ...", + "Verifies that each fingerprint passes its internal tests.", options, null); + System.exit(1); + } + + verifierOpts = getVerifierOptions(line); + filePaths = line.getArgs(); + } catch (org.apache.commons.cli.ParseException exception) { + System.err.println("error: command line parsing failed: " + exception.getMessage()); + System.exit(-1); + } + + int failures = 0; + int warnings = 0; + + for (String filePath : filePaths) { + File fingerprintFile = new File(filePath); + Collection files = Collections.emptySet(); + if (fingerprintFile.isDirectory()) { + files = FileUtils.listFiles(fingerprintFile, new String[]{"xml"}, true); + } else if (fingerprintFile.isFile()) { + files = Collections.singleton(fingerprintFile); + } + + for (File file : files) { + try { + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(file); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers); + verifier.verify(); + failures += verifier.getReporter().getFailureCount(); + warnings += verifier.getReporter().getWarningCount(); + } catch (ParseException exception) { + System.err.printf("error: parsing fingerprints file '%s': %s%n", file, exception.getMessage()); + System.exit(-1); + } + } + } + + System.exit(failures + warnings); + } + + private static VerifierOptions getVerifierOptions(CommandLine line) { + VerifierOptions verifierOpts = new VerifierOptions(); + + if (line.hasOption("format")) { + verifierOpts.setDetail(true); + if (line.getOptionValue("format").startsWith("d")) { + verifierOpts.setDetail(true); + } else if (line.getOptionValue("format").startsWith("q")) { + verifierOpts.setQuiet(true); + } + } + + if (line.hasOption("color")) { + verifierOpts.setColor(true); + } + + if (line.hasOption("warnings")) { + verifierOpts.setWarnings(true); + } + + if (line.hasOption("no-warnings")) { + verifierOpts.setWarnings(false); + } + + return verifierOpts; + } +} diff --git a/src/main/java/com/rapid7/recog/verify/Status.java b/src/main/java/com/rapid7/recog/verify/Status.java new file mode 100644 index 0000000..95027c1 --- /dev/null +++ b/src/main/java/com/rapid7/recog/verify/Status.java @@ -0,0 +1,8 @@ +package com.rapid7.recog.verify; + +// Verifier status +public enum Status { + Warn, + Fail, + Success +} diff --git a/src/main/java/com/rapid7/recog/verify/VerifierOptions.java b/src/main/java/com/rapid7/recog/verify/VerifierOptions.java new file mode 100644 index 0000000..ad32d05 --- /dev/null +++ b/src/main/java/com/rapid7/recog/verify/VerifierOptions.java @@ -0,0 +1,47 @@ +package com.rapid7.recog.verify; + +public class VerifierOptions { + private boolean color; + private boolean detail; + private boolean quiet; + private boolean warnings; + + public VerifierOptions() { + color = false; + detail = false; + quiet = false; + warnings = true; + } + + public boolean isColor() { + return color; + } + + public void setColor(boolean color) { + this.color = color; + } + + public boolean isDetail() { + return detail; + } + + public void setDetail(boolean detail) { + this.detail = detail; + } + + public boolean isQuiet() { + return quiet; + } + + public void setQuiet(boolean quiet) { + this.quiet = quiet; + } + + public boolean isWarnings() { + return warnings; + } + + public void setWarnings(boolean warnings) { + this.warnings = warnings; + } +} diff --git a/src/main/java/com/rapid7/recog/verify/VerifyReporter.java b/src/main/java/com/rapid7/recog/verify/VerifyReporter.java new file mode 100644 index 0000000..096d394 --- /dev/null +++ b/src/main/java/com/rapid7/recog/verify/VerifyReporter.java @@ -0,0 +1,112 @@ +package com.rapid7.recog.verify; + +import com.rapid7.recog.RecogMatcher; + +public class VerifyReporter { + + private final VerifierOptions options; + private final Formatter formatter; + private int successCount; + private int warningCount; + private int failureCount; + + public VerifyReporter(VerifierOptions options, Formatter formatter) { + this.options = options; + this.formatter = formatter; + resetCounts(); + } + + public Formatter getFormatter() { + return formatter; + } + + public int getSuccessCount() { + return successCount; + } + + public int getWarningCount() { + return warningCount; + } + + public int getFailureCount() { + return failureCount; + } + + public void report(int fingerprintCount) { + if (!options.isQuiet()) { + summarize(fingerprintCount); + } + } + + public void success(String text) { + successCount++; + if (options.isDetail()) { + formatter.successMessage(String.format("%s%s", padding(), text)); + } + } + + public void warning(String text) { + if (!options.isWarnings()) { + return; + } + + warningCount++; + formatter.warningMessage(String.format("%s%s", padding(), text)); + } + + public void failure(String text) { + failureCount++; + formatter.failureMessage(String.format("%s%s", padding(), text)); + } + + public void printName(RecogMatcher fingerprint) { + if (options.isDetail() && !fingerprint.getExamples().isEmpty()) { + String name = fingerprint.getDescription().isEmpty() ? "[unnamed]" : fingerprint.getDescription(); + formatter.statusMessage(String.format("\n%s", name)); + } + } + + public void summarize(int fingerprintCount) { + if (options.isDetail()) { + printFingerprintCount(fingerprintCount); + } + printSummary(); + } + + public void printFingerprintCount(int count) { + formatter.statusMessage(String.format("\nVerified %d fingerprints:", count)); + } + + public void printSummary() { + colorizeSummary(summaryLine()); + } + + private void resetCounts() { + successCount = 0; + failureCount = 0; + warningCount = 0; + } + + private String padding() { + if (options.isDetail()) { + return " "; + } + return ""; + } + + private String summaryLine() { + return String.format("SUMMARY: Test completed with %d successful, %d warnings" + + ", and %d failures", successCount, warningCount, failureCount); + } + + private void colorizeSummary(String summary) { + if (failureCount > 0) { + formatter.failureMessage(summary); + } else if (warningCount > 0) { + formatter.warningMessage(summary); + } else { + formatter.successMessage(summary); + } + } + +} \ No newline at end of file From 1eccc3dfefd17e4e27fca0fd134ae1f877cc4c76 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 20 Sep 2021 18:48:32 -0400 Subject: [PATCH 05/21] Add tests for RecogVerifier --- .../recog/verify/RecogVerifierTest.java | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java diff --git a/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java b/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java new file mode 100644 index 0000000..4115858 --- /dev/null +++ b/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java @@ -0,0 +1,208 @@ +package com.rapid7.recog.verify; + +import com.rapid7.recog.RecogMatchers; +import com.rapid7.recog.parser.ParseException; +import com.rapid7.recog.parser.RecogParser; +import org.apache.commons.io.output.NullOutputStream; +import org.junit.jupiter.api.Test; + +import java.io.StringReader; + +import static com.rapid7.recog.TestGenerators.anyString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RecogVerifierTest { + @Test + public void verifyNoExampleNoParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(1, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifyNoExampleZeroPositionParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " \n" + + " \n" + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(1, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifyNoExampleNonZeroPositionParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(4, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifySuccessfulExampleNonZeroPositionParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(1, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(3, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifySuccessfulExample() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(1, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(0, verifier.getReporter().getWarningCount()); + } + + @Test + public void verify1FailureAnd1SuccessfulExamples() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 1.2.3.4\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(1, verifier.getReporter().getSuccessCount()); + assertEquals(1, verifier.getReporter().getFailureCount()); + assertEquals(0, verifier.getReporter().getWarningCount()); + } + + @Test + public void verify2FailureExamples() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 1.2.3.4\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(2, verifier.getReporter().getFailureCount()); + assertEquals(0, verifier.getReporter().getWarningCount()); + } +} From c9a43a836b11a242ec2a592622fc9dca30362199 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 20 Sep 2021 23:52:59 -0400 Subject: [PATCH 06/21] Add help command line option --- .../com/rapid7/recog/verify/RecogVerifier.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java index e6f92d1..f5b5488 100644 --- a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java +++ b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java @@ -82,6 +82,7 @@ public static void main(String[] args) { options.addOption(new Option("c", "color", false, "Enable color in the output.")); options.addOption(new Option(null, "warnings", false, "Do not track warnings")); options.addOption(new Option(null, "no-warnings", false, "Track warnings")); + options.addOption(new Option("h", "help", false, "Command help")); VerifierOptions verifierOpts = null; @@ -90,12 +91,12 @@ public static void main(String[] args) { // parse the command line arguments CommandLine line = parser.parse(options, args); - if (line.getArgs().length == 0) { + if (line.hasOption("help")) { + usage(options); + System.exit(1); + } else if (line.getArgs().length == 0) { System.err.println("Missing XML fingerprint files"); - // generate the help statement - HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp("recog_verify [options] XML_FINGERPRINT_FILE1 ...", - "Verifies that each fingerprint passes its internal tests.", options, null); + usage(options); System.exit(1); } @@ -136,6 +137,13 @@ public static void main(String[] args) { System.exit(failures + warnings); } + private static void usage(Options options) { + // generate the help statement + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("recog_verify [options] XML_FINGERPRINT_FILE1 ...", + "Verifies that each fingerprint passes its internal tests.", options, null); + } + private static VerifierOptions getVerifierOptions(CommandLine line) { VerifierOptions verifierOpts = new VerifierOptions(); From 1e2676f2391b6d70ef3d67df9655293fc4a5b0b5 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Thu, 23 Sep 2021 00:26:29 -0400 Subject: [PATCH 07/21] Move interpolate to RecogMatcher.match --- .../java/com/rapid7/recog/RecogMatcher.java | 52 +++++++++++++++++- .../java/com/rapid7/recog/RecogMatchers.java | 53 +------------------ .../rapid7/recog/FingerprintMatchersTest.java | 6 +-- 3 files changed, 55 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/rapid7/recog/RecogMatcher.java b/src/main/java/com/rapid7/recog/RecogMatcher.java index 58ca8a0..2746f89 100644 --- a/src/main/java/com/rapid7/recog/RecogMatcher.java +++ b/src/main/java/com/rapid7/recog/RecogMatcher.java @@ -26,6 +26,56 @@ */ public class RecogMatcher implements Serializable { + private static final String CPE_SUFFIX = ".cpe23"; + + /** + * Interpolate the string using the "recog interpolation syntax" + * This syntax will take a string like "adsf {service.version} {service.family}" + * and attempt to resolve "{service.version}" and "{service.family}" in the string + * against the parameter map. So, given these parameters: + * - service.cpe23: "adsf {service.version} {service.family}" + * - service.version: "1.1" + * - service.family: "foo" + * + *

The map will resolve to: + * - service.cpe23: "asdf 1.1 foo" + * - service.version: "1.1" + * - service.family: "foo" + * + * @param keyEndsWith A string used to filter which keys will have their + * values interpolated (any key ending with this value). If {@code null}, + * all keys are considered. + * @param match The map containing values that can be interpolated. Must not + * be {@code null}. + * @return A map containing the interpolated key/values. + */ + public static Map interpolate(String keyEndsWith, Map match) { + requireNonNull(match); + + for (Entry entry : match.entrySet()) { + // For all keys that end with a certain extension (for optimization)... + if (keyEndsWith == null || entry.getKey().endsWith(keyEndsWith)) { + String value = entry.getValue(); + if (value != null) { + // The operation below is a "fold left" -- basically iterate over + // all the items in the map, and attempt to replace the items in + // this string with those map items. + String result = + match.entrySet().stream() + .reduce( + entry.getValue(), + (part, item) -> { + return part.replace("{" + item.getKey() + "}", item.getValue() == null ? "-" : item.getValue()); + }, + (part, item) -> part); + match.put(entry.getKey(), result.replaceAll(":$", "")); + } + } + } + + return match; + } + private final RecogPatternMatcher matcher; /** "Constant" values always matched as parameters. Key is the name, value is the value. */ @@ -167,7 +217,7 @@ public Map match(String input) { } } - return values; + return interpolate(CPE_SUFFIX, values); } else return null; } diff --git a/src/main/java/com/rapid7/recog/RecogMatchers.java b/src/main/java/com/rapid7/recog/RecogMatchers.java index baf62b5..188633d 100644 --- a/src/main/java/com/rapid7/recog/RecogMatchers.java +++ b/src/main/java/com/rapid7/recog/RecogMatchers.java @@ -15,7 +15,6 @@ */ public class RecogMatchers extends ArrayList { - private static final String CPE_SUFFIX = ".cpe23"; private String key; private String protocol; private String type; @@ -48,54 +47,6 @@ public float getPreference() { return preference; } - /** - * Interpolate the string using the "recog interpolation syntax" - * This syntax will take a string like "adsf {service.version} {service.family}" - * and attempt to resolve "{service.version}" and "{service.family}" in the string - * against the parameter map. So, given these parameters: - * - service.cpe23: "adsf {service.version} {service.family}" - * - service.version: "1.1" - * - service.family: "foo" - * - *

The map will resolve to: - * - service.cpe23: "asdf 1.1 foo" - * - service.version: "1.1" - * - service.family: "foo" - * - * @param keyEndsWith A string used to filter which keys will have their - * values interpolated (any key ending with this value). If {@code null}, - * all keys are considered. - * @param match The map containing values that can be interpolated. Must not - * be {@code null}. - * @return A map containing the interpolated key/values. - */ - public Map interpolate(String keyEndsWith, Map match) { - requireNonNull(match); - - for (Map.Entry entry : match.entrySet()) { - // For all keys that end with a certain extension (for optimization)... - if (keyEndsWith == null || entry.getKey().endsWith(keyEndsWith)) { - String value = entry.getValue(); - if (value != null) { - // The operation below is a "fold left" -- basically iterate over - // all the items in the map, and attempt to replace the items in - // this string with those map items. - String result = - match.entrySet().stream() - .reduce( - entry.getValue(), - (part, item) -> { - return part.replace("{" + item.getKey() + "}", item.getValue() == null ? "-" : item.getValue()); - }, - (part, item) -> part); - match.put(entry.getKey(), result.replaceAll(":$", "")); - } - } - } - - return match; - } - /** * Finds matches for a string input against all matchers. * @@ -108,7 +59,7 @@ public List getMatches(String input) { else return stream().map(matcher -> { Map match = matcher.match(input); - return match != null ? new RecogMatch(matcher, interpolate(CPE_SUFFIX, match)) : null; + return match != null ? new RecogMatch(matcher, match) : null; }).filter(Objects::nonNull).collect(toList()); } @@ -125,7 +76,7 @@ public RecogMatch getFirstMatch(String input) { for (RecogMatcher matcher : this) { Map match = matcher.match(input); if (match != null) - return new RecogMatch(matcher, interpolate(CPE_SUFFIX, match)); + return new RecogMatch(matcher, match); } return null; diff --git a/src/test/java/com/rapid7/recog/FingerprintMatchersTest.java b/src/test/java/com/rapid7/recog/FingerprintMatchersTest.java index d6f31d7..27b6ea1 100644 --- a/src/test/java/com/rapid7/recog/FingerprintMatchersTest.java +++ b/src/test/java/com/rapid7/recog/FingerprintMatchersTest.java @@ -217,13 +217,12 @@ public void multipleOfTheSameInterpolationProperty() { @Test public void interpolateWithNullSuffix() { // given - RecogMatchers matchers = new RecogMatchers(); HashMap map = new HashMap<>(); map.put("foo", "test"); map.put("bar", "{foo}"); // when - matchers.interpolate(null, map); + RecogMatcher.interpolate(null, map); // then assertThat(map.get("bar"), is("test")); @@ -233,13 +232,12 @@ public void interpolateWithNullSuffix() { @Test public void interpolateWithNonNullSuffix() { // given - RecogMatchers matchers = new RecogMatchers(); HashMap map = new HashMap<>(); map.put("foo", "test"); map.put("bar", "{foo}"); // when - matchers.interpolate("bar", map); + RecogMatcher.interpolate("bar", map); // then assertThat(map.get("bar"), is("test")); From ada31bbb510ed23dc9a52d646e44b8750ff92224 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Thu, 23 Sep 2021 00:27:25 -0400 Subject: [PATCH 08/21] Support glob in argument paths --- .../rapid7/recog/verify/RecogVerifier.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java index f5b5488..8c4c646 100644 --- a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java +++ b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java @@ -5,8 +5,17 @@ import com.rapid7.recog.parser.ParseException; import com.rapid7.recog.parser.RecogParser; import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; @@ -111,24 +120,29 @@ public static void main(String[] args) { int warnings = 0; for (String filePath : filePaths) { - File fingerprintFile = new File(filePath); - Collection files = Collections.emptySet(); - if (fingerprintFile.isDirectory()) { - files = FileUtils.listFiles(fingerprintFile, new String[]{"xml"}, true); - } else if (fingerprintFile.isFile()) { - files = Collections.singleton(fingerprintFile); + List globPaths = null; + PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + filePath); + try (Stream stream = Files.walk(FileSystems.getDefault().getPath(""))) { + globPaths = stream + .filter(Files::isRegularFile) + .filter(pathMatcher::matches) + .sorted() + .collect(Collectors.toList()); + } catch (IOException exception) { + System.err.printf("error: processing path '%s': %s%n", filePath, exception.getMessage()); + System.exit(-1); } - for (File file : files) { + for (Path p : globPaths) { try { RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(file); + RecogMatchers matchers = recogParser.parse(p.toFile()); RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers); verifier.verify(); failures += verifier.getReporter().getFailureCount(); warnings += verifier.getReporter().getWarningCount(); } catch (ParseException exception) { - System.err.printf("error: parsing fingerprints file '%s': %s%n", file, exception.getMessage()); + System.err.printf("error: parsing fingerprints file '%s': %s%n", p.toFile(), exception.getMessage()); System.exit(-1); } } From ef4879bc9e84a33a9a0cdfdc6ca18656de721b40 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Thu, 23 Sep 2021 00:29:21 -0400 Subject: [PATCH 09/21] Remove implemented command line tool recog_verify --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 393fa0f..2b9306d 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Missing features: - Matching against multi-line input strings - Matching against base64 encoded strings -- Command line tools like `recog_match` and `recog_verify` +- Command line tools like `recog_match` ## Development From 4f3d5db5c6c70e8c7ad778cd3e8d9fdde0a5d41c Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Fri, 24 Sep 2021 16:50:57 -0400 Subject: [PATCH 10/21] Fix failed match check A valid match may return an empty result Map. --- src/main/java/com/rapid7/recog/RecogMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/rapid7/recog/RecogMatcher.java b/src/main/java/com/rapid7/recog/RecogMatcher.java index 2746f89..6da2ce0 100644 --- a/src/main/java/com/rapid7/recog/RecogMatcher.java +++ b/src/main/java/com/rapid7/recog/RecogMatcher.java @@ -231,7 +231,7 @@ public void verifyExamples(BiConsumer consumer) { // make sure each test case passes for (FingerprintExample example : examples) { Map result = match(example.getText()); - if (result == null || result.isEmpty()) { + if (result == null) { consumer.accept(Status.Fail, String.format("'%s' failed to match \"%s\" with '%s'", description, example.getText(), matcher.getPattern())); continue; From 9a706c3a66bb42f3c1cd1adf319a3eb5a7c26598 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Fri, 24 Sep 2021 16:51:48 -0400 Subject: [PATCH 11/21] Interpolate all params not only those CPE suffix --- src/main/java/com/rapid7/recog/RecogMatcher.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/rapid7/recog/RecogMatcher.java b/src/main/java/com/rapid7/recog/RecogMatcher.java index 6da2ce0..8dbdda0 100644 --- a/src/main/java/com/rapid7/recog/RecogMatcher.java +++ b/src/main/java/com/rapid7/recog/RecogMatcher.java @@ -26,8 +26,6 @@ */ public class RecogMatcher implements Serializable { - private static final String CPE_SUFFIX = ".cpe23"; - /** * Interpolate the string using the "recog interpolation syntax" * This syntax will take a string like "adsf {service.version} {service.family}" @@ -217,7 +215,7 @@ public Map match(String input) { } } - return interpolate(CPE_SUFFIX, values); + return interpolate(null, values); } else return null; } From 53a65d7e837c361114e1c75291ef55642e686ca8 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Fri, 24 Sep 2021 16:52:19 -0400 Subject: [PATCH 12/21] Remove interpolate stripping trailing colon char --- src/main/java/com/rapid7/recog/RecogMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/rapid7/recog/RecogMatcher.java b/src/main/java/com/rapid7/recog/RecogMatcher.java index 8dbdda0..2c0c4d2 100644 --- a/src/main/java/com/rapid7/recog/RecogMatcher.java +++ b/src/main/java/com/rapid7/recog/RecogMatcher.java @@ -66,7 +66,7 @@ public static Map interpolate(String keyEndsWith, Map part); - match.put(entry.getKey(), result.replaceAll(":$", "")); + match.put(entry.getKey(), result); } } } From 64ba3705978419636f63b11579abef14857a10d6 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 27 Sep 2021 12:31:05 -0400 Subject: [PATCH 13/21] Checkstyle corrections --- .../recog/verify/RecogVerifierTest.java | 390 +++++++++--------- 1 file changed, 194 insertions(+), 196 deletions(-) diff --git a/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java b/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java index 4115858..51d6b0c 100644 --- a/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java +++ b/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java @@ -3,206 +3,204 @@ import com.rapid7.recog.RecogMatchers; import com.rapid7.recog.parser.ParseException; import com.rapid7.recog.parser.RecogParser; +import java.io.StringReader; import org.apache.commons.io.output.NullOutputStream; import org.junit.jupiter.api.Test; - -import java.io.StringReader; - import static com.rapid7.recog.TestGenerators.anyString; import static org.junit.jupiter.api.Assertions.assertEquals; public class RecogVerifierTest { - @Test - public void verifyNoExampleNoParamsWarnCount() throws ParseException { - // given - String xml = "\n" - + "\n" - + " \n" - + " Service Server - no examples or params\n" - + " \n" - + ""; - - // when - RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); - VerifierOptions verifierOpts = new VerifierOptions(); - RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); - verifier.verify(); - - // then - assertEquals(0, verifier.getReporter().getSuccessCount()); - assertEquals(0, verifier.getReporter().getFailureCount()); - assertEquals(1, verifier.getReporter().getWarningCount()); - } - - @Test - public void verifyNoExampleZeroPositionParamsWarnCount() throws ParseException { - // given - String xml = "\n" - + "\n" - + " \n" - + " Service Server - no examples or params\n" - + " \n" - + " \n" - + " \n" - + ""; - - // when - RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); - VerifierOptions verifierOpts = new VerifierOptions(); - RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); - verifier.verify(); - - // then - assertEquals(0, verifier.getReporter().getSuccessCount()); - assertEquals(0, verifier.getReporter().getFailureCount()); - assertEquals(1, verifier.getReporter().getWarningCount()); - } - - @Test - public void verifyNoExampleNonZeroPositionParamsWarnCount() throws ParseException { - // given - String xml = "\n" - + "\n" - + " \n" - + " Service Server - no examples or params\n" - + " \n" - + " \n" - + " \n" - + " " - + " " - + " \n" - + ""; - - // when - RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); - VerifierOptions verifierOpts = new VerifierOptions(); - RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); - verifier.verify(); - - // then - assertEquals(0, verifier.getReporter().getSuccessCount()); - assertEquals(0, verifier.getReporter().getFailureCount()); - assertEquals(4, verifier.getReporter().getWarningCount()); - } - - @Test - public void verifySuccessfulExampleNonZeroPositionParamsWarnCount() throws ParseException { - // given - String xml = "\n" - + "\n" - + " \n" - + " Service Server - no examples or params\n" - + " Media Server 7.9.3 - 1631723269\n" - + " \n" - + " \n" - + " \n" - + " " - + " " - + " \n" - + ""; - - // when - RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); - VerifierOptions verifierOpts = new VerifierOptions(); - RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); - verifier.verify(); - - // then - assertEquals(1, verifier.getReporter().getSuccessCount()); - assertEquals(0, verifier.getReporter().getFailureCount()); - assertEquals(3, verifier.getReporter().getWarningCount()); - } - - @Test - public void verifySuccessfulExample() throws ParseException { - // given - String xml = "\n" - + "\n" - + " \n" - + " Service Server - no examples or params\n" - + " Media Server 7.9.3 - 1631723269\n" - + " \n" - + " \n" - + " \n" - + " " - + " " - + " \n" - + ""; - - // when - RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); - VerifierOptions verifierOpts = new VerifierOptions(); - RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); - verifier.verify(); - - // then - assertEquals(1, verifier.getReporter().getSuccessCount()); - assertEquals(0, verifier.getReporter().getFailureCount()); - assertEquals(0, verifier.getReporter().getWarningCount()); - } - - @Test - public void verify1FailureAnd1SuccessfulExamples() throws ParseException { - // given - String xml = "\n" - + "\n" - + " \n" - + " Service Server - no examples or params\n" - + " Media Server 1.2.3.4\n" - + " Media Server 7.9.3 - 1631723269\n" - + " \n" - + " \n" - + " \n" - + " " - + " " - + " \n" - + ""; - - // when - RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); - VerifierOptions verifierOpts = new VerifierOptions(); - RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); - verifier.verify(); - - // then - assertEquals(1, verifier.getReporter().getSuccessCount()); - assertEquals(1, verifier.getReporter().getFailureCount()); - assertEquals(0, verifier.getReporter().getWarningCount()); - } - - @Test - public void verify2FailureExamples() throws ParseException { - // given - String xml = "\n" - + "\n" - + " \n" - + " Service Server - no examples or params\n" - + " Media Server 1.2.3.4\n" - + " Media Server 7.9.3 - 1631723269\n" - + " \n" - + " \n" - + " \n" - + " " - + " " - + " \n" - + ""; - - // when - RecogParser recogParser = new RecogParser(true); - RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); - VerifierOptions verifierOpts = new VerifierOptions(); - RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); - verifier.verify(); - - // then - assertEquals(0, verifier.getReporter().getSuccessCount()); - assertEquals(2, verifier.getReporter().getFailureCount()); - assertEquals(0, verifier.getReporter().getWarningCount()); - } + @Test + public void verifyNoExampleNoParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(1, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifyNoExampleZeroPositionParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " \n" + + " \n" + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(1, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifyNoExampleNonZeroPositionParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(4, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifySuccessfulExampleNonZeroPositionParamsWarnCount() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(1, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(3, verifier.getReporter().getWarningCount()); + } + + @Test + public void verifySuccessfulExample() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(1, verifier.getReporter().getSuccessCount()); + assertEquals(0, verifier.getReporter().getFailureCount()); + assertEquals(0, verifier.getReporter().getWarningCount()); + } + + @Test + public void verify1FailureAnd1SuccessfulExamples() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 1.2.3.4\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(1, verifier.getReporter().getSuccessCount()); + assertEquals(1, verifier.getReporter().getFailureCount()); + assertEquals(0, verifier.getReporter().getWarningCount()); + } + + @Test + public void verify2FailureExamples() throws ParseException { + // given + String xml = "\n" + + "\n" + + " \n" + + " Service Server - no examples or params\n" + + " Media Server 1.2.3.4\n" + + " Media Server 7.9.3 - 1631723269\n" + + " \n" + + " \n" + + " \n" + + " " + + " " + + " \n" + + ""; + + // when + RecogParser recogParser = new RecogParser(true); + RecogMatchers matchers = recogParser.parse(new StringReader(xml), anyString()); + VerifierOptions verifierOpts = new VerifierOptions(); + RecogVerifier verifier = RecogVerifier.create(verifierOpts, matchers, NullOutputStream.NULL_OUTPUT_STREAM); + verifier.verify(); + + // then + assertEquals(0, verifier.getReporter().getSuccessCount()); + assertEquals(2, verifier.getReporter().getFailureCount()); + assertEquals(0, verifier.getReporter().getWarningCount()); + } } From da79712a13818db3893ab4806ec48a3bd304b01c Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 27 Sep 2021 12:40:13 -0400 Subject: [PATCH 14/21] Optimized imports --- src/main/java/com/rapid7/recog/verify/RecogVerifier.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java index 8c4c646..41c6542 100644 --- a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java +++ b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java @@ -4,15 +4,11 @@ import com.rapid7.recog.RecogMatchers; import com.rapid7.recog.parser.ParseException; import com.rapid7.recog.parser.RecogParser; -import java.io.File; import java.io.IOException; -import java.nio.file.DirectoryStream; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; -import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; From 835813a119a2dc48d6f0227d23d58bad3485417d Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 27 Sep 2021 18:55:07 -0400 Subject: [PATCH 15/21] Remove unnecessary sort --- src/main/java/com/rapid7/recog/verify/RecogVerifier.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java index 41c6542..47d376a 100644 --- a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java +++ b/src/main/java/com/rapid7/recog/verify/RecogVerifier.java @@ -122,7 +122,6 @@ public static void main(String[] args) { globPaths = stream .filter(Files::isRegularFile) .filter(pathMatcher::matches) - .sorted() .collect(Collectors.toList()); } catch (IOException exception) { System.err.printf("error: processing path '%s': %s%n", filePath, exception.getMessage()); From 8494f3e8db3e7801b96af177426af09e8442824d Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 4 Oct 2021 14:37:27 -0400 Subject: [PATCH 16/21] Split lib and recog-verify using Maven modules --- pom.xml | 156 ++++++++++-------- recog-verify/pom.xml | 83 ++++++++++ .../java/com/rapid7/recog/verify/Color.java | 0 .../com/rapid7/recog/verify/Formatter.java | 0 .../rapid7/recog/verify/RecogVerifier.java | 0 .../rapid7/recog/verify/VerifierOptions.java | 0 .../rapid7/recog/verify/VerifyReporter.java | 0 .../recog/verify/RecogVerifierTest.java | 0 recog/pom.xml | 82 +++++++++ .../com/rapid7/recog/FingerprintExample.java | 0 .../main/java/com/rapid7/recog/Recog.java | 0 .../java/com/rapid7/recog/RecogMatch.java | 0 .../com/rapid7/recog/RecogMatchResult.java | 0 .../java/com/rapid7/recog/RecogMatcher.java | 0 .../java/com/rapid7/recog/RecogMatchers.java | 0 .../main/java/com/rapid7/recog/RecogType.java | 0 .../java/com/rapid7/recog/RecogVersion.java | 0 .../java/com/rapid7/recog/VerifyStatus.java | 0 .../rapid7/recog/parser/ParseException.java | 0 .../com/rapid7/recog/parser/RecogParser.java | 0 .../pattern/JavaRegexRecogPatternMatcher.java | 0 .../pattern/RecogPatternMatchResult.java | 0 .../recog/pattern/RecogPatternMatcher.java | 0 .../CompositeRecogMatchersProvider.java | 0 .../provider/IRecogMatchersProvider.java | 0 .../recog/provider/RecogMatchersProvider.java | 0 .../recog/CustomPatternMatcherTest.java | 0 .../rapid7/recog/FingerprintMatcherTest.java | 0 .../rapid7/recog/FingerprintMatchersTest.java | 0 .../com/rapid7/recog/RecogIntegration.java | 0 .../java/com/rapid7/recog/TestGenerators.java | 0 .../parser/FingerprintMatcherParserTest.java | 0 ...positeFingerprintMatchersProviderTest.java | 0 33 files changed, 253 insertions(+), 68 deletions(-) create mode 100644 recog-verify/pom.xml rename {src => recog-verify/src}/main/java/com/rapid7/recog/verify/Color.java (100%) rename {src => recog-verify/src}/main/java/com/rapid7/recog/verify/Formatter.java (100%) rename {src => recog-verify/src}/main/java/com/rapid7/recog/verify/RecogVerifier.java (100%) rename {src => recog-verify/src}/main/java/com/rapid7/recog/verify/VerifierOptions.java (100%) rename {src => recog-verify/src}/main/java/com/rapid7/recog/verify/VerifyReporter.java (100%) rename {src => recog-verify/src}/test/java/com/rapid7/recog/verify/RecogVerifierTest.java (100%) create mode 100644 recog/pom.xml rename {src => recog/src}/main/java/com/rapid7/recog/FingerprintExample.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/Recog.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/RecogMatch.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/RecogMatchResult.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/RecogMatcher.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/RecogMatchers.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/RecogType.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/RecogVersion.java (100%) rename src/main/java/com/rapid7/recog/verify/Status.java => recog/src/main/java/com/rapid7/recog/VerifyStatus.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/parser/ParseException.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/parser/RecogParser.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/pattern/JavaRegexRecogPatternMatcher.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/pattern/RecogPatternMatchResult.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/pattern/RecogPatternMatcher.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/provider/CompositeRecogMatchersProvider.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/provider/IRecogMatchersProvider.java (100%) rename {src => recog/src}/main/java/com/rapid7/recog/provider/RecogMatchersProvider.java (100%) rename {src => recog/src}/test/java/com/rapid7/recog/CustomPatternMatcherTest.java (100%) rename {src => recog/src}/test/java/com/rapid7/recog/FingerprintMatcherTest.java (100%) rename {src => recog/src}/test/java/com/rapid7/recog/FingerprintMatchersTest.java (100%) rename {src => recog/src}/test/java/com/rapid7/recog/RecogIntegration.java (100%) rename {src => recog/src}/test/java/com/rapid7/recog/TestGenerators.java (100%) rename {src => recog/src}/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java (100%) rename {src => recog/src}/test/java/com/rapid7/recog/provider/CompositeFingerprintMatchersProviderTest.java (100%) diff --git a/pom.xml b/pom.xml index 92428e0..c68f8bf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,9 @@ 4.0.0 com.rapid7.recog - recog-java + recog-parent 0.7.2-SNAPSHOT - jar - recog-java - https://www.rapid7.com - Java implementation of Recog that supports parsing and matching. + pom scm:git:git@github.com:rapid7/recog-java.git @@ -60,75 +57,92 @@ 2.18.0 - - - commons-cli - commons-cli - ${commons.cli.version} - compile - - - commons-io - commons-io - ${commons.io.version} - test - + + + + com.rapid7.recog + recog-java + ${project.version} + - - org.apache.commons - commons-lang3 - ${commons.lang3.version} - test - + + com.rapid7.recog + recog-java + ${project.version} + test-jar + test + - - org.hamcrest - hamcrest-core - ${hamcrest.version} - test - + + commons-cli + commons-cli + ${commons.cli.version} + compile + - - org.hamcrest - hamcrest-library - ${hamcrest.version} - test - + + commons-io + commons-io + ${commons.io.version} + test + - - org.junit.jupiter - junit-jupiter-api - ${junit.jupiter.version} - test - + + org.apache.commons + commons-lang3 + ${commons.lang3.version} + test + - - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} - test - + + org.hamcrest + hamcrest-core + ${hamcrest.version} + test + - - org.mockito - mockito-core - ${mockito.version} - test - + + org.hamcrest + hamcrest-library + ${hamcrest.version} + test + - - org.slf4j - slf4j-api - ${slf4j.version} - + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + - - org.slf4j - slf4j-simple - ${slf4j.version} - test - - + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + @@ -160,7 +174,7 @@ maven-checkstyle-plugin 3.0.0 - ${basedir}/google_checks.xml + ${maven.multiModuleProjectDirectory}/google_checks.xml warning @@ -171,7 +185,7 @@ check - ${basedir}/google_checks.xml + ${maven.multiModuleProjectDirectory}/google_checks.xml @@ -303,4 +317,10 @@ + + + recog + recog-verify + + diff --git a/recog-verify/pom.xml b/recog-verify/pom.xml new file mode 100644 index 0000000..d0255a9 --- /dev/null +++ b/recog-verify/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + com.rapid7.recog + recog-verify + jar + recog-verify + https://www.rapid7.com + Java implementation of Recog fingerprint verification tool. + + + com.rapid7.recog + recog-parent + 0.7.2-SNAPSHOT + + + + + com.rapid7.recog + recog-java + + + + com.rapid7.recog + recog-java + test-jar + test + + + + commons-cli + commons-cli + + + + commons-io + commons-io + + + + org.apache.commons + commons-lang3 + + + + org.hamcrest + hamcrest-core + + + + org.hamcrest + hamcrest-library + + + + org.junit.jupiter + junit-jupiter-api + + + + org.junit.jupiter + junit-jupiter-engine + + + + org.mockito + mockito-core + + + + org.slf4j + slf4j-api + + + + org.slf4j + slf4j-simple + + + + diff --git a/src/main/java/com/rapid7/recog/verify/Color.java b/recog-verify/src/main/java/com/rapid7/recog/verify/Color.java similarity index 100% rename from src/main/java/com/rapid7/recog/verify/Color.java rename to recog-verify/src/main/java/com/rapid7/recog/verify/Color.java diff --git a/src/main/java/com/rapid7/recog/verify/Formatter.java b/recog-verify/src/main/java/com/rapid7/recog/verify/Formatter.java similarity index 100% rename from src/main/java/com/rapid7/recog/verify/Formatter.java rename to recog-verify/src/main/java/com/rapid7/recog/verify/Formatter.java diff --git a/src/main/java/com/rapid7/recog/verify/RecogVerifier.java b/recog-verify/src/main/java/com/rapid7/recog/verify/RecogVerifier.java similarity index 100% rename from src/main/java/com/rapid7/recog/verify/RecogVerifier.java rename to recog-verify/src/main/java/com/rapid7/recog/verify/RecogVerifier.java diff --git a/src/main/java/com/rapid7/recog/verify/VerifierOptions.java b/recog-verify/src/main/java/com/rapid7/recog/verify/VerifierOptions.java similarity index 100% rename from src/main/java/com/rapid7/recog/verify/VerifierOptions.java rename to recog-verify/src/main/java/com/rapid7/recog/verify/VerifierOptions.java diff --git a/src/main/java/com/rapid7/recog/verify/VerifyReporter.java b/recog-verify/src/main/java/com/rapid7/recog/verify/VerifyReporter.java similarity index 100% rename from src/main/java/com/rapid7/recog/verify/VerifyReporter.java rename to recog-verify/src/main/java/com/rapid7/recog/verify/VerifyReporter.java diff --git a/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java b/recog-verify/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java similarity index 100% rename from src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java rename to recog-verify/src/test/java/com/rapid7/recog/verify/RecogVerifierTest.java diff --git a/recog/pom.xml b/recog/pom.xml new file mode 100644 index 0000000..b50babf --- /dev/null +++ b/recog/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + com.rapid7.recog + recog-java + jar + recog-java + https://www.rapid7.com + Java implementation of Recog that supports parsing and matching. + + + com.rapid7.recog + recog-parent + 0.7.2-SNAPSHOT + + + + + commons-io + commons-io + + + + org.apache.commons + commons-lang3 + + + + org.hamcrest + hamcrest-core + + + + org.hamcrest + hamcrest-library + + + + org.junit.jupiter + junit-jupiter-api + + + + org.junit.jupiter + junit-jupiter-engine + + + + org.mockito + mockito-core + + + + org.slf4j + slf4j-api + + + + org.slf4j + slf4j-simple + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + test-jar + + + + + + + + diff --git a/src/main/java/com/rapid7/recog/FingerprintExample.java b/recog/src/main/java/com/rapid7/recog/FingerprintExample.java similarity index 100% rename from src/main/java/com/rapid7/recog/FingerprintExample.java rename to recog/src/main/java/com/rapid7/recog/FingerprintExample.java diff --git a/src/main/java/com/rapid7/recog/Recog.java b/recog/src/main/java/com/rapid7/recog/Recog.java similarity index 100% rename from src/main/java/com/rapid7/recog/Recog.java rename to recog/src/main/java/com/rapid7/recog/Recog.java diff --git a/src/main/java/com/rapid7/recog/RecogMatch.java b/recog/src/main/java/com/rapid7/recog/RecogMatch.java similarity index 100% rename from src/main/java/com/rapid7/recog/RecogMatch.java rename to recog/src/main/java/com/rapid7/recog/RecogMatch.java diff --git a/src/main/java/com/rapid7/recog/RecogMatchResult.java b/recog/src/main/java/com/rapid7/recog/RecogMatchResult.java similarity index 100% rename from src/main/java/com/rapid7/recog/RecogMatchResult.java rename to recog/src/main/java/com/rapid7/recog/RecogMatchResult.java diff --git a/src/main/java/com/rapid7/recog/RecogMatcher.java b/recog/src/main/java/com/rapid7/recog/RecogMatcher.java similarity index 100% rename from src/main/java/com/rapid7/recog/RecogMatcher.java rename to recog/src/main/java/com/rapid7/recog/RecogMatcher.java diff --git a/src/main/java/com/rapid7/recog/RecogMatchers.java b/recog/src/main/java/com/rapid7/recog/RecogMatchers.java similarity index 100% rename from src/main/java/com/rapid7/recog/RecogMatchers.java rename to recog/src/main/java/com/rapid7/recog/RecogMatchers.java diff --git a/src/main/java/com/rapid7/recog/RecogType.java b/recog/src/main/java/com/rapid7/recog/RecogType.java similarity index 100% rename from src/main/java/com/rapid7/recog/RecogType.java rename to recog/src/main/java/com/rapid7/recog/RecogType.java diff --git a/src/main/java/com/rapid7/recog/RecogVersion.java b/recog/src/main/java/com/rapid7/recog/RecogVersion.java similarity index 100% rename from src/main/java/com/rapid7/recog/RecogVersion.java rename to recog/src/main/java/com/rapid7/recog/RecogVersion.java diff --git a/src/main/java/com/rapid7/recog/verify/Status.java b/recog/src/main/java/com/rapid7/recog/VerifyStatus.java similarity index 100% rename from src/main/java/com/rapid7/recog/verify/Status.java rename to recog/src/main/java/com/rapid7/recog/VerifyStatus.java diff --git a/src/main/java/com/rapid7/recog/parser/ParseException.java b/recog/src/main/java/com/rapid7/recog/parser/ParseException.java similarity index 100% rename from src/main/java/com/rapid7/recog/parser/ParseException.java rename to recog/src/main/java/com/rapid7/recog/parser/ParseException.java diff --git a/src/main/java/com/rapid7/recog/parser/RecogParser.java b/recog/src/main/java/com/rapid7/recog/parser/RecogParser.java similarity index 100% rename from src/main/java/com/rapid7/recog/parser/RecogParser.java rename to recog/src/main/java/com/rapid7/recog/parser/RecogParser.java diff --git a/src/main/java/com/rapid7/recog/pattern/JavaRegexRecogPatternMatcher.java b/recog/src/main/java/com/rapid7/recog/pattern/JavaRegexRecogPatternMatcher.java similarity index 100% rename from src/main/java/com/rapid7/recog/pattern/JavaRegexRecogPatternMatcher.java rename to recog/src/main/java/com/rapid7/recog/pattern/JavaRegexRecogPatternMatcher.java diff --git a/src/main/java/com/rapid7/recog/pattern/RecogPatternMatchResult.java b/recog/src/main/java/com/rapid7/recog/pattern/RecogPatternMatchResult.java similarity index 100% rename from src/main/java/com/rapid7/recog/pattern/RecogPatternMatchResult.java rename to recog/src/main/java/com/rapid7/recog/pattern/RecogPatternMatchResult.java diff --git a/src/main/java/com/rapid7/recog/pattern/RecogPatternMatcher.java b/recog/src/main/java/com/rapid7/recog/pattern/RecogPatternMatcher.java similarity index 100% rename from src/main/java/com/rapid7/recog/pattern/RecogPatternMatcher.java rename to recog/src/main/java/com/rapid7/recog/pattern/RecogPatternMatcher.java diff --git a/src/main/java/com/rapid7/recog/provider/CompositeRecogMatchersProvider.java b/recog/src/main/java/com/rapid7/recog/provider/CompositeRecogMatchersProvider.java similarity index 100% rename from src/main/java/com/rapid7/recog/provider/CompositeRecogMatchersProvider.java rename to recog/src/main/java/com/rapid7/recog/provider/CompositeRecogMatchersProvider.java diff --git a/src/main/java/com/rapid7/recog/provider/IRecogMatchersProvider.java b/recog/src/main/java/com/rapid7/recog/provider/IRecogMatchersProvider.java similarity index 100% rename from src/main/java/com/rapid7/recog/provider/IRecogMatchersProvider.java rename to recog/src/main/java/com/rapid7/recog/provider/IRecogMatchersProvider.java diff --git a/src/main/java/com/rapid7/recog/provider/RecogMatchersProvider.java b/recog/src/main/java/com/rapid7/recog/provider/RecogMatchersProvider.java similarity index 100% rename from src/main/java/com/rapid7/recog/provider/RecogMatchersProvider.java rename to recog/src/main/java/com/rapid7/recog/provider/RecogMatchersProvider.java diff --git a/src/test/java/com/rapid7/recog/CustomPatternMatcherTest.java b/recog/src/test/java/com/rapid7/recog/CustomPatternMatcherTest.java similarity index 100% rename from src/test/java/com/rapid7/recog/CustomPatternMatcherTest.java rename to recog/src/test/java/com/rapid7/recog/CustomPatternMatcherTest.java diff --git a/src/test/java/com/rapid7/recog/FingerprintMatcherTest.java b/recog/src/test/java/com/rapid7/recog/FingerprintMatcherTest.java similarity index 100% rename from src/test/java/com/rapid7/recog/FingerprintMatcherTest.java rename to recog/src/test/java/com/rapid7/recog/FingerprintMatcherTest.java diff --git a/src/test/java/com/rapid7/recog/FingerprintMatchersTest.java b/recog/src/test/java/com/rapid7/recog/FingerprintMatchersTest.java similarity index 100% rename from src/test/java/com/rapid7/recog/FingerprintMatchersTest.java rename to recog/src/test/java/com/rapid7/recog/FingerprintMatchersTest.java diff --git a/src/test/java/com/rapid7/recog/RecogIntegration.java b/recog/src/test/java/com/rapid7/recog/RecogIntegration.java similarity index 100% rename from src/test/java/com/rapid7/recog/RecogIntegration.java rename to recog/src/test/java/com/rapid7/recog/RecogIntegration.java diff --git a/src/test/java/com/rapid7/recog/TestGenerators.java b/recog/src/test/java/com/rapid7/recog/TestGenerators.java similarity index 100% rename from src/test/java/com/rapid7/recog/TestGenerators.java rename to recog/src/test/java/com/rapid7/recog/TestGenerators.java diff --git a/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java b/recog/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java similarity index 100% rename from src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java rename to recog/src/test/java/com/rapid7/recog/parser/FingerprintMatcherParserTest.java diff --git a/src/test/java/com/rapid7/recog/provider/CompositeFingerprintMatchersProviderTest.java b/recog/src/test/java/com/rapid7/recog/provider/CompositeFingerprintMatchersProviderTest.java similarity index 100% rename from src/test/java/com/rapid7/recog/provider/CompositeFingerprintMatchersProviderTest.java rename to recog/src/test/java/com/rapid7/recog/provider/CompositeFingerprintMatchersProviderTest.java From 32c4ab6174d94582f555514309b98eadc5758d5e Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 4 Oct 2021 14:38:09 -0400 Subject: [PATCH 17/21] Move VerifyStatus enum to lib --- .../java/com/rapid7/recog/RecogMatcher.java | 17 ++++++++--------- .../java/com/rapid7/recog/VerifyStatus.java | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/recog/src/main/java/com/rapid7/recog/RecogMatcher.java b/recog/src/main/java/com/rapid7/recog/RecogMatcher.java index 2c0c4d2..a7cf372 100644 --- a/recog/src/main/java/com/rapid7/recog/RecogMatcher.java +++ b/recog/src/main/java/com/rapid7/recog/RecogMatcher.java @@ -3,7 +3,6 @@ import com.rapid7.recog.pattern.JavaRegexRecogPatternMatcher; import com.rapid7.recog.pattern.RecogPatternMatchResult; import com.rapid7.recog.pattern.RecogPatternMatcher; -import com.rapid7.recog.verify.Status; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; @@ -220,22 +219,22 @@ public Map match(String input) { return null; } - public void verifyExamples(BiConsumer consumer) { + public void verifyExamples(BiConsumer consumer) { // look for the presence of test cases if (examples.size() == 0) { - consumer.accept(Status.Warn, String.format("'%s' has no test cases", description)); + consumer.accept(VerifyStatus.Warn, String.format("'%s' has no test cases", description)); } // make sure each test case passes for (FingerprintExample example : examples) { Map result = match(example.getText()); if (result == null) { - consumer.accept(Status.Fail, String.format("'%s' failed to match \"%s\" with '%s'", + consumer.accept(VerifyStatus.Fail, String.format("'%s' failed to match \"%s\" with '%s'", description, example.getText(), matcher.getPattern())); continue; } - Status status = Status.Success; + VerifyStatus verifyStatus = VerifyStatus.Success; String message = example.getText(); // Ensure that all the attributes as provided by the example were parsed // out correctly and match the capture group values we expect. @@ -247,19 +246,19 @@ public void verifyExamples(BiConsumer consumer) { } if (!result.containsKey(key) || !result.get(key).equals(value)) { - status = Status.Fail; + verifyStatus = VerifyStatus.Fail; message = String.format("'%s' failed to find expected capture group %s '%s'. Result was %s", description, key, value, result.get(key)); break; } } - consumer.accept(status, message); + consumer.accept(verifyStatus, message); } verifyExamplesHaveCaptureGroups(consumer); } - private void verifyExamplesHaveCaptureGroups(BiConsumer consumer) { + private void verifyExamplesHaveCaptureGroups(BiConsumer consumer) { Map captureGroupUsed = new HashMap<>(); // get a list of parameters that are defined by capture groups for (Entry parameter : positionalParameters.entrySet()) { @@ -285,7 +284,7 @@ private void verifyExamplesHaveCaptureGroups(BiConsumer consumer Boolean paramUsed = entry.getValue(); if (!paramUsed) { String message = String.format("'%s' is missing an example that checks for parameter '%s' which is derived from a capture group", description, paramName); - consumer.accept(Status.Warn, message); + consumer.accept(VerifyStatus.Warn, message); } } } diff --git a/recog/src/main/java/com/rapid7/recog/VerifyStatus.java b/recog/src/main/java/com/rapid7/recog/VerifyStatus.java index 95027c1..9640315 100644 --- a/recog/src/main/java/com/rapid7/recog/VerifyStatus.java +++ b/recog/src/main/java/com/rapid7/recog/VerifyStatus.java @@ -1,7 +1,7 @@ -package com.rapid7.recog.verify; +package com.rapid7.recog; // Verifier status -public enum Status { +public enum VerifyStatus { Warn, Fail, Success From 7fbe3a13fa4c9142aa3a3291d19d68597b9cc5be Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Mon, 4 Oct 2021 17:13:38 -0400 Subject: [PATCH 18/21] Bump version to 0.8.0-SNAPSHOT --- pom.xml | 2 +- recog-verify/pom.xml | 2 +- recog/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index c68f8bf..dbcb081 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.rapid7.recog recog-parent - 0.7.2-SNAPSHOT + 0.8.0-SNAPSHOT pom diff --git a/recog-verify/pom.xml b/recog-verify/pom.xml index d0255a9..d62913b 100644 --- a/recog-verify/pom.xml +++ b/recog-verify/pom.xml @@ -13,7 +13,7 @@ com.rapid7.recog recog-parent - 0.7.2-SNAPSHOT + 0.8.0-SNAPSHOT diff --git a/recog/pom.xml b/recog/pom.xml index b50babf..732fdc3 100644 --- a/recog/pom.xml +++ b/recog/pom.xml @@ -12,7 +12,7 @@ com.rapid7.recog recog-parent - 0.7.2-SNAPSHOT + 0.8.0-SNAPSHOT From ce2c8ef1ac43398803e303ef1786250f682ea860 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Tue, 5 Oct 2021 00:27:58 -0400 Subject: [PATCH 19/21] Move integration test plugins to lib pom --- pom.xml | 47 ----------------------------------------------- recog/pom.xml | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/pom.xml b/pom.xml index dbcb081..9c4c3cb 100644 --- a/pom.xml +++ b/pom.xml @@ -198,53 +198,6 @@ - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.0 - - - - integration-test - verify - - - - **/*Integration.java - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.7 - - - recog-download - pre-integration-test - - - - - - - - - - - - - - - run - - - - diff --git a/recog/pom.xml b/recog/pom.xml index 732fdc3..fa84377 100644 --- a/recog/pom.xml +++ b/recog/pom.xml @@ -76,6 +76,53 @@ + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.0 + + + + integration-test + verify + + + + **/*Integration.java + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + recog-download + pre-integration-test + + + + + + + + + + + + + + + run + + + + From d76445c71b437d3ff15fa96e66211f76897b1d8e Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Tue, 5 Oct 2021 16:49:29 -0400 Subject: [PATCH 20/21] Bump recog content version to latest 2.3.21 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9c4c3cb..9ee63b5 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ - 2.2.1 + 2.3.21 1.8 1.8 UTF-8 From 914482cc7e9b76a853a8e612954029de5d380cc9 Mon Sep 17 00:00:00 2001 From: Matthew Kienow Date: Tue, 5 Oct 2021 17:30:25 -0400 Subject: [PATCH 21/21] Use GitHub CI workflow for build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b9306d..96e6359 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Recog Java -[![Travis (.org)](https://img.shields.io/travis/rapid7/recog-java.svg)](https://travis-ci.org/rapid7/recog-java) [![Maven Central](https://img.shields.io/maven-central/v/com.rapid7.recog/recog-java.svg)](https://search.maven.org/artifact/com.rapid7.recog/recog-java) [![Javadocs](https://www.javadoc.io/badge/com.rapid7.recog/recog-java.svg)](https://www.javadoc.io/doc/com.rapid7.recog/recog-java) +[![CI workflow](https://github.com/rapid7/recog-java/actions/workflows/ci.yml/badge.svg)](https://github.com/rapid7/recog-java/actions/workflows/ci.yml) [![Maven Central](https://img.shields.io/maven-central/v/com.rapid7.recog/recog-java.svg)](https://search.maven.org/artifact/com.rapid7.recog/recog-java) [![Javadocs](https://www.javadoc.io/badge/com.rapid7.recog/recog-java.svg)](https://www.javadoc.io/doc/com.rapid7.recog/recog-java) Java implementation of [Recog](https://github.com/rapid7/recog) that supports parsing and matching.