Sfoglia il codice sorgente

feature/fqdn_show_block_stats (#45)

show shadownban blocks in shadowban app

WIP. refactor, cleanup

WIP. per site stats setting working. cleaning up in-page app ui

WIP. allow disabling block stats per fqdn

Co-authored-by: Jonathan Cobb <jonathan@kyuss.org>
Reviewed-on: #45
tags/v1.0.4
jonathan 4 anni fa
parent
commit
948107da23
31 ha cambiato i file con 577 aggiunte e 200 eliminazioni
  1. +67
    -13
      bubble-server/src/main/java/bubble/app/bblock/BubbleBlockAppConfigDriver.java
  2. +2
    -0
      bubble-server/src/main/java/bubble/app/bblock/BubbleBlockAppDataDriver.java
  3. +29
    -2
      bubble-server/src/main/java/bubble/dao/app/AppDataDAO.java
  4. +8
    -0
      bubble-server/src/main/java/bubble/model/app/AppData.java
  5. +14
    -0
      bubble-server/src/main/java/bubble/model/app/HasAppDataCallback.java
  6. +1
    -0
      bubble-server/src/main/java/bubble/model/app/config/AppConfigView.java
  7. +16
    -3
      bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java
  8. +8
    -6
      bubble-server/src/main/java/bubble/rule/AbstractAppRuleDriver.java
  9. +2
    -0
      bubble-server/src/main/java/bubble/rule/AppRuleDriver.java
  10. +2
    -1
      bubble-server/src/main/java/bubble/rule/BubbleAlternateRegexReplacement.java
  11. +59
    -10
      bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java
  12. +42
    -0
      bubble-server/src/main/java/bubble/rule/bblock/BubbleHideStats.java
  13. +3
    -0
      bubble-server/src/main/java/bubble/service/cloud/DeviceIdService.java
  14. +28
    -2
      bubble-server/src/main/java/bubble/service/cloud/StandardDeviceIdService.java
  15. +14
    -8
      bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java
  16. +13
    -7
      bubble-server/src/main/java/bubble/service/stream/StandardRuleEngineService.java
  17. +28
    -4
      bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs
  18. +69
    -87
      bubble-server/src/main/resources/bubble/rule/bblock/BubbleBlockRuleDriver_stats.js.hbs
  19. +109
    -34
      bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs
  20. +6
    -6
      bubble-server/src/main/resources/bubble/rule/social/block/site/FB.js.hbs
  21. +2
    -2
      bubble-server/src/main/resources/bubble/rule/social/block/site/HackerNews.js.hbs
  22. +2
    -2
      bubble-server/src/main/resources/bubble/rule/social/block/site/MR.js.hbs
  23. +2
    -2
      bubble-server/src/main/resources/bubble/rule/social/block/site/Reason.js.hbs
  24. +2
    -2
      bubble-server/src/main/resources/bubble/rule/social/block/site/Twitter.js.hbs
  25. +3
    -3
      bubble-server/src/main/resources/logback.xml
  26. +29
    -2
      bubble-server/src/main/resources/models/apps/bubble_block/bubbleApp_bubbleBlock.json
  27. +6
    -0
      bubble-server/src/main/resources/models/apps/user_block/bubbleApp_userBlock.json
  28. +7
    -2
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_conn_check.py
  29. +2
    -0
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/dns_spoofing.py
  30. +1
    -1
      bubble-server/src/main/resources/packer/roles/mitmproxy/tasks/main.yml
  31. +1
    -1
      bubble-web

+ 67
- 13
bubble-server/src/main/java/bubble/app/bblock/BubbleBlockAppConfigDriver.java Vedi File

@@ -7,20 +7,18 @@ package bubble.app.bblock;
import bubble.abp.BlockDecision;
import bubble.abp.BlockListSource;
import bubble.abp.BlockSpec;
import bubble.dao.app.AppDataDAO;
import bubble.dao.app.AppMatcherDAO;
import bubble.dao.app.AppSiteDAO;
import bubble.model.account.Account;
import bubble.model.app.AppMatcher;
import bubble.model.app.AppRule;
import bubble.model.app.BubbleApp;
import bubble.model.app.RuleDriver;
import bubble.model.app.*;
import bubble.model.app.config.AppConfigDriverBase;
import bubble.model.device.Device;
import bubble.rule.bblock.BubbleBlockConfig;
import bubble.rule.bblock.BubbleBlockList;
import bubble.rule.bblock.BubbleBlockRuleDriver;
import bubble.rule.bblock.BubbleUserAgentBlock;
import bubble.rule.bblock.*;
import bubble.server.BubbleConfiguration;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.collection.ExpirationMap;
import org.cobbzilla.util.string.ValidationRegexes;
import org.cobbzilla.wizard.validation.ValidationResult;
import org.springframework.beans.factory.annotation.Autowired;
@@ -28,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
import java.util.stream.Collectors;

import static bubble.rule.bblock.BubbleBlockRuleDriver.fqdnFromKey;
import static java.util.Collections.emptySet;
import static java.util.concurrent.TimeUnit.HOURS;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError;
import static org.cobbzilla.util.http.HttpSchemes.SCHEME_HTTPS;
@@ -49,10 +49,27 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
public static final String VIEW_manageList = "manageList";
public static final String VIEW_manageRules = "manageRules";
public static final String VIEW_manageUserAgents = "manageUserAgents";
public static final String VIEW_manageHideStats = "manageHideStats";
public static final AppMatcher TEST_MATCHER = new AppMatcher();
public static final Device TEST_DEVICE = new Device();
public static final String PREFIX_APPDATA_HIDE_STATS = "hideStats_";
public static final String DEFAULT_MATCHER_NAME = "BubbleBlockMatcher";
public static final String DEFAULT_SITE_NAME = "All_Sites";

@Autowired private BubbleConfiguration configuration;
@Autowired private AppDataDAO dataDAO;
@Autowired private AppMatcherDAO matcherDAO;
@Autowired private AppSiteDAO siteDAO;

private final Map<Account, AppMatcher> defaultMatchers = new ExpirationMap<>(4, HOURS.toMillis(1));
private AppMatcher getDefaultMatcher (Account account, BubbleApp app) {
return defaultMatchers.computeIfAbsent(account, a -> matcherDAO.findByAccountAndAppAndId(account.getUuid(), app.getUuid(), DEFAULT_MATCHER_NAME));
}

private final Map<Account, AppSite> defaultSites = new ExpirationMap<>(4, HOURS.toMillis(1));
private AppSite getDefaultSite (Account account, BubbleApp app) {
return defaultSites.computeIfAbsent(account, a -> siteDAO.findByAccountAndAppAndId(account.getUuid(), app.getUuid(), DEFAULT_SITE_NAME));
}

@Override public Object getView(Account account, BubbleApp app, String view, Map<String, String> params) {
String id = params.get(PARAM_ID);
@@ -74,6 +91,9 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {

case VIEW_manageUserAgents:
return loadUserAgentBlocks(account, app);

case VIEW_manageHideStats:
return loadHideStats(account, app);
}
throw notFoundEx(view);
}
@@ -83,7 +103,8 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
if (list == null) throw notFoundEx(id);
if (list.hasAdditionalEntries()) {
return Arrays.stream(list.getAdditionalEntries())
.map(BlockListEntry::additionalRule).collect(Collectors.toCollection(TreeSet::new));
.map(BlockListEntry::additionalRule)
.collect(Collectors.toCollection(TreeSet::new));
}
return emptySet();
}
@@ -103,11 +124,9 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
loadDriver(account, rule, BubbleBlockRuleDriver.class); // validate proper driver

final BubbleBlockConfig blockConfig = json(rule.getConfigJson(), BubbleBlockConfig.class);
final List<BubbleBlockList> blockLists = new ArrayList<>();
blockLists.addAll( Arrays.stream(blockConfig.getBlockLists())
return Arrays.stream(blockConfig.getBlockLists())
.map(list -> list.setRule(rule))
.collect(Collectors.toList()) );
return blockLists;
.collect(Collectors.toList());
}

private BubbleUserAgentBlock[] loadUserAgentBlocks(Account account, BubbleApp app) {
@@ -120,6 +139,13 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
return empty(blocks) ? BubbleUserAgentBlock.NO_BLOCKS : blocks;
}

private List<BubbleHideStats> loadHideStats(Account account, BubbleApp app) {
return dataDAO.findByAccountAndAppAndAndKeyPrefix(account.getUuid(), app.getUuid(), PREFIX_APPDATA_HIDE_STATS)
.stream()
.map(BubbleHideStats::new)
.collect(Collectors.toList());
}

public static final String ACTION_enableList = "enableList";
public static final String ACTION_disableList = "disableList";
public static final String ACTION_createList = "createList";
@@ -130,6 +156,8 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
public static final String ACTION_createUserAgentBlock = "createUserAgentBlock";
public static final String ACTION_removeUserAgentBlock = "removeUserAgentBlock";
public static final String ACTION_testUrl = "testUrl";
public static final String ACTION_removeHideStats = "removeHideStats";
public static final String ACTION_createHideStats = "createHideStats";

