diff --git a/bubble-server/src/main/java/bubble/resources/stream/FilterMatchersRequest.java b/bubble-server/src/main/java/bubble/resources/stream/FilterMatchersRequest.java index 2d20bbd3..cdf2106b 100644 --- a/bubble-server/src/main/java/bubble/resources/stream/FilterMatchersRequest.java +++ b/bubble-server/src/main/java/bubble/resources/stream/FilterMatchersRequest.java @@ -1,5 +1,6 @@ package bubble.resources.stream; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -21,4 +22,6 @@ public class FilterMatchersRequest { // FilterHttpResource.matchersCache cache would be useless, since every cache entry would be unique public String cacheKey() { return hashOf(fqdn, uri, userAgent, referer, remoteAddr); } + @JsonIgnore public String getUrl() { return fqdn + "/" + uri; } + } diff --git a/bubble-server/src/main/java/bubble/rule/AppRuleDriver.java b/bubble-server/src/main/java/bubble/rule/AppRuleDriver.java index f5fe1c8c..46616d3a 100644 --- a/bubble-server/src/main/java/bubble/rule/AppRuleDriver.java +++ b/bubble-server/src/main/java/bubble/rule/AppRuleDriver.java @@ -14,6 +14,7 @@ import org.cobbzilla.util.handlebars.HandlebarsUtil; import org.glassfish.grizzly.http.server.Request; import org.glassfish.jersey.server.ContainerRequest; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Map; @@ -23,6 +24,8 @@ import static org.cobbzilla.util.string.StringUtil.getPackagePath; public interface AppRuleDriver { + InputStream EMPTY_STREAM = new ByteArrayInputStream(new byte[0]); + AppRuleDriver getNext(); void setNext(AppRuleDriver next); default boolean hasNext() { return getNext() != null; } diff --git a/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlock.java b/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlock.java index df64dc53..6e35171b 100644 --- a/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlock.java +++ b/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlock.java @@ -1,9 +1,6 @@ package bubble.rule.bblock; -import bubble.abp.BlockDecision; -import bubble.abp.BlockList; -import bubble.abp.BlockListSource; -import bubble.abp.BlockSpec; +import bubble.abp.*; import bubble.model.account.Account; import bubble.model.app.AppMatcher; import bubble.model.app.AppRule; @@ -42,6 +39,7 @@ import static org.cobbzilla.util.string.StringUtil.getPackagePath; @Slf4j public class BubbleBlock extends TrafficAnalytics { + private static final String META_REQUEST = "__bubble_request"; private static final String META_BLOCK_FILTERS = "__bubble_block_filters"; private BubbleBlockConfig bubbleBlockConfig; @@ -90,11 +88,11 @@ public class BubbleBlock extends TrafficAnalytics { case allow: default: return FilterMatchResponse.NO_MATCH; case filter: - return getFilterMatchResponse(decision); + return getFilterMatchResponse(filter, decision); } } - public FilterMatchResponse getFilterMatchResponse(BlockDecision decision) { + public FilterMatchResponse getFilterMatchResponse(FilterMatchersRequest filter, BlockDecision decision) { switch (decision.getDecisionType()) { case block: return FilterMatchResponse.ABORT_NOT_FOUND; case allow: return FilterMatchResponse.NO_MATCH; @@ -106,7 +104,9 @@ public class BubbleBlock extends TrafficAnalytics { } else { return new FilterMatchResponse() .setDecision(FilterMatchDecision.match) - .setMeta(new NameAndValue[]{new NameAndValue(META_BLOCK_FILTERS, json(specs, COMPACT_MAPPER))}); + .setMeta(new NameAndValue[]{ + new NameAndValue(META_REQUEST, json(filter, COMPACT_MAPPER)) + }); } } return die("getFilterMatchResponse: invalid decisionType: "+decision.getDecisionType()); @@ -114,9 +114,51 @@ public class BubbleBlock extends TrafficAnalytics { @Override public InputStream doFilterResponse(String requestId, String contentType, NameAndValue[] meta, InputStream in) { - final String blockSpecJson = NameAndValue.find(meta, META_BLOCK_FILTERS); - if (empty(blockSpecJson) || !isHtml(contentType)) return in; + final String requestJson = NameAndValue.find(meta, META_REQUEST); + if (requestJson == null) { + log.error("doFilterResponse: no request found, returning EMPTY_STREAM"); + return EMPTY_STREAM; + } + + final FilterMatchersRequest request; + try { + request = json(requestJson, FilterMatchersRequest.class); + } catch (Exception e) { + log.error("doFilterResponse: error parsing request, returning EMPTY_STREAM: "+shortError(e)); + return EMPTY_STREAM; + } + + // Now that we know the content type, re-check the BlockList + final BlockDecision decision = blockList.getDecision(request.getFqdn(), request.getUri(), contentType); + final List filters; + switch (decision.getDecisionType()) { + case block: + log.warn("doFilterRequest: preprocessed request was filtered, but ultimate decision was block, returning EMPTY_STREAM"); + return EMPTY_STREAM; + case allow: + log.warn("doFilterRequest: preprocessed request was filtered, but ultimate decision was allow, returning as-is"); + return in; + case filter: + if (!decision.hasSpecs()) { + // should never happen + log.warn("doFilterRequest: preprocessed request was filtered, but ultimate decision was filtered, but no filters provided, returning as-is"); + return in; + } + filters = decision.getSpecs(); + break; + default: + // should never happen + log.warn("doFilterRequest: preprocessed request was filtered, but ultimate decision was invalid, returning EMPTY_STREAM"); + return EMPTY_STREAM; + } + + + if (!isHtml(contentType)) { + log.warn("doFilterRequest: cannot filter non-html response ("+request.getUrl()+"), returning as-is: "+contentType); + return in; + } + final String blockSpecJson = json(filters, COMPACT_MAPPER); final String replacement = ""; final RegexReplacementFilter filter = new RegexReplacementFilter("", replacement); final RegexFilterReader reader = new RegexFilterReader(new InputStreamReader(in), filter).setMaxMatches(1); diff --git a/utils/abp-parser b/utils/abp-parser index 2528e6ba..f82c8215 160000 --- a/utils/abp-parser +++ b/utils/abp-parser @@ -1 +1 @@ -Subproject commit 2528e6ba5c69420515c08662899e123ea9334166 +Subproject commit f82c8215876ba8e26a51541111c5befd009ad1d3