diff options
author | Bjørn Christian Seime <bjorncs@oath.com> | 2018-11-23 13:04:35 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@oath.com> | 2018-11-26 13:53:02 +0100 |
commit | ba26e114cd33172edb299548b243ac26a84af98c (patch) | |
tree | 82d9d9e191d20e643919ad5d18da80266411ff44 /security-utils | |
parent | 04cc3c48130b8397c04335948e5971914b2eaf22 (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.java | 32 | ||||
-rw-r--r-- | security-utils/src/test/java/com/yahoo/security/tls/policy/HostGlobPatternTest.java | 69 |
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 |