public static final String PARAM_URL = "url";
public static final String PARAM_RULE = "rule";
@@ -137,6 +165,7 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
public static final String PARAM_TEST_URL = "testUrl";
public static final String PARAM_TEST_USER_AGENT = "testUserAgent";
public static final String PARAM_TEST_URL_PRIMARY = "testUrlPrimary";
public static final String PARAM_FQDN = "fqdn";

@Override public Object takeAppAction(Account account,
BubbleApp app,
@@ -153,6 +182,8 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
return testUrl(account, app, data);
case ACTION_createUserAgentBlock:
return createUserAgentBlock(account, app, params, data);
case ACTION_createHideStats:
return createHideStats(account, app, params, data);
}
throw notFoundEx(action);
}
@@ -286,6 +317,26 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {
return blockConfig.getUserAgentBlocks();
}


private BubbleHideStats createHideStats(Account account, BubbleApp app, Map<String, String> params, JsonNode data) {
final JsonNode fqdnNode = data.get(PARAM_FQDN);
if (fqdnNode == null) throw invalidEx("err.fqdn.required");
final String fqdn = fqdnNode.textValue();
final BubbleHideStats showStats = new BubbleHideStats(fqdn);
final AppData appData = dataDAO.set(showStats.toAppData(account, app, getDefaultMatcher(account, app), getDefaultSite(account, app)));
showStats.setId(appData.getUuid());
return showStats;
}

private List<BubbleHideStats> removeHideStats(Account account, BubbleApp app, String uuid) {
final AppData appData = dataDAO.findByAccountAndId(account.getUuid(), uuid);
if (appData != null) {
final String fqdn = fqdnFromKey(appData.getKey()); // sanity check that key is in the right format
dataDAO.delete(appData.getUuid());
}
return loadHideStats(account, app);
}

