aboutsummaryrefslogtreecommitdiffstats
path: root/security-utils
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-11-23 13:04:35 +0100
committerBjørn Christian Seime <bjorncs@oath.com>2018-11-26 13:53:02 +0100
commitba26e114cd33172edb299548b243ac26a84af98c (patch)
tree82d9d9e191d20e643919ad5d18da80266411ff44 /security-utils
parent04cc3c48130b8397c04335948e5971914b2eaf22 (diff)
Add glob pattern matching for host expressions
Diffstat (limited to 'security-utils')
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/policy/HostGlobPattern.java32
-rw-r--r--security-utils/src/test/java/com/yahoo/security/tls/policy/HostGlobPatternTest.java69
2 files changed, 101 insertions, 0 deletions
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/policy/HostGlobPattern.java b/security-utils/src/main/java/com/yahoo/security/tls/policy/HostGlobPattern.java
index dbe500836e5..f667d832d9e 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/policy/HostGlobPattern.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/policy/HostGlobPattern.java
@@ -2,6 +2,7 @@
package com.yahoo.security.tls.policy;
import java.util.Objects;
+import java.util.regex.Pattern;
/**
* @author bjorncs
@@ -9,15 +10,46 @@ import java.util.Objects;
public class HostGlobPattern {
private final String pattern;
+ private final Pattern regexPattern;
public HostGlobPattern(String pattern) {
this.pattern = pattern;
+ this.regexPattern = toRegexPattern(pattern);
}
public String asString() {
return pattern;
}
+ public boolean matches(String hostString) {
+ return regexPattern.matcher(hostString).matches();
+ }
+
+ private static Pattern toRegexPattern(String pattern) {
+ StringBuilder builder = new StringBuilder("^");
+ for (char c : pattern.toCharArray()) {
+ if (c == '*') {
+ // Note: we explicitly stop matching at a dot separator boundary.
+ // This is to make host name matching less vulnerable to dirty tricks.
+ builder.append("[^.]*");
+ } else if (c == '?') {
+ // Same applies for single chars; they should only match _within_ a dot boundary.
+ builder.append("[^.]");
+ } else if (isSpecialCharacter(c)){
+ builder.append("\\");
+ builder.append(c);
+ } else {
+ builder.append(c);
+ }
+ }
+ builder.append('$');
+ return Pattern.compile(builder.toString());
+ }
+
+ private static boolean isSpecialCharacter(char c) {
+ return "\\.[]{}()<>*+-=?^$|".indexOf(c) != -1;
+ }
+
@Override
public String toString() {
return "HostGlobPattern{" +
diff --git a/security-utils/src/test/java/com/yahoo/security/tls/policy/HostGlobPatternTest.java b/security-utils/src/test/java/com/yahoo/security/tls/policy/HostGlobPatternTest.java
new file mode 100644
index 00000000000..d03ca14a728
--- /dev/null
+++ b/security-utils/src/test/java/com/yahoo/security/tls/policy/HostGlobPatternTest.java
@@ -0,0 +1,69 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.security.tls.policy;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author bjorncs
+ */
+public class HostGlobPatternTest {
+
+ @Test
+ public void glob_without_wildcards_matches_entire_string() {
+ assertTrue(globMatches("foo", "foo"));
+ assertFalse(globMatches("foo", "fooo"));
+ assertFalse(globMatches("foo", "ffoo"));
+ }
+
+ @Test
+ public void wildcard_glob_can_match_prefix() {
+ assertTrue(globMatches("foo*", "foo"));
+ assertTrue(globMatches("foo*", "foobar"));
+ assertFalse(globMatches("foo*", "ffoo"));
+ }
+
+ @Test
+ public void wildcard_glob_can_match_suffix() {
+ assertTrue(globMatches("*foo", "foo"));
+ assertTrue(globMatches("*foo", "ffoo"));
+ assertFalse(globMatches("*foo", "fooo"));
+ }
+
+ @Test
+ public void wildcard_glob_can_match_substring() {
+ assertTrue(globMatches("f*o", "fo"));
+ assertTrue(globMatches("f*o", "foo"));
+ assertTrue(globMatches("f*o", "ffoo"));
+ assertFalse(globMatches("f*o", "boo"));
+ }
+
+ @Test
+ public void wildcard_glob_does_not_cross_multiple_dot_delimiter_boundaries() {
+ assertTrue(globMatches("*.bar.baz", "foo.bar.baz"));
+ assertTrue(globMatches("*.bar.baz", ".bar.baz"));
+ assertFalse(globMatches("*.bar.baz", "zoid.foo.bar.baz"));
+ assertTrue(globMatches("foo.*.baz", "foo.bar.baz"));
+ assertFalse(globMatches("foo.*.baz", "foo.bar.zoid.baz"));
+ }
+
+ @Test
+ public void single_char_glob_matches_non_dot_characters() {
+ assertTrue(globMatches("f?o", "foo"));
+ assertFalse(globMatches("f?o", "fooo"));
+ assertFalse(globMatches("f?o", "ffoo"));
+ assertFalse(globMatches("f?o", "f.o"));
+ }
+
+ @Test
+ public void regex_special_characters_are_matched_as_literal_characters() {
+ assertTrue(globMatches("\\.[]{}()<>+-=^$|", "\\.[]{}()<>+-=^$|"));
+ }
+
+ private static boolean globMatches(String pattern, String value) {
+ return new HostGlobPattern(pattern).matches(value);
+ }
+} \ No newline at end of file