@@ -43,7 +43,10 @@ public class BlockList { | |||
public BlockDecision getDecision(String fqdn, String path, String contentType, String referer, boolean primary) { | |||
for (BlockSpec allow : whitelist) { | |||
if (allow.matches(fqdn, path, contentType, referer)) return BlockDecision.ALLOW; | |||
if (allow.matches(fqdn, path, contentType, referer)) { | |||
log.warn("getDecision: found whitelist match for fqdn="+fqdn+", path="+path); | |||
return BlockDecision.ALLOW; | |||
} | |||
} | |||
final BlockDecision decision = new BlockDecision(); | |||
for (BlockSpec block : blacklist) { | |||
@@ -14,8 +14,10 @@ import java.util.List; | |||
import static bubble.abp.selector.BlockSelector.buildSelector; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.hashOf; | |||
@Slf4j @EqualsAndHashCode(of={"target", "domainExclusions", "typeMatches", "typeExclusions", "selector"}) | |||
@Slf4j | |||
@EqualsAndHashCode(of={"target", "domainExclusions", "typeMatches", "typeExclusions", "selector", "conditionsHash"}) | |||
public class BlockSpec { | |||
public static final String BUBBLE_BLOCK_SPEC_PREFIX = "~"; | |||
@@ -40,10 +42,13 @@ public class BlockSpec { | |||
public boolean hasSelector() { return selector != null; } | |||
public boolean hasNoSelector() { return !hasSelector(); } | |||
@JsonIgnore @Getter private final String conditionsHash; | |||
public BlockSpec(String line, BlockTarget target, List<String> options, BlockSelector selector) { | |||
this.line = line; | |||
this.target = target; | |||
this.selector = selector; | |||
this.conditionsHash = target.hasConditions() ? hashOf((Object) target.getConditions()) : null; | |||
if (options != null) { | |||
for (String opt : options) { | |||
if (opt.startsWith(OPT_DOMAIN_PREFIX)) { | |||
@@ -92,7 +97,8 @@ public class BlockSpec { | |||
line = line.trim(); | |||
if (line.startsWith(BUBBLE_BLOCK_SPEC_PREFIX)) { | |||
return new SingletonList<>(new BlockSpec(line, BlockTarget.parseBubbleLine(line), null, null)); | |||
final BlockTarget target = BlockTarget.parseBubbleLine(line); | |||
return new SingletonList<>(new BlockSpec(line, target, null, null)); | |||
} | |||
int optionStartPos = line.indexOf('$'); | |||
int selectorStartPos = line.indexOf("#"); | |||
@@ -43,6 +43,7 @@ public class BlockTarget { | |||
public boolean conditionsMatch(String fqdn, String path, String contentType, String referer) { | |||
if (!hasConditions()) return false; | |||
if (!fqdn.equalsIgnoreCase(partialDomainBlock)) return false; | |||
if (hasRegex() && !getRegexPattern().matcher(fqdn+path).matches()) return false; | |||
for (BubbleBlockCondition condition : conditions) { | |||
if (!condition.matches(fqdn, path, contentType, referer)) return false; | |||
} | |||
@@ -59,9 +60,15 @@ public class BlockTarget { | |||
final int conditionsStart = line.indexOf(BUBBLE_BLOCK_SPEC_PREFIX); | |||
if (conditionsStart == -1) throw new IllegalArgumentException("parseBubbleLine: invalid line, expected "+BUBBLE_BLOCK_SPEC_PREFIX+" to begin conditions: "+line); | |||
final BlockTarget target = new BlockTarget(); | |||
target.setPartialDomainBlock(line.substring(0, conditionsStart)); | |||
target.setConditions(BubbleBlockCondition.parse(json(line.substring(conditionsStart+1), String[].class))); | |||
final String data = line.substring(0, conditionsStart); | |||
final BlockTarget target = parseTarget(data); | |||
final int slash = data.indexOf("/"); | |||
if (slash == -1) { | |||
target.setPartialDomainBlock(data); | |||
} else { | |||
target.setPartialDomainBlock(data.substring(0, slash)); | |||
} | |||
target.setConditions(BubbleBlockCondition.parse(json(line.substring(conditionsStart + 1), String[].class))); | |||
return target; | |||
} | |||
@@ -1,5 +1,6 @@ | |||
package bubble.abp; | |||
import lombok.EqualsAndHashCode; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import lombok.Setter; | |||
@@ -10,7 +11,8 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; | |||
import static org.cobbzilla.util.http.HttpSchemes.SCHEME_HTTP; | |||
import static org.cobbzilla.util.http.HttpSchemes.SCHEME_HTTPS; | |||
@NoArgsConstructor @Accessors(chain=true) @Slf4j | |||
@NoArgsConstructor @Accessors(chain=true) @EqualsAndHashCode | |||
@Slf4j | |||
public class BubbleBlockCondition { | |||
@Getter @Setter private BubbleBlockConditionField field; | |||
@@ -8,6 +8,7 @@ import org.junit.Test; | |||
import java.util.Arrays; | |||
import static bubble.abp.BlockListSource.WHITELIST_PREFIX; | |||
import static org.junit.Assert.assertEquals; | |||
public class BlockListTest { | |||
@@ -187,6 +188,46 @@ public class BlockListTest { | |||
expected, | |||
blockList.getDecision(fqdn, "/", null, referer, true).getDecisionType()); | |||
} | |||
} | |||
public static final String[][][] WHITELIST_CONDITIONAL_SPECS = { | |||
// rules | |||
{ | |||
{"foo.bar.com"}, | |||
{"@@foo.bar.com/baz"}, | |||
{"@@~foo.bar.com/quux~[\"referer_host eq bar.com\"]"}, | |||
{"@@~foo.bar.com/quux~[\"referer_host eq foo.bar.com\"]"}, | |||
}, | |||
// test | |||
{ | |||
// fqdn // path // referer // expect | |||
{"foo.com", "/", "foo.com", ALLOW}, | |||
{"foo.bar.com", "/", "bar.com", BLOCK}, | |||
{"foo.bar.com", "/foo", "bar.com", BLOCK}, | |||
{"foo.bar.com", "/baz", "bar.com", ALLOW}, | |||
{"foo.bar.com", "/quux", "baz.com", BLOCK}, | |||
{"foo.bar.com", "/quux", "bar.com", ALLOW}, | |||
{"foo.bar.com", "/quux", "foo.bar.com", ALLOW}, | |||
} | |||
}; | |||
@Test public void testWhitelistConditionalMatches () throws Exception { | |||
final BlockList blockList = new BlockList(); | |||
for (String[] rule : WHITELIST_CONDITIONAL_SPECS[0]) { | |||
final String line = rule[0]; | |||
if (line.startsWith(WHITELIST_PREFIX)) { | |||
blockList.addToWhitelist(BlockSpec.parse(line.substring(WHITELIST_PREFIX.length()))); | |||
} else { | |||
blockList.addToBlacklist(BlockSpec.parse(line)); | |||
} | |||
} | |||
for (String[] test : WHITELIST_CONDITIONAL_SPECS[1]) { | |||
final BlockDecisionType expected = BlockDecisionType.fromString(test[3]); | |||
final String fqdn = test[0]; | |||
final String path = test[1]; | |||
final String referer = test[2]; | |||
assertEquals("expected "+expected+" for test: fqdn="+fqdn+", path="+path+", referer="+referer, | |||
expected, | |||
blockList.getDecision(fqdn, path, null, referer, true).getDecisionType()); | |||
} | |||
} | |||
} |