@Override public Object takeItemAction(Account account,
BubbleApp app,
String view,
@@ -329,6 +380,9 @@ public class BubbleBlockAppConfigDriver extends AppConfigDriverBase {

case ACTION_removeUserAgentBlock:
return removeUserAgentBlock(account, app, id);

case ACTION_removeHideStats:
return removeHideStats(account, app, id);
}

throw notFoundEx(action);


+ 2
- 0
bubble-server/src/main/java/bubble/app/bblock/BubbleBlockAppDataDriver.java Vedi File

@@ -5,5 +5,7 @@
package bubble.app.bblock;

import bubble.app.analytics.TrafficAnalyticsAppDataDriver;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BubbleBlockAppDataDriver extends TrafficAnalyticsAppDataDriver {}

+ 29
- 2
bubble-server/src/main/java/bubble/dao/app/AppDataDAO.java Vedi File

@@ -15,7 +15,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static org.cobbzilla.util.daemon.ZillaRuntime.die;
import static org.hibernate.criterion.Restrictions.and;
@@ -50,6 +53,10 @@ public class AppDataDAO extends AppTemplateEntityDAO<AppData> {
return filterExpired(findByFields("account", account, "app", app, "key", key));
}

public List<AppData> findByAccountAndAppAndAndKeyPrefix(String account, String app, String keyPrefix) {
return filterExpired(findByFieldsEqualAndFieldLike("account", account, "app", app, "key", keyPrefix+"%"));
}

public List<AppData> findEnabledByAccountAndAppAndSite(String account, String app, String site) {
return filterExpired(findByFields("account", account, "app", app, "site", site, "enabled", true));
}
@@ -68,6 +75,9 @@ public class AppDataDAO extends AppTemplateEntityDAO<AppData> {
return data;
}

private final Map<String, Function<AppData, AppData>> dataSetCallbacks = new HashMap<>();
public void registerCallback(String appUuid, Function<AppData, AppData> callback) { dataSetCallbacks.put(appUuid, callback); }

public AppData set(AppData data) {
final AppData found = findByAppAndSiteAndKey(data.getApp(), data.getSite(), data.getKey());
if (data.getAccount() == null) {
@@ -76,11 +86,16 @@ public class AppDataDAO extends AppTemplateEntityDAO<AppData> {
if (app == null) return die("set: App not found: "+data.getApp());
data.setAccount(app.getAccount());
}
if (found == null) return create(data);

final Function<AppData, AppData> callback = dataSetCallbacks.get(data.getApp());
log.info("set: found callback="+callback+" for app: "+data.getApp());

if (found == null) return callback == null ? create(data) : callback.apply(create(data).setCreating(true));

if (!found.getSite().equals(data.getSite())) return die("set: matcher mismatch: found ("+found.getUuid()+"/"+found.getKey()+") with site "+found.getSite()+", update has site: "+data.getSite());
found.update(data);
return update(found);
final AppData updated = update(found);
return callback != null ? callback.apply(updated) : updated;
}

public List<AppData> findByExample(Account account, AppData basis) {
@@ -111,4 +126,16 @@ public class AppDataDAO extends AppTemplateEntityDAO<AppData> {
log.info("deleteApp: deleted "+count+" AppData records for app "+uuid);
}

@Override public void delete(String uuid) {
final AppData data = findByUuid(uuid);
if (data == null) return;
final BubbleApp app = appDAO.findByUuid(data.getApp());
if (app != null) {
final Function<AppData, AppData> callback = dataSetCallbacks.get(app.getUuid());
if (callback != null) {
callback.apply(data.setDeleting(true));
}
}
super.delete(uuid);
}
}

+ 8
- 0
bubble-server/src/main/java/bubble/model/app/AppData.java Vedi File

@@ -7,6 +7,7 @@ package bubble.model.app;
import bubble.model.account.Account;
import bubble.model.device.Device;
import bubble.rule.RuleConfig;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -27,6 +28,7 @@ import javax.persistence.Transient;
import javax.validation.constraints.Size;

import static bubble.ApiConstants.EP_DATA;
import static org.cobbzilla.util.daemon.ZillaRuntime.bool;
import static org.cobbzilla.util.daemon.ZillaRuntime.now;
import static org.cobbzilla.util.reflect.ReflectionUtil.copy;
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_STRING;
@@ -156,4 +158,10 @@ public class AppData extends IdentifiableBase implements AppTemplateEntity {
@JsonProperty @Override public long getCtime () { return super.getCtime(); }
@JsonProperty @Override public long getMtime () { return super.getMtime(); }

@Transient @JsonIgnore @Getter @Setter private Boolean creating;
public boolean creating () { return bool(creating); }

@Transient @JsonIgnore @Getter @Setter private Boolean deleting;
public boolean deleting () { return bool(deleting); }

}

+ 14
- 0
bubble-server/src/main/java/bubble/model/app/HasAppDataCallback.java Vedi File

@@ -0,0 +1,14 @@
package bubble.model.app;

import bubble.model.account.Account;
import bubble.server.BubbleConfiguration;

import java.util.function.Function;

public interface HasAppDataCallback {

void prime(Account account, BubbleApp app, BubbleConfiguration configuration);

Function<AppData, AppData> createCallback(Account account, BubbleApp app, BubbleConfiguration configuration);

}

+ 1
- 0
bubble-server/src/main/java/bubble/model/app/config/AppConfigView.java Vedi File

@@ -13,6 +13,7 @@ public class AppConfigView {

@Getter @Setter private String name;
@Getter @Setter private AppConfigScope scope;
@Getter @Setter private String when;

@Getter @Setter private Boolean root = false;
public boolean root() { return root != null && root; }


+ 16
- 3
bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java Vedi File

@@ -178,7 +178,7 @@ public class FilterHttpResource {
}

if (isLocalIp) {
final boolean showStats = showStats(accountUuid);
final boolean showStats = showStats(accountUuid, connCheckRequest.getAddr(), connCheckRequest.getFqdns());
final DeviceSecurityLevel secLevel = device.getSecurityLevel();
if (showStats && secLevel.supportsRequestModification()) {
// allow it for now
@@ -236,7 +236,20 @@ public class FilterHttpResource {
@Getter(lazy=true) private final BubbleNode thisNode = selfNodeService.getThisNode();
@Getter(lazy=true) private final BubbleNetwork thisNetwork = selfNodeService.getThisNetwork();

public boolean showStats(String accountUuid) { return deviceIdService.doShowBlockStats(accountUuid); }
public boolean showStats(String accountUuid, String ip, String[] fqdns) {
if (!deviceIdService.doShowBlockStats(accountUuid)) return false;
for (String fqdn : fqdns) {
final Boolean show = deviceIdService.doShowBlockStatsForIpAndFqdn(ip, fqdn);
if (show != null) return show;
}
return true;
}

public boolean showStats(String accountUuid, String ip, String fqdn) {
if (!deviceIdService.doShowBlockStats(accountUuid)) return false;
final Boolean show = deviceIdService.doShowBlockStatsForIpAndFqdn(ip, fqdn);
return show == null || show;
}

@POST @Path(EP_MATCHERS+"/{requestId}")
@Consumes(APPLICATION_JSON)
@@ -277,7 +290,7 @@ public class FilterHttpResource {
// if this is for a local ip, it's an automatic block
// legitimate local requests would have been passthru and never reached here
final boolean isLocalIp = isForLocalIp(filterRequest);
final boolean showStats = showStats(device.getAccount());
final boolean showStats = showStats(device.getAccount(), filterRequest.getClientAddr(), filterRequest.getFqdn());
if (isLocalIp) {
if (filterRequest.isBrowser() && showStats) {
blockStats.record(filterRequest, FilterMatchDecision.abort_not_found);


+ 8
- 6
bubble-server/src/main/java/bubble/rule/AbstractAppRuleDriver.java Vedi File

@@ -165,17 +165,19 @@ public abstract class AbstractAppRuleDriver implements AppRuleDriver {
+ getBubbleJs(filterRequest, filterCtx, bubbleJsTemplate, defaultSiteTemplate, siteJsInsertionVar, showIcon)
+ getScriptClose();

final String prefix = getClass().getSimpleName()+".filterInsertJs("+filterRequest.getUrl()+"): ";

// Do any alternates apply?
List<BubbleAlternateRegexReplacement> alternates = null;
if (modConfig.hasAlternateRegexReplacements()) {
final FilterMatchersRequest request = filterRequest.getMatchersResponse().getRequest();
for (BubbleAlternateRegexReplacement alt : modConfig.getAlternateRegexReplacements()) {
if (alt.matches(request.getFqdn())) {
if (log.isDebugEnabled()) log.debug("filterInsertJs: including alternate filter: "+alt);
if (log.isDebugEnabled()) log.debug(prefix + "including alternate filter: " +alt);
if (alternates == null) alternates = new ArrayList<>();
alternates.add(alt);
} else {
if (log.isDebugEnabled()) log.debug("filterInsertJs: NOT including alternate filter: "+alt);
if (log.isDebugEnabled()) log.debug(prefix + "NOT including alternate filter: " +alt);
}
}
}
@@ -184,20 +186,20 @@ public abstract class AbstractAppRuleDriver implements AppRuleDriver {
RegexFilterReader reader;
if (alternates != null) {
final BubbleAlternateRegexReplacement firstAlt = alternates.get(0);
if (log.isInfoEnabled()) log.info("filterInsertJs: using alternate filter (0): "+firstAlt);
if (log.isInfoEnabled()) log.info(prefix + "using alternate filter (0): " +firstAlt);
reader = new RegexFilterReader(new InputStreamReader(in), firstAlt.regexFilter(filterRequest, replacement))
.setName(filterNamePrefix + "(alt0: "+firstAlt.getFqdnMatch()+") " + firstAlt.getInsertionRegex())
.setMaxMatches(1);
for (int i=1; i<alternates.size(); i++) {
final BubbleAlternateRegexReplacement alt = alternates.get(i);
if (log.isInfoEnabled()) log.info("filterInsertJs: using alternate filter ("+i+"): "+alt);
if (log.isInfoEnabled()) log.info(prefix + "using alternate filter (" +i+"): "+alt);
reader = new RegexFilterReader(reader, alt.regexFilter(filterRequest, replacement))
.setName(filterNamePrefix + "(alt"+i+": "+alt.getFqdnMatch()+") " + alt.getInsertionRegex())
.setMaxMatches(1);
}

} else {
if (log.isInfoEnabled()) log.info("filterInsertJs: using default filter: "+getInsertionRegex());
if (log.isInfoEnabled()) log.info(prefix + "using default filter: " +getInsertionRegex());
reader = new RegexFilterReader(new InputStreamReader(in), new RegexReplacementFilter(getInsertionRegex(), replacement))
.setName(filterNamePrefix + getInsertionRegex())
.setMaxMatches(1);
@@ -205,7 +207,7 @@ public abstract class AbstractAppRuleDriver implements AppRuleDriver {

if (modConfig.hasAdditionalRegexReplacements()) {
for (BubbleRegexReplacement re : modConfig.getAdditionalRegexReplacements()) {
if (log.isInfoEnabled()) log.info("filterInsertJs: using additional filter: "+re.getInsertionRegex());
if (log.isInfoEnabled()) log.info(prefix + "using additional filter: " +re.getInsertionRegex());
reader = new RegexFilterReader(reader, re.regexFilter(filterRequest, replacement))
.setName(filterNamePrefix+" (additional) "+re.getInsertionRegex());
}


+ 2
- 0
bubble-server/src/main/java/bubble/rule/AppRuleDriver.java Vedi File

@@ -5,6 +5,7 @@
package bubble.rule;

import bubble.model.account.Account;
import bubble.model.app.AppData;
import bubble.model.app.AppMatcher;
import bubble.model.app.AppRule;
import bubble.model.app.BubbleApp;
@@ -26,6 +27,7 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
import static org.cobbzilla.util.daemon.ZillaRuntime.now;


+ 2
- 1
bubble-server/src/main/java/bubble/rule/BubbleAlternateRegexReplacement.java Vedi File

@@ -1,5 +1,6 @@
package bubble.rule;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@@ -13,7 +14,7 @@ public class BubbleAlternateRegexReplacement extends BubbleRegexReplacement {

@Getter @Setter private String fqdnMatch;

@Getter(lazy=true) private final Pattern pattern = Pattern.compile(fqdnMatch, CASE_INSENSITIVE);
@JsonIgnore @Getter(lazy=true) private final Pattern pattern = Pattern.compile(fqdnMatch, CASE_INSENSITIVE);

public boolean matches (String fqdn) { return getPattern().matcher(fqdn).matches(); }



+ 59
- 10
bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java Vedi File

@@ -5,10 +5,9 @@
package bubble.rule.bblock;

import bubble.abp.*;
import bubble.dao.app.AppDataDAO;
import bubble.model.account.Account;
import bubble.model.app.AppMatcher;
import bubble.model.app.AppRule;
import bubble.model.app.BubbleApp;
import bubble.model.app.*;
import bubble.model.device.Device;
import bubble.resources.stream.FilterHttpRequest;
import bubble.resources.stream.FilterMatchersRequest;
@@ -16,6 +15,8 @@ import bubble.rule.FilterMatchDecision;
import bubble.rule.RequestModifierConfig;
import bubble.rule.RequestModifierRule;
import bubble.rule.analytics.TrafficAnalyticsRuleDriver;
import bubble.server.BubbleConfiguration;
import bubble.service.cloud.DeviceIdService;
import bubble.service.stream.AppRuleHarness;
import bubble.service.stream.ConnectionCheckResponse;
import com.fasterxml.jackson.databind.JsonNode;
@@ -32,19 +33,24 @@ import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

import static bubble.app.bblock.BubbleBlockAppConfigDriver.PREFIX_APPDATA_HIDE_STATS;
import static bubble.service.stream.HttpStreamDebug.getLogFqdn;
import static java.util.concurrent.TimeUnit.DAYS;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.io.StreamUtil.stream2string;
import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.util.string.StringUtil.EMPTY;
import static org.cobbzilla.util.string.StringUtil.getPackagePath;
import static org.cobbzilla.util.string.ValidationRegexes.HOST_PATTERN;
import static org.cobbzilla.util.string.ValidationRegexes.validateRegexMatches;
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx;

@Slf4j
public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements RequestModifierRule {
public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver
implements RequestModifierRule, HasAppDataCallback {

private final AtomicReference<BlockList> blockList = new AtomicReference<>(new BlockList());
private BlockList getBlockList() { return blockList.get(); }
@@ -60,7 +66,11 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements

private final static Map<String, BlockListSource> blockListCache = new ConcurrentHashMap<>();

public boolean showStats() { return deviceService.doShowBlockStats(account.getUuid()); }
public boolean showStats(String ip, String fqdn) {
if (!deviceService.doShowBlockStats(account.getUuid())) return false;
final Boolean show = deviceService.doShowBlockStatsForIpAndFqdn(ip, fqdn);
return show == null || show;
}

@Override public <C> Class<C> getConfigClass() { return (Class<C>) BubbleBlockConfig.class; }

@@ -68,7 +78,8 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements

@Override public boolean couldModify(FilterHttpRequest request) {
final BubbleBlockConfig config = getRuleConfig();
return request.isHtml() && request.isBrowser() && (config.inPageBlocks() || showStats());
final FilterMatchersRequest req = request.getMatchersResponse().getRequest();
return request.isHtml() && request.isBrowser() && (config.inPageBlocks() || showStats(req.getClientAddr(), req.getFqdn()));
}

@Override public void init(JsonNode config,
@@ -246,7 +257,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements
return refererDecision;
}
}
if (showStats()) {
if (showStats(filter.getClientAddr(), filter.getFqdn())) {
if (log.isInfoEnabled()) log.info(prefix+"decision was "+decisionType+" but showStats=true, returning match");
else if (extraLog) log.error(prefix+"decision was "+decisionType+" but showStats=true, returning match");
return FilterMatchDecision.match;
@@ -355,7 +366,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements
return in;
}

final boolean showStats = showStats();
final boolean showStats = showStats(request.getClientAddr(), request.getFqdn());
if (!bubbleBlockConfig.inPageBlocks() && !showStats) {
if (log.isInfoEnabled()) log.info(prefix + "SEND: both inPageBlocks and showStats are false, returning as-is");
return in;
@@ -398,4 +409,42 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements
return ctx;
}

public static String fqdnFromKey(String key) {
if (!key.startsWith(PREFIX_APPDATA_HIDE_STATS)) die("expected key to start with '"+ PREFIX_APPDATA_HIDE_STATS +"', was: "+ key);
return key.substring(PREFIX_APPDATA_HIDE_STATS.length());
}

@Override public void prime(Account account, BubbleApp app, BubbleConfiguration configuration) {
final DeviceIdService deviceIdService = configuration.getBean(DeviceIdService.class);
final AppDataDAO dataDAO = configuration.getBean(AppDataDAO.class);
log.info("priming app="+app.getName());
dataDAO.findByAccountAndAppAndAndKeyPrefix(account.getUuid(), app.getUuid(), PREFIX_APPDATA_HIDE_STATS)
.forEach(data -> deviceIdService.setBlockStatsForFqdn(account, fqdnFromKey(data.getKey()), false));
}

@Override public Function<AppData, AppData> createCallback(Account account,
BubbleApp app,
BubbleConfiguration configuration) {
return data -> {
final String prefix = "createCallbackB("+data.getKey()+"="+data.getData()+"): ";
log.info(prefix+"starting");
if (data.getKey().startsWith(PREFIX_APPDATA_HIDE_STATS)) {
final DeviceIdService deviceIdService = configuration.getBean(DeviceIdService.class);
final String fqdn = fqdnFromKey(data.getKey());
if (validateRegexMatches(HOST_PATTERN, fqdn)) {
if (data.deleting()) {
log.info(prefix+"unsetting fqdn: "+fqdn);
deviceIdService.unsetBlockStatsForFqdn(account, fqdn);
} else {
log.info(prefix+"setting fqdn: "+fqdn);
deviceIdService.setBlockStatsForFqdn(account, fqdn, false);
}
} else {
throw invalidEx("err.fqdn.invalid", "not a valid FQDN: "+fqdn, fqdn);
}
}
return data;
};
}

}

+ 42
- 0
bubble-server/src/main/java/bubble/rule/bblock/BubbleHideStats.java Vedi File

@@ -0,0 +1,42 @@
package bubble.rule.bblock;

import bubble.model.account.Account;
import bubble.model.app.AppData;
import bubble.model.app.AppMatcher;
import bubble.model.app.AppSite;
import bubble.model.app.BubbleApp;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;

import static bubble.app.bblock.BubbleBlockAppConfigDriver.PREFIX_APPDATA_HIDE_STATS;
import static bubble.rule.bblock.BubbleBlockRuleDriver.fqdnFromKey;

@NoArgsConstructor @Accessors(chain=true)
public class BubbleHideStats {

@Getter @Setter private String id;
@Getter @Setter private String fqdn;

public BubbleHideStats(String fqdn) {
setFqdn(fqdn);
}

public BubbleHideStats(AppData datum) {
final String key = datum.getKey();
setId(datum.getUuid());
setFqdn(fqdnFromKey(key));
}

public AppData toAppData(Account account, BubbleApp app, AppMatcher matcher, AppSite site) {
return new AppData()
.setAccount(account.getUuid())
.setApp(app.getUuid())
.setMatcher(matcher.getUuid())
.setSite(site.getUuid())
.setKey(PREFIX_APPDATA_HIDE_STATS +fqdn)
.setData("true");
}

}

+ 3
- 0
bubble-server/src/main/java/bubble/service/cloud/DeviceIdService.java Vedi File

@@ -21,6 +21,9 @@ public interface DeviceIdService {

void initBlockStats (Account account);
default boolean doShowBlockStats(String accountUuid) { return false; }
default Boolean doShowBlockStatsForIpAndFqdn(String ip, String fqdn) { return false; }
default void setBlockStatsForFqdn (Account account, String fqdn, boolean value) {}
default void unsetBlockStatsForFqdn (Account account, String fqdn) {}

DeviceStatus getDeviceStatus(String deviceUuid);
DeviceStatus getLiveDeviceStatus(String deviceUuid);


+ 28
- 2
bubble-server/src/main/java/bubble/service/cloud/StandardDeviceIdService.java Vedi File

@@ -12,6 +12,7 @@ import bubble.model.app.AppSite;
import bubble.model.device.Device;
import bubble.model.device.DeviceStatus;
import bubble.server.BubbleConfiguration;
import bubble.service.stream.StandardRuleEngineService;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.collection.ExpirationMap;
import org.cobbzilla.util.collection.SingletonList;
@@ -68,6 +69,7 @@ public class StandardDeviceIdService implements DeviceIdService {
@Autowired private RedisService redis;
@Autowired private GeoService geoService;
@Autowired private AppSiteDAO siteDAO;
@Autowired private StandardRuleEngineService ruleEngine;
@Autowired private BubbleConfiguration configuration;

private final Map<String, Device> deviceCache = new ExpirationMap<>(MINUTES.toMillis(10));
@@ -159,7 +161,7 @@ public class StandardDeviceIdService implements DeviceIdService {
}
}

public void initBlockStats (Account account) {
@Override public void initBlockStats (Account account) {
final boolean showBlockStats = configuration.showBlockStatsSupported() && account.showBlockStats();
redis.set_plaintext(REDIS_KEY_ACCOUNT_SHOW_BLOCK_STATS+account.getUuid(), Boolean.toString(showBlockStats));
redis.del_matching_withPrefix(REDIS_KEY_CHUNK_FILTER_PASS+"*");
@@ -172,11 +174,17 @@ public class StandardDeviceIdService implements DeviceIdService {
}
}

public boolean doShowBlockStats(String accountUuid) {
@Override public boolean doShowBlockStats(String accountUuid) {
return configuration.showBlockStatsSupported()
&& Boolean.parseBoolean(redis.get_plaintext(REDIS_KEY_ACCOUNT_SHOW_BLOCK_STATS + accountUuid));
}

@Override public Boolean doShowBlockStatsForIpAndFqdn(String ip, String fqdn) {
if (!configuration.showBlockStatsSupported()) return false;
final String val = redis.get_plaintext(REDIS_KEY_DEVICE_SHOW_BLOCK_STATS + ip + ":" + fqdn);
return val == null ? null : Boolean.parseBoolean(val);
}

public void showBlockStats (Device device) {
final Set<String> configuredIps = NetworkUtil.configuredIps();
final String privateIp = configuredIps.stream()
@@ -200,6 +208,24 @@ public class StandardDeviceIdService implements DeviceIdService {
}
}

@Override public void setBlockStatsForFqdn(Account account, String fqdn, boolean value) {
for (Device device : deviceDAO.findByAccount(account.getUuid())) {
for (String ip : findIpsByDevice(device.getUuid())) {
redis.set_plaintext(REDIS_KEY_DEVICE_SHOW_BLOCK_STATS + ip + ":" + fqdn, String.valueOf(value));
}
}
ruleEngine.flushMatchers();
}

@Override public void unsetBlockStatsForFqdn(Account account, String fqdn) {
for (Device device : deviceDAO.findByAccount(account.getUuid())) {
for (String ip : findIpsByDevice(device.getUuid())) {
redis.del_withPrefix(REDIS_KEY_DEVICE_SHOW_BLOCK_STATS + ip + ":" + fqdn);
}
}
ruleEngine.flushMatchers();
}

private final ExpirationMap<String, DeviceStatus> deviceStatusCache = new ExpirationMap<>(MINUTES.toMillis(2));

@Override public DeviceStatus getDeviceStatus(String deviceUuid) {


+ 14
- 8
bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java Vedi File

@@ -5,16 +5,10 @@
package bubble.service.stream;

import bubble.dao.account.AccountDAO;
import bubble.dao.app.AppMatcherDAO;
import bubble.dao.app.AppRuleDAO;
import bubble.dao.app.BubbleAppDAO;
import bubble.dao.app.RuleDriverDAO;
import bubble.dao.app.*;
import bubble.dao.device.DeviceDAO;
import bubble.model.account.Account;
import bubble.model.app.AppMatcher;
import bubble.model.app.AppRule;
import bubble.model.app.BubbleApp;
import bubble.model.app.RuleDriver;
import bubble.model.app.*;
import bubble.model.cloud.AnsibleInstallType;
import bubble.model.cloud.BubbleNetwork;
import bubble.model.device.Device;
@@ -45,6 +39,7 @@ public class StandardAppPrimerService implements AppPrimerService {
@Autowired private AppMatcherDAO matcherDAO;
@Autowired private AppRuleDAO ruleDAO;
@Autowired private RuleDriverDAO driverDAO;
@Autowired private AppDataDAO dataDAO;
@Autowired private RedisService redis;
@Autowired private BubbleConfiguration configuration;

@@ -129,6 +124,7 @@ public class StandardAppPrimerService implements AppPrimerService {
.collect(Collectors.toList())
: new SingletonList<>(singleApp);
for (BubbleApp app : appsToPrime) {
log.info("_prime: priming app: "+app.getUuid()+"/"+app.getName());
final List<AppRule> rules = ruleDAO.findByAccountAndApp(account.getUuid(), app.getUuid());
final List<AppMatcher> matchers = matcherDAO.findByAccountAndApp(account.getUuid(), app.getUuid());
for (AppRule rule : rules) {
@@ -137,6 +133,16 @@ public class StandardAppPrimerService implements AppPrimerService {
log.warn("_prime: driver not found for app/rule " + app.getName() + "/" + rule.getName() + ": " + rule.getDriver());
continue;
}
// handle AppData callback registration with a basic driver
final AppRuleDriver cbDriver = driver.getDriver();
if (cbDriver instanceof HasAppDataCallback) {
log.info("_prime: AppRuleDriver ("+cbDriver.getClass().getSimpleName()+") implements HasAppDataCallback, registering: "+app.getUuid()+"/"+app.getName());
final HasAppDataCallback dataCallback = (HasAppDataCallback) cbDriver;
dataCallback.prime(account, app, configuration);
dataDAO.registerCallback(app.getUuid(), dataCallback.createCallback(account, app, configuration));
} else {
log.info("_prime: AppRuleDriver ("+cbDriver.getClass().getSimpleName()+") does NOT implement HasAppDataCallback, NOT registering: "+app.getUuid()+"/"+app.getName());
}
for (Device device : devices) {
final Set<String> rejectDomains = new HashSet<>();
final Set<String> blockDomains = new HashSet<>();


+ 13
- 7
bubble-server/src/main/java/bubble/service/stream/StandardRuleEngineService.java Vedi File

@@ -64,8 +64,7 @@ import static java.util.concurrent.TimeUnit.MINUTES;
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import static org.apache.http.HttpHeaders.TRANSFER_ENCODING;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.daemon.ZillaRuntime.hashOf;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.http.HttpStatusCodes.OK;
import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER;
import static org.cobbzilla.util.json.JsonUtil.json;
@@ -197,7 +196,9 @@ public class StandardRuleEngineService implements RuleEngineService {
public void enableCacheFlushing () { cachedFlushingEnabled.set(true); }
public void disableCacheFlushing () { cachedFlushingEnabled.set(false); }

public Map<Object, Object> flushCaches() {
public Map<Object, Object> flushCaches() { return flushCaches(true); }

public Map<Object, Object> flushCaches(boolean prime) {
if (!cachedFlushingEnabled.get()) {
if (log.isDebugEnabled()) log.debug("flushCaches: flushing disabled");
return Collections.emptyMap();
@@ -206,9 +207,7 @@ public class StandardRuleEngineService implements RuleEngineService {
ruleCache.clear();
if (log.isDebugEnabled()) log.debug("flushCaches: flushed "+ruleEngineCacheSize+" ruleCache entries");

final RedisService matchersCache = getMatchersCache();
final Long matcherCount = matchersCache.del_matching(ALL_KEYS);
if (log.isDebugEnabled()) log.debug("flushCaches: flushed "+matcherCount+" matchersCache entries");
final Long matcherCount = flushMatchers();

final Long connCheckDeletions = redis.del_matching("bubble_conn_check_*");
if (log.isDebugEnabled()) log.debug("flushCaches: removed "+connCheckDeletions+" conn_check cache entries");
@@ -219,11 +218,18 @@ public class StandardRuleEngineService implements RuleEngineService {
{"ruleEngineCache", ruleEngineCacheSize}
});
if (log.isInfoEnabled()) log.info("flushCaches: flushed: "+json(flushStatus, COMPACT_MAPPER));
appPrimerService.primeApps();
if (prime) appPrimerService.primeApps();
return flushStatus;
}
}

public Long flushMatchers() {
final RedisService matchersCache = getMatchersCache();
final Long matcherCount = matchersCache.del_matching(ALL_KEYS);
if (log.isDebugEnabled()) log.debug("flushCaches: flushed "+matcherCount+" matchersCache entries");
return matcherCount;
}

private List<AppRuleHarness> initRules(FilterHttpRequest filterRequest) {
return initRules(filterRequest.getAccount(), filterRequest.getDevice(), filterRequest.getMatchers());
}


+ 28
- 4
bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs Vedi File

@@ -34,6 +34,28 @@
});
}

{{JS_PREFIX}}_app_title_span = function (defaultName) {
const appTitleSpan = document.createElement('span');
const appImage = document.createElement('img');
appImage.src = {{JS_PREFIX}}_asset_img_url('icon');
appImage.width = 16;
const appTitle = document.createElement('strong');
const appName = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_appName', defaultName);
appTitle.appendChild(document.createTextNode(appName));
appTitleSpan.appendChild(appImage);
appTitleSpan.appendChild(appTitle);
return appTitleSpan;
}

function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelFormat) {
const label = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, labelKey, labelDefault);
const btn = document.createElement('button');
btn.style.fontSize = 'x-small';
btn.addEventListener('click', onclick);
btn.appendChild(document.createTextNode(labelFormat ? labelFormat(label) : label));
return btn;
}

if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') {
let {{PAGE_PREFIX}}_doc_ready = false;

@@ -129,17 +151,19 @@ if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') {
}
for (let i=0; i<{{PAGE_PREFIX}}_icon_status.length; i++) {
const iconSpecs = {{PAGE_PREFIX}}_icon_status[i];
let br = document.createElement('br');
let link = document.createElement('a');
const br = document.createElement('br');
const link = document.createElement('a');
if (typeof iconSpecs.link === 'function') {
link.onclick = function (ev) { iconSpecs.link(ev); return false; }
} else {
link.href = '{{{BUBBLE_HOME}}}/app/' + iconSpecs.app + '/' + iconSpecs.link;
}
let img = document.createElement('img');
const screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
const img = document.createElement('img');
img.id = {{PAGE_PREFIX}}_getAppIconImgId(iconSpecs);
img.src = {{PAGE_PREFIX}}_getAppIconImgSrc(iconSpecs);
img.width = 48;
{{PAGE_PREFIX}}_log('screenWidth='+screenWidth);
img.width = Math.min(128, screenWidth/8);
link.appendChild(img);
bubbleControlDiv.appendChild(br);
bubbleControlDiv.appendChild(link);


+ 69
- 87
bubble-server/src/main/resources/bubble/rule/bblock/BubbleBlockRuleDriver_stats.js.hbs Vedi File

@@ -4,98 +4,46 @@ let {{JS_PREFIX}}_app_details = false;
let {{JS_PREFIX}}_last_stats = null;
let {{JS_PREFIX}}_app_stats_last_change = 0;
const {{JS_PREFIX}}_app_stats_timeout = 35000;
const {{PAGE_PREFIX}}_extra_blocks = {};

const {{PAGE_PREFIX}}_add_extra_block = function(name) {
if (!(name in {{PAGE_PREFIX}}_extra_blocks)) {
{{PAGE_PREFIX}}_extra_blocks[name] = 1;
} else {
{{PAGE_PREFIX}}_extra_blocks[name] = {{PAGE_PREFIX}}_extra_blocks[name] + 1;
}
}

const {{PAGE_PREFIX}}_add_extra_allow = function() {
if (!('_' in {{PAGE_PREFIX}}_extra_blocks)) {
{{PAGE_PREFIX}}_extra_blocks['_'] = 1;
} else {
{{PAGE_PREFIX}}_extra_blocks['_'] = {{PAGE_PREFIX}}_extra_blocks['_'] + 1;
}
}

const {{PAGE_PREFIX}}_remove_extra_allow = function() {
if (!('_' in {{PAGE_PREFIX}}_extra_blocks)) {
{{PAGE_PREFIX}}_extra_blocks['_'] = 0;
} else {
{{PAGE_PREFIX}}_extra_blocks['_'] = {{PAGE_PREFIX}}_extra_blocks['_'] - 1;
function {{JS_PREFIX}}_hide_app_details() {
const detailsDivId = '{{JS_PREFIX}}_detailsDiv';
const detailsDiv = document.getElementById(detailsDivId);
{{JS_PREFIX}}_app_details = false;
if (detailsDiv != null) {
detailsDiv.style.display = 'none';
while (detailsDiv.firstChild) {
detailsDiv.removeChild(detailsDiv.lastChild);
}
}
}

function {{JS_PREFIX}}_toggle_app_details(ev) {
function {{JS_PREFIX}}_show_app_details(ev) {
const detailsDivId = '{{JS_PREFIX}}_detailsDiv';
let detailsDiv = document.getElementById(detailsDivId);
if ({{JS_PREFIX}}_app_details) {
{{JS_PREFIX}}_app_details = false;
if (detailsDiv != null) {
detailsDiv.style.display = 'none';
{{JS_PREFIX}}_app_details = true;
{{JS_PREFIX}}_app_refresh(function () {
if ({{JS_PREFIX}}_last_stats != null) {
if (detailsDiv === null) {
detailsDiv = {{PAGE_PREFIX}}_createDetailsDiv(detailsDivId);
document.getElementsByTagName('body')[0].appendChild(detailsDiv);
}
while (detailsDiv.firstChild) {
detailsDiv.removeChild(detailsDiv.lastChild);
}
}
} else {
const extraKeys = Object.keys({{PAGE_PREFIX}}_extra_blocks);
if (extraKeys.length === 0
&& ({{JS_PREFIX}}_last_stats === null || {{JS_PREFIX}}_last_stats.blocks.length === 0)) {
return;
}
{{JS_PREFIX}}_app_details = true;
{{JS_PREFIX}}_app_refresh(function () {
if ({{JS_PREFIX}}_last_stats != null) {
if (detailsDiv === null) {
detailsDiv = {{PAGE_PREFIX}}_createDetailsDiv(detailsDivId);
document.getElementsByTagName('body')[0].appendChild(detailsDiv);
detailsDiv.onclick = function (ev) {
{{JS_PREFIX}}_toggle_app_details();
}
}
while (detailsDiv.firstChild) {
detailsDiv.removeChild(detailsDiv.lastChild);
}
detailsDiv.style.display = 'block';
// add rows for blocked stuff...
if (extraKeys.length > 0) {
extraKeys.sort(function (a, b) {
return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'});
});
let totalBlocks = 0;
let totalAllowed = 0;
extraKeys.forEach(k => {
const blockCount = {{PAGE_PREFIX}}_extra_blocks[k];
if (k === '_') {
totalAllowed = blockCount;
return;
}
totalBlocks += blockCount;
const useEm = k.startsWith('*');
const token = useEm ? k.substring(1) : k;
const entryDiv = document.createElement('div');
if (useEm) {
const em = document.createElement('em');
em.appendChild(document.createTextNode(token + ': ' + blockCount));
entryDiv.appendChild(em);
} else {
const entryText = document.createTextNode(token + ': ' + blockCount);
entryDiv.appendChild(entryText);
}
detailsDiv.appendChild(entryDiv);
});
const extraSummary = document.createElement('strong');
const summaryLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_signalNoiseRatio', 'signal/noise');
const fullTotal = totalAllowed + totalBlocks;
const ratio = 100.0 * (totalAllowed / fullTotal);
extraSummary.appendChild(document.createTextNode(summaryLabel + ': ' + totalAllowed + '/' + totalBlocks + ' ≈ ' + ratio.toLocaleString('{{ACCOUNT_LOCALE_HYPHEN}}', { maximumSignificantDigits: 3 }) + '%'));
detailsDiv.appendChild(extraSummary);
detailsDiv.appendChild(document.createElement('hr'));
}
detailsDiv.style.display = 'block';
detailsDiv.appendChild({{JS_PREFIX}}_app_title_span('BlockParty!'));
detailsDiv.appendChild(document.createElement('hr'));

// add rows for blocked stuff...
const adsAndTrackersLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_adsAndTrackers', 'Blocked Ads/Trackers');
const adsAndTrackersHeader = document.createElement('strong');
adsAndTrackersHeader.appendChild(document.createTextNode(adsAndTrackersLabel));
detailsDiv.appendChild(adsAndTrackersHeader);
if ({{JS_PREFIX}}_last_stats.blocks.length === 0) {
const emptyLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noAdsOrTrackers', '(no blocks)');
detailsDiv.appendChild(document.createElement('br'));
detailsDiv.appendChild(document.createTextNode(emptyLabel));
} else {
for (let i = 0; i < {{JS_PREFIX}}_last_stats.blocks.length; i++) {
const entry = {{JS_PREFIX}}_last_stats.blocks[i];
const entryDiv = document.createElement('div');
@@ -104,8 +52,32 @@ function {{JS_PREFIX}}_toggle_app_details(ev) {
detailsDiv.appendChild(entryDiv);
}
}
})
}
detailsDiv.appendChild(document.createElement('hr'));
const fqdn = window.location.hostname;
const hideStatsButton = {{JS_PREFIX}}_create_button('web_hideStats', 'Hide BlockParty for '+fqdn, function (e) {
// write appdata
const hide_show_stats_url = '/__bubble/api/filter/data/{{BUBBLE_DATA_ID}}/write';
const requestOptions = {
method: 'POST',
body: JSON.stringify({key: 'hideStats_'+fqdn, data: 'false'})
};
fetch(hide_show_stats_url, requestOptions)
.then(() => window.location.reload());
//.then(() => window.location.reload());
return false;
}, label => label + fqdn);

detailsDiv.appendChild(hideStatsButton);
detailsDiv.appendChild(document.createElement('hr'));
const closeButton = {{JS_PREFIX}}_create_button('web_close', 'close', function (e) {
e.stopPropagation();
e.preventDefault();
{{JS_PREFIX}}_hide_app_details();
return false;
});
detailsDiv.appendChild(closeButton);
}
});
}

const {{JS_PREFIX}}_app_refresh = function (displayFunc) {
@@ -166,12 +138,22 @@ let {{JS_PREFIX}}_app_refresh_interval = null;
const {{JS_PREFIX}}_app = {
jsPrefix: '{{JS_PREFIX}}',
app: '{{BUBBLE_APP_NAME}}',
link: {{JS_PREFIX}}_toggle_app_details,
link: {{JS_PREFIX}}_show_app_details,
icon: 'icon-gray',
onReady: function () {
{{JS_PREFIX}}_load_messages('web_', function (messages) {
{{JS_PREFIX}}_messages = messages;
})
});
document.onkeydown = function(e) {
e = e || window.event;
let isEscape = false;
if ("key" in e) {
isEscape = (e.key === "Escape" || e.key === "Esc");
} else {
isEscape = (e.keyCode === 27);
}
if (isEscape) {{JS_PREFIX}}_hide_app_details();
};
{{JS_PREFIX}}_app_refresh_interval = window.setInterval({{JS_PREFIX}}_app_refresh, 5000);
}
};


+ 109
- 34
bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs Vedi File

@@ -14,6 +14,42 @@ let {{JS_PREFIX}}_idle_interval = 2500;

let {{JS_PREFIX}}_messages = null;

const {{PAGE_PREFIX}}_block_keyword_tally = {};
const {{PAGE_PREFIX}}_block_author_tally = {};
let {{PAGE_PREFIX}}_allow_tally = 0;

function {{JS_PREFIX}}_tally_keyword_block(name) {
{{JS_PREFIX}}_incr_tally(name, {{PAGE_PREFIX}}_block_keyword_tally);
}

function {{JS_PREFIX}}_incr_tally(name, tally) {
if (!(name in tally)) {
tally[name] = 1;
} else {
tally[name] = tally[name] + 1;
}
}

function {{JS_PREFIX}}_decr_tally(name, tally) {
if (!(name in tally)) {
tally[name] = 0;
} else {
tally[name] = tally[name] - 1;
}
}

function {{JS_PREFIX}}_tally_author_block(name) {
{{JS_PREFIX}}_incr_tally(name, {{PAGE_PREFIX}}_block_author_tally);
}

function {{JS_PREFIX}}_tally_allow() {
{{PAGE_PREFIX}}_allow_tally++;
}

function {{JS_PREFIX}}_untally_allow() {
{{PAGE_PREFIX}}_allow_tally--;
}

function {{JS_PREFIX}}_uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
@@ -163,26 +199,6 @@ function {{JS_PREFIX}}_mark_evaluated(node) {
return firstEval;
}

function {{JS_PREFIX}}_tally_block(name) {
// console.log('tally_block: '+name);
if (typeof {{PAGE_PREFIX}}_add_extra_block === 'function') {
{{PAGE_PREFIX}}_add_extra_block(name);
}
}

function {{JS_PREFIX}}_tally_allow(name) {
// console.log('tally_allow: '+name);
if (typeof {{PAGE_PREFIX}}_add_extra_block === 'function') {
{{PAGE_PREFIX}}_add_extra_allow();
}
}
function {{JS_PREFIX}}_untally_allow(name) {
// console.log('untally_allow: '+name);
if (typeof {{PAGE_PREFIX}}_add_extra_block === 'function') {
{{PAGE_PREFIX}}_remove_extra_allow();
}
}

function {{JS_PREFIX}}_includes_block_keyword (element, firstEval) {
const keywords = Object.keys(Object.assign({}, {{JS_PREFIX}}_blocked_keywords, {{JS_PREFIX}}_blocked_list_keywords));
if (keywords.length > 0) {
@@ -193,14 +209,14 @@ function {{JS_PREFIX}}_includes_block_keyword (element, firstEval) {
let cskw = kw.substring(1);
if (html.indexOf(cskw) !== -1) {
// {{PAGE_PREFIX}}_log('>>> includes_block_keyword: blocking based on case-sensitive keyword: ' + cskw);
{{JS_PREFIX}}_tally_block('*'+cskw);
{{JS_PREFIX}}_tally_keyword_block(cskw);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
return true;
}
} else {
if (html.toLowerCase().indexOf(kw.toLowerCase()) !== -1) {
// {{PAGE_PREFIX}}_log('>>> includes_block_keyword: blocking based on case-insensitive keyword: ' + kw);
{{JS_PREFIX}}_tally_block('*'+kw);
{{JS_PREFIX}}_tally_keyword_block(kw);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
return true;
}
@@ -284,15 +300,6 @@ function {{JS_PREFIX}}_hide_app_details() {
}
}

function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick) {
const label = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, labelKey, labelDefault);
const btn = document.createElement('button');
btn.style.fontSize = 'x-small';
btn.addEventListener('click', onclick);
btn.appendChild(document.createTextNode(label));
return btn;
}

function {{JS_PREFIX}}_show_app_details() {
// {{PAGE_PREFIX}}_log('show_app_details called');
{{JS_PREFIX}}_refresh_blocks();
@@ -313,13 +320,71 @@ function {{JS_PREFIX}}_show_app_details() {
}
detailsDiv.style.display = 'block';
detailsDiv.style.visibility = 'visible';
detailsDiv.appendChild({{JS_PREFIX}}_app_title_span('ShadowBan'));
detailsDiv.appendChild(document.createElement('hr'));

const recentBlocksText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_recentBlocks', 'Recent Blocks');
const recentBlocksHeader = document.createElement('strong')
recentBlocksHeader.appendChild(document.createTextNode(recentBlocksText));
detailsDiv.appendChild(recentBlocksHeader);

const keywordTally = {{PAGE_PREFIX}}_block_keyword_tally;
let keywordTallyKeys = Object.keys({{PAGE_PREFIX}}_block_keyword_tally);
const authorTally = {{PAGE_PREFIX}}_block_author_tally;
let authorTallyKeys = Object.keys({{PAGE_PREFIX}}_block_author_tally);
if (keywordTallyKeys.length === 0 && authorTallyKeys.length === 0) {
const noRecentBlocksText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noRecentBlocks', '(empty)');
detailsDiv.appendChild(document.createElement('br'));
detailsDiv.appendChild(document.createTextNode(noRecentBlocksText));
} else {
let totalBlocks = 0;
if (keywordTallyKeys.length > 0) {
keywordTallyKeys = keywordTallyKeys.slice();
keywordTallyKeys.sort(function (a, b) {
return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'});
});
const openQuote = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_openQuote', '“');
const closeQuote = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_closeQuote', '”');
keywordTallyKeys.forEach(keyword => {
const count = keywordTally[keyword];
totalBlocks += count;
const entryDiv = document.createElement('div');
const em = document.createElement('em');
em.appendChild(document.createTextNode(openQuote + keyword + closeQuote + ': ' + count));
entryDiv.appendChild(em);
detailsDiv.appendChild(entryDiv);
});
}
if (authorTallyKeys.length > 0) {
authorTallyKeys = authorTallyKeys.slice();
authorTallyKeys.sort(function (a, b) {
return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'});
});
authorTallyKeys.forEach(author => {
const count = authorTally[author];
totalBlocks += count;
const entryDiv = document.createElement('div');
entryDiv.appendChild(document.createTextNode(author + ': ' + count));
detailsDiv.appendChild(entryDiv);
});
}
const summaryDiv = document.createElement('div');
const summaryHeader = document.createElement('strong');
const summaryLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_signalNoiseRatio', 'signal/noise');
const totalAllowed = {{PAGE_PREFIX}}_allow_tally;
const fullTotal = totalAllowed + totalBlocks;
const ratio = 100.0 * (totalAllowed / fullTotal);
summaryHeader.appendChild(document.createTextNode(summaryLabel + ': ' + totalAllowed + '/' + totalBlocks + ' ≈ ' + ratio.toLocaleString('{{ACCOUNT_LOCALE_HYPHEN}}', { maximumSignificantDigits: 3 }) + '%'));
summaryDiv.appendChild(summaryHeader);
detailsDiv.appendChild(summaryDiv);
}
detailsDiv.appendChild(document.createElement('hr'));

let blocks = Object.keys({{JS_PREFIX}}_blocked_users);
if (blocks !== null && blocks.length > 0) {
blocks = blocks.slice(); // copy first, then sort case insensitive using user's locale
blocks.sort(function (a, b) {
return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'});
return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'});
});
}
let keywords = Object.keys({{JS_PREFIX}}_blocked_keywords);
@@ -469,7 +534,7 @@ function {{JS_PREFIX}}_show_app_details() {
detailsDiv.appendChild(document.createElement('hr'));
}
}
const closeButton = {{JS_PREFIX}}_create_button('web_close', 'refresh', function (e) {
const closeButton = {{JS_PREFIX}}_create_button('web_close', 'close', function (e) {
e.stopPropagation();
e.preventDefault();
{{JS_PREFIX}}_hide_app_details();
@@ -499,7 +564,17 @@ function {{JS_PREFIX}}_toggle_app_details(ev) {
onReady: function () {
{{JS_PREFIX}}_load_messages('web_', function (messages) {
{{JS_PREFIX}}_messages = messages;
})
});
document.onkeydown = function(e) {
e = e || window.event;
let isEscape = false;
if ("key" in e) {
isEscape = (e.key === "Escape" || e.key === "Esc");
} else {
isEscape = (e.keyCode === 27);
}
if (isEscape) {{JS_PREFIX}}_hide_app_details();
};
{{JS_PREFIX}}_fetch_blocks();
}
});

+ 6
- 6
bubble-server/src/main/resources/bubble/rule/social/block/site/FB.js.hbs Vedi File

@@ -147,7 +147,7 @@ function {{JS_PREFIX}}_should_block(blocked_users, article) {
if (authorName.endsWith('/')) authorName = authorName.substring(0, authorName.length-1);
if (authorName === 'profile.php') {
// log('should_block returning true for '+authorName);
{{JS_PREFIX}}_tally_block({{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_advertOrOtherBlock', 'ad/other'));
{{JS_PREFIX}}_tally_author_block({{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_advertOrOtherBlock', 'ad/other'));
if (!firstEval) {{JS_PREFIX}}_untally_allow(authorName);
return true;
}
@@ -158,14 +158,14 @@ function {{JS_PREFIX}}_should_block(blocked_users, article) {
const authorDisplayName = authorDisplay.innerHTML;
if ({{JS_PREFIX}}_is_ad(article)) {
// log('removing ad ('+authorDisplayName+')');
{{JS_PREFIX}}_tally_block(authorName == null ? {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_advertOrOtherBlock', 'ad/other') : authorName);
if (!firstEval) {{JS_PREFIX}}_untally_allow(authorName);
{{JS_PREFIX}}_tally_author_block(authorName == null ? {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_advertOrOtherBlock', 'ad/other') : authorName);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
return authorName == null ? true : authorName;

} else if (authorName in blocked_users) {
// log('should_block returning '+authorName);
{{JS_PREFIX}}_tally_block(authorName == null ? {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_advertOrOtherBlock', 'ad/other') : authorName);
if (!firstEval) {{JS_PREFIX}}_untally_allow(authorName);
{{JS_PREFIX}}_tally_author_block(authorName == null ? {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_advertOrOtherBlock', 'ad/other') : authorName);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
return authorName == null ? true : authorName;

} else {
@@ -194,7 +194,7 @@ function {{JS_PREFIX}}_should_block(blocked_users, article) {
}
if (firstEval) {
// console.log('>>> allowing post with firstAuthor = '+firstAuthor);
{{JS_PREFIX}}_tally_allow(firstAuthor);
{{JS_PREFIX}}_tally_allow();
}
return false;
}


+ 2
- 2
bubble-server/src/main/resources/bubble/rule/social/block/site/HackerNews.js.hbs Vedi File

@@ -40,7 +40,7 @@ function {{JS_PREFIX}}_apply_blocks(blocked_users) {
const authorBlocked = author in blocked_users;
if (authorBlocked || {{JS_PREFIX}}_includes_block_keyword(comment, firstEval)) {
if (authorBlocked) {
{{JS_PREFIX}}_tally_block(author);
{{JS_PREFIX}}_tally_author_block(author);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
}
blocking = true;
@@ -54,7 +54,7 @@ function {{JS_PREFIX}}_apply_blocks(blocked_users) {
blockNode.className = "bubble_block";
blockNode.innerHTML = ' [<b><a href="#" onclick="{{JS_PREFIX}}_block_user(\''+author+'\'); return false;">X</a></b>]';
ageElement.parentNode.insertBefore(blockNode, ageElement.nextSibling);
{{JS_PREFIX}}_tally_allow(author);
{{JS_PREFIX}}_tally_allow();
}
}
}


+ 2
- 2
bubble-server/src/main/resources/bubble/rule/social/block/site/MR.js.hbs Vedi File

@@ -22,7 +22,7 @@ function {{JS_PREFIX}}_consider_block(comments, blocked_users) {
if (userElement !== null) {
const author = userElement.innerText;
if (author in blocked_users) {
{{JS_PREFIX}}_tally_block(author);
{{JS_PREFIX}}_tally_author_block(author);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
comment.parentNode.removeChild(comment);
continue;
@@ -34,7 +34,7 @@ function {{JS_PREFIX}}_consider_block(comments, blocked_users) {
blockNode.style['white-space'] = 'nowrap';
blockNode.innerHTML = '[<b><a href="#" onclick="{{JS_PREFIX}}_block_user(\'' + author.replace("'", "\\\'") + '\'); return false;">X</a></b>]&nbsp;&nbsp;';
userElement.parentNode.insertBefore(blockNode, userElement);
{{JS_PREFIX}}_tally_allow(author);
{{JS_PREFIX}}_tally_allow();
}
}
}


+ 2
- 2
bubble-server/src/main/resources/bubble/rule/social/block/site/Reason.js.hbs Vedi File

@@ -23,7 +23,7 @@ function {{JS_PREFIX}}_consider_block(comments, blocked_users) {
if (userElement !== null) {
const author = userElement.innerText;
if (author in blocked_users) {
{{JS_PREFIX}}_tally_block(author);
{{JS_PREFIX}}_tally_author_block(author);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
comment.parentNode.removeChild(comment);
continue;
@@ -39,7 +39,7 @@ function {{JS_PREFIX}}_consider_block(comments, blocked_users) {
if (replies !== null) {
{{JS_PREFIX}}_consider_block(replies.querySelector('li.comment'));
}
{{JS_PREFIX}}_tally_allow(author);
{{JS_PREFIX}}_tally_allow();
}
}
}


+ 2
- 2
bubble-server/src/main/resources/bubble/rule/social/block/site/Twitter.js.hbs Vedi File

@@ -41,7 +41,7 @@ function {{JS_PREFIX}}_apply_blocks(blocked_users) {
const authorBlocked = authorName in blocked_users;
if (authorBlocked || {{JS_PREFIX}}_includes_block_keyword(node)) {
if (authorBlocked) {
{{JS_PREFIX}}_tally_block(authorName);
{{JS_PREFIX}}_tally_author_block(authorName);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
}
console.log('removing post by author: ' + authorName);
@@ -96,7 +96,7 @@ function {{JS_PREFIX}}_apply_blocks(blocked_users) {

authorDiv.setAttribute('white-space', 'no-wrap');
authorDiv.parentNode.appendChild(blockControl);
{{JS_PREFIX}}_tally_allow(authorName);
{{JS_PREFIX}}_tally_allow();
}
}
}


+ 3
- 3
bubble-server/src/main/resources/logback.xml Vedi File

@@ -54,11 +54,11 @@
<!-- <logger name="org.cobbzilla.util.io.multi.MultiStream" level="TRACE" />-->
<!-- <logger name="bubble.filters.BubbleRateLimitFilter" level="TRACE" />-->
<!-- <logger name="org.cobbzilla.wizard.filters.RateLimitFilter" level="TRACE" />-->
<!-- <logger name="bubble.service.stream.StandardRuleEngineService" level="DEBUG" />-->
<logger name="bubble.service.stream.StandardRuleEngineService" level="DEBUG" />
<logger name="bubble.service.stream.ActiveStreamState" level="WARN" />
<logger name="bubble.resources.stream" level="WARN" />
<!-- <logger name="bubble.resources.stream.FilterHttpResource" level="DEBUG" />-->
<logger name="bubble.resources.stream.FilterHttpResource" level="WARN" />
<logger name="bubble.resources.stream.FilterHttpResource" level="DEBUG" />
<!-- <logger name="bubble.resources.stream.FilterHttpResource" level="WARN" />-->
<logger name="bubble.service.stream" level="INFO" />
<!-- <logger name="bubble.service.dbfilter" level="DEBUG" />-->
<!-- <logger name="bubble.service.account.StandardAccountMessageService" level="DEBUG" />-->


+ 29
- 2
bubble-server/src/main/resources/models/apps/bubble_block/bubbleApp_bubbleBlock.json Vedi File

@@ -38,7 +38,8 @@
{"name": "testUserAgent", "required": false},
{"name": "testUrlPrimary", "type": "flag"},
{"name": "urlRegex", "required": false},
{"name": "userAgentRegex"}
{"name": "userAgentRegex"},
{"name": "fqdn", "type": "fqdn"}
],
"configViews": [{
"name": "manageLists",
@@ -111,6 +112,20 @@
"successMessage": "decisionType"
}
]
}, {
"name": "manageHideStats",
"scope": "app",
"root": "true",
"fields": ["fqdn"],
"when": "configs.showBlockStatsSupported",
"actions": [
{"name": "removeHideStats", "index": 10},
{
"name": "createHideStats", "scope": "app", "index": 10,
"params": ["fqdn"],
"button": "createHideStats"
}
]
}]
},
"children": {
@@ -196,6 +211,7 @@
{"name": "config.view.manageList", "value": "Manage Filter List"},
{"name": "config.view.manageRules", "value": "Manage Filter Rules"},
{"name": "config.view.manageUserAgents", "value": "Manage User-Agents"},
{"name": "config.view.manageHideStats", "value": "Disable In-Page Block Stats"},

{"name": "config.field.name", "value": "Name"},
{"name": "config.field.description", "value": "Description"},
@@ -218,6 +234,8 @@
{"name": "config.field.urlRegex.description", "value": "A regular expression to match against the URL"},
{"name": "config.field.userAgentRegex", "value": "User-Agent"},
{"name": "config.field.userAgentRegex.description", "value": "A regular expression to match against the User-Agent"},
{"name": "config.field.fqdn", "value": "Domain Name"},
{"name": "config.field.fqdn.description", "value": "In-page block stats will be disabled for this domain"},

{"name": "config.action.enableList", "value": "Enable"},
{"name": "config.action.disableList", "value": "Disable"},
@@ -235,6 +253,10 @@
{"name": "config.button.createUserAgentBlock", "value": "Add"},
{"name": "config.action.removeUserAgentBlock", "value": "Remove Rule"},
{"name": "config.button.removeUserAgentBlock", "value": "Remove"},
{"name": "config.action.createHideStats", "value": "Disable Block Stats"},
{"name": "config.button.createHideStats", "value": "Add"},
{"name": "config.action.removeHideStats", "value": "Remove"},
{"name": "config.button.removeHideStats", "value": "Remove"},
{"name": "config.action.testUrl", "value": "Test URL"},
{"name": "config.button.testUrl", "value": "Test"},
{"name": "config.response.block", "value": "Block"},
@@ -243,7 +265,12 @@
{"name": "config.response.allow.description", "value": "Requests to this URL would be allowed by your Bubble, and would not be filtered"},
{"name": "config.response.filter", "value": "Filter"},
{"name": "config.response.filter.description", "value": "Requests to this URL would be allowed by your Bubble, but would be filtered"},
{"name": "web_signalNoiseRatio", "value": "signal/noise"}

{"name": "web_appName", "value": "BlockParty!"},
{"name": "web_adsAndTrackers", "value": "Blocked Ads/Trackers"},
{"name": "web_hideStats", "value": "Hide BlockParty for "},
{"name": "web_noAdsOrTrackers", "value": "(no blocks)"},
{"name": "web_close", "value": "close"}
]
}]
}

+ 6
- 0
bubble-server/src/main/resources/models/apps/user_block/bubbleApp_userBlock.json Vedi File

@@ -39,12 +39,18 @@
{"name": "action.disable", "value": "Disable Block"},
{"name": "action.delete", "value": "Delete Block"},

{"name": "web_appName", "value": "ShadowBan"},
{"name": "web_blockedUsers", "value": "Users"},
{"name": "web_noUsersBlocked", "value": "(empty)"},
{"name": "web_blockedKeywords", "value": "Keywords"},
{"name": "web_noKeywordsBlocked", "value": "(empty)"},
{"name": "web_addKeyword", "value": "add"},
{"name": "web_advertOrOtherBlock", "value": "advert/other"},
{"name": "web_recentBlocks", "value": "Recent Blocks"},
{"name": "web_noRecentBlocks", "value": "(empty)"},
{"name": "web_openQuote", "value": "“"},
{"name": "web_closeQuote", "value": "”"},
{"name": "web_signalNoiseRatio", "value": "signal/noise"},
{"name": "web_close", "value": "close"},
{"name": "web_blockedKeywordLists", "value": "Keyword Lists"},
{"name": "web_kwlist_us_news", "value": "US News Media"},


+ 7
- 2
bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_conn_check.py Vedi File

@@ -69,7 +69,12 @@ def get_device_security_level(client_addr, fqdns):
return {'level': level}


def show_block_stats(client_addr):
def show_block_stats(client_addr, fqdns):
if fqdns is not None:
for fqdn in fqdns:
show = REDIS.get(REDIS_KEY_DEVICE_SHOW_BLOCK_STATS+client_addr+':'+fqdn)
if show is not None:
return show.decode() == 'true'
show = REDIS.get(REDIS_KEY_DEVICE_SHOW_BLOCK_STATS+client_addr)
if show is None:
return False
@@ -259,7 +264,7 @@ def next_layer(next_layer):
elif 'block' in check and check['block']:
bubble_log('next_layer: enabling block for server=' + server_addr+', fqdns='+str(fqdns))
bubble_activity_log(client_addr, server_addr, 'conn_block', fqdns)
if show_block_stats(client_addr) and security_level['level'] != SEC_BASIC:
if show_block_stats(client_addr, fqdns) and security_level['level'] != SEC_BASIC:
next_layer.do_block = True
next_layer.__class__ = TlsFeedback
else:


+ 2
- 0
bubble-server/src/main/resources/packer/roles/mitmproxy/files/dns_spoofing.py Vedi File

@@ -82,6 +82,8 @@ class Rerouter:
port = 80
is_http = True

# check if https and sni is missing but we have a host header, fill in the sni
host_header = flow.request.host_header
# bubble_log("dns_spoofing.request: host_header is "+repr(host_header))
if host_header:


+ 1
- 1
bubble-server/src/main/resources/packer/roles/mitmproxy/tasks/main.yml Vedi File

@@ -41,7 +41,7 @@
get_url:
url: https://github.com/getbubblenow/bubble-dist/raw/master/mitmproxy/mitmproxy.zip
dest: /tmp/mitmproxy.zip
checksum: sha256:573bf83388a1634ffcd710121b9185b54b4dc62e9553d3bc654cc940783bda9d
checksum: sha256:c5817949c5159ad07f6434032df1249c8f4f0e74eeee6cef4e5d7d3676cdc49e

- name: Unzip mitmproxy.zip
unarchive:


+ 1
- 1
bubble-web

@@ -1 +1 @@
Subproject commit 52bbba5ccbf22f5adf97c6e3aa7b2cb4d131885c
Subproject commit a4944233bf2bd32c76aceb8efb4f5574cc4eee4f

Caricamento…
Annulla
Salva