Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
Expand Down
2 changes: 1 addition & 1 deletion recog-verify/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<artifactId>mockito-inline</artifactId>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ public static void main(String[] args) {
failures += verifier.getReporter().getFailureCount();
warnings += verifier.getReporter().getWarningCount();
} catch (ParseException exception) {
System.err.printf("error: parsing fingerprints file '%s': %s%n", p.toFile(), exception.getMessage());
String message = exception.getCause() != null ? exception.getCause().getMessage() : exception.getMessage();
System.err.printf("error: parsing fingerprints file '%s': %s%n", p.toFile(), message);
System.exit(-1);
}
}
Expand Down
2 changes: 1 addition & 1 deletion recog/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<artifactId>mockito-inline</artifactId>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion recog/src/main/java/com/rapid7/recog/RecogMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public void verifyExamples(BiConsumer<VerifyStatus, String> consumer) {
for (Map.Entry<String, String> entry : example.getAttributeMap().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key.equals("_encoding")) {
if (key.equals("_encoding") || key.equals("_filename")) {
continue;
}

Expand Down
32 changes: 31 additions & 1 deletion recog/src/main/java/com/rapid7/recog/parser/RecogParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
Expand All @@ -32,6 +36,7 @@
* halt processing.
*/
public class RecogParser {
private static final String FILENAME_KEY = "_filename";

/**
* Factory used to create the underlying {@link RecogPatternMatcher} used
Expand Down Expand Up @@ -188,7 +193,15 @@ public RecogMatchers parse(Reader reader, String path, String name)
attributeMap.put(attrName, attrValue);
}

fingerprintPattern.addExample(new FingerprintExample(example.getTextContent(), attributeMap));
String exampleText;
if (attributeMap.containsKey(FILENAME_KEY)) {
// process external example file
String filename = attributeMap.get(FILENAME_KEY);
exampleText = getExternalExampleText(path, name, filename);
} else {
exampleText = example.getTextContent();
}
fingerprintPattern.addExample(new FingerprintExample(exampleText, attributeMap));
}

// parse and add parameter specifications
Expand Down Expand Up @@ -260,4 +273,21 @@ private String getRequiredAttribute(Element element, String name) throws ParseEx

return value;
}

private String getExternalExampleText(String path, String name, String filename) throws ParseException {
Path examplePath;
if (path != null) {
examplePath = Paths.get(Paths.get(path).getParent().toString(), name, filename);
} else {
examplePath = Paths.get(name, filename);
}

byte[] exampleBytes;
try {
exampleBytes = Files.readAllBytes(examplePath);
} catch (IOException exception) {
throw new ParseException(String.format("Unable to process fingerprint example file '%s'", examplePath), exception);
}
return new String(exampleBytes, StandardCharsets.US_ASCII);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
import com.rapid7.recog.RecogMatcher;
import com.rapid7.recog.RecogMatchers;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import static com.rapid7.recog.RecogMatcher.pattern;
import static com.rapid7.recog.TestGenerators.anyString;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
Expand All @@ -12,8 +18,11 @@
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;

public class FingerprintMatcherParserTest {

Expand Down Expand Up @@ -97,6 +106,40 @@ public void twoValidFingerprints() throws ParseException {
new RecogMatcher(pattern("^Apache$", DOTALL, MULTILINE)).addValue("service.vendor", "Apache").addValue("service.product", "HTTPD").addValue("service.family", "Apache")));
}

@Test
public void validFingerprintExternalExampleFile() throws ParseException {
// given
int exServiceVersion = 1;
String exText = String.format("Apache %d", exServiceVersion);
String exFilename = anyString();
String xml = String.format("<?xml version=\"1.0\"?>\n"
+ "<fingerprints matches=\"http_header.server\">"
+ " <fingerprint pattern=\"^Apache (\\d)$\">\n"
+ " <description>Apache returning only its major version number</description>\n"
+ " <example _filename=\"%s\" service.version=\"%d\"></example>\n"
+ " <param pos=\"0\" name=\"service.vendor\" value=\"Apache\"/>\n"
+ " <param pos=\"0\" name=\"service.product\" value=\"HTTPD\"/>\n"
+ " <param pos=\"0\" name=\"service.family\" value=\"Apache\"/>\n"
+ " <param pos=\"1\" name=\"service.version\"/>\n"
+ " </fingerprint>\n"
+ "</fingerprints>", exFilename, exServiceVersion);

try (MockedStatic<Files> mockFiles = Mockito.mockStatic(Files.class)) {
mockFiles.when(() -> Files.readAllBytes(any(Path.class)))
.thenReturn(exText.getBytes(StandardCharsets.US_ASCII));

// when
RecogMatchers patterns = new RecogParser().parse(new StringReader(xml), anyString());

// then
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")));
assertThat(patterns.get(0).getExamples().size(), is(1));
mockFiles.verify(() -> Files.readAllBytes(any(Path.class)));
assertThat(patterns.get(0).getExamples(), contains(hasProperty("text", is(exText))));
}
}

@Test
public void invalidFingerprintsIgnored() throws ParseException {
// given
Expand Down Expand Up @@ -279,4 +322,31 @@ public void paramNonZeroPositionWithValueFailsWhenStrict() {
// then
assertEquals(expectedMessage, exception.getMessage());
}
}

@Test
public void missingExternalExampleFileFailsWhenStrict() {
// given
String exFilename = anyString();
String xml = String.format("<?xml version=\"1.0\"?>\n"
+ "<fingerprints matches=\"http_header.server\">"
+ " <fingerprint pattern=\"^Apache (\\d)$\">\n"
+ " <description>Apache returning only its major version number</description>\n"
+ " <example _filename=\"%s\"></example>\n"
+ " <param pos=\"0\" name=\"service.vendor\" value=\"Apache\"/>\n"
+ " <param pos=\"0\" name=\"service.product\" value=\"HTTPD\"/>\n"
+ " <param pos=\"0\" name=\"service.family\" value=\"Apache\"/>\n"
+ " <param pos=\"1\" name=\"service.version\"/>\n"
+ " </fingerprint>\n"
+ "</fingerprints>", exFilename);
String name = anyString();
String expectedMessage = String.format("Unable to process fingerprint example file '%s'", Paths.get(name, exFilename));

// when
Exception exception = assertThrows(ParseException.class, () -> {
new RecogParser(true).parse(new StringReader(xml), name);
}, expectedMessage);

// then
assertEquals(expectedMessage, exception.getMessage());
}
}