瀏覽代碼

Add full support for response header modification

pull/58/head
Kristijan Mitrovic 4 年之前
父節點
當前提交
34a48b3745
共有 9 個文件被更改,包括 205 次插入143 次删除
  1. +23
    -23
      bubble-server/src/main/java/bubble/app/request/RequestProtectorAppConfigDriver.java
  2. +14
    -2
      bubble-server/src/main/java/bubble/rule/AppRuleDriver.java
  3. +6
    -5
      bubble-server/src/main/java/bubble/rule/request/HeaderReplacement.java
  4. +6
    -9
      bubble-server/src/main/java/bubble/rule/request/RequestProtectorConfig.java
  5. +9
    -25
      bubble-server/src/main/java/bubble/rule/request/RequestProtectorRuleDriver.java
  6. +114
    -67
      bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java
  7. +11
    -11
      bubble-server/src/main/resources/models/apps/request/bubbleApp_request.json
  8. +20
    -0
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_api.py
  9. +2
    -1
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py

+ 23
- 23
bubble-server/src/main/java/bubble/app/request/RequestProtectorAppConfigDriver.java 查看文件

@@ -8,7 +8,7 @@ import bubble.model.account.Account;
import bubble.model.app.AppRule;
import bubble.model.app.BubbleApp;
import bubble.model.app.config.AppConfigDriverBase;
import bubble.rule.request.CookieReplacement;
import bubble.rule.request.HeaderReplacement;
import bubble.rule.request.RequestProtectorConfig;
import bubble.rule.request.RequestProtectorRuleDriver;
import com.fasterxml.jackson.databind.JsonNode;
@@ -25,27 +25,27 @@ import static org.cobbzilla.wizard.resources.ResourceUtil.notFoundEx;
@Slf4j
public class RequestProtectorAppConfigDriver extends AppConfigDriverBase {

public static final String VIEW_manageCookieReplacements = "manageCookieReplacements";
public static final String VIEW_manageHeaderReplacements = "manageHeaderReplacements";

@Override public Object getView(Account account, BubbleApp app, String view, Map<String, String> params) {
switch (view) {
case VIEW_manageCookieReplacements:
case VIEW_manageHeaderReplacements:
return loadManageCookiesReplacements(account, app);
}
throw notFoundEx(view);
}

private Set<CookieReplacement> loadManageCookiesReplacements(Account account, BubbleApp app) {
private Set<HeaderReplacement> loadManageCookiesReplacements(Account account, BubbleApp app) {
final RequestProtectorConfig config = getConfig(account, app);
return config.getCookieReplacements();
return config.getHeaderReplacements();
}

private RequestProtectorConfig getConfig(Account account, BubbleApp app) {
return getConfig(account, app, RequestProtectorRuleDriver.class, RequestProtectorConfig.class);
}

public static final String ACTION_addCookieReplacement = "addCookieReplacement";
public static final String ACTION_removeCookieReplacement = "removeCookieReplacement";
public static final String ACTION_addHeaderReplacement = "addHeaderReplacement";
public static final String ACTION_removeHeaderReplacement = "removeHeaderReplacement";

public static final String PARAM_REGEX = "regex";
public static final String PARAM_REPLACEMENT = "replacement";
@@ -53,17 +53,17 @@ public class RequestProtectorAppConfigDriver extends AppConfigDriverBase {
@Override public Object takeAppAction(Account account, BubbleApp app, String view, String action,
Map<String, String> params, JsonNode data) {
switch (action) {
case ACTION_addCookieReplacement:
return addCookieReplacement(account, app, data);
case ACTION_addHeaderReplacement:
return addHeaderReplacement(account, app, data);
}
if (log.isWarnEnabled()) log.warn("takeAppAction: action not found: "+action);
throw notFoundEx(action);
}

private Set<CookieReplacement> addCookieReplacement(Account account, BubbleApp app, JsonNode data) {
private Set<HeaderReplacement> addHeaderReplacement(Account account, BubbleApp app, JsonNode data) {
final JsonNode regexNode = data.get(PARAM_REGEX);
if (regexNode == null || regexNode.textValue() == null || empty(regexNode.textValue().trim())) {
throw invalidEx("err.requestProtector.cookieRegexRequired");
throw invalidEx("err.requestProtector.headerRegexRequired");
}
final String regex = regexNode.textValue().trim().toLowerCase();

@@ -72,44 +72,44 @@ public class RequestProtectorAppConfigDriver extends AppConfigDriverBase {
? ""
: replacementNode.textValue().trim().toLowerCase();

final RequestProtectorConfig config = getConfig(account, app).addCookieReplacement(regex, replacement);
final RequestProtectorConfig config = getConfig(account, app).addHeaderReplacement(regex, replacement);

final AppRule rule = loadRule(account, app);
loadDriver(account, rule, RequestProtectorRuleDriver.class); // validate proper driver
if (log.isDebugEnabled()) {
log.debug("addCookieReplacement: updating rule: " + rule.getName() + ", adding regex: " + regex);
log.debug("addHeaderReplacement: updating rule: " + rule.getName() + ", adding regex: " + regex);
}
ruleDAO.update(rule.setConfigJson(json(config)));

return config.getCookieReplacements();
return config.getHeaderReplacements();
}

@Override public Object takeItemAction(Account account, BubbleApp app, String view, String action, String id,
Map<String, String> params, JsonNode data) {
switch (action) {
case ACTION_removeCookieReplacement:
return removeCookieReplacement(account, app, id);
case ACTION_removeHeaderReplacement:
return removeHeaderReplacement(account, app, id);
}
if (log.isWarnEnabled()) log.warn("takeItemAction: action not found: "+action);
throw notFoundEx(action);
}

private Set<CookieReplacement> removeCookieReplacement(Account account, BubbleApp app, String regex) {
private Set<HeaderReplacement> removeHeaderReplacement(Account account, BubbleApp app, String regex) {
final AppRule rule = loadRule(account, app);
loadDriver(account, rule, RequestProtectorRuleDriver.class); // validate proper driver
final RequestProtectorConfig config = getConfig(account, app);
if (log.isDebugEnabled()) {
log.debug("removeCookieReplacement: removing regex: " + regex + " from config.cookiesReplacements: "
+ config.getCookieReplacements().toString());
log.debug("removeHeaderReplacement: removing regex: " + regex + " from config.cookiesReplacements: "
+ config.getHeaderReplacements().toString());
}

final RequestProtectorConfig updated = config.removeCookieReplacement(regex);
final RequestProtectorConfig updated = config.removeHeaderReplacement(regex);
if (log.isDebugEnabled()) {
log.debug("removeCookieReplacement: updated.cookiesReplacements: "
+ updated.getCookieReplacements().toString());
log.debug("removeHeaderReplacement: updated.cookiesReplacements: "
+ updated.getHeaderReplacements().toString());
}
ruleDAO.update(rule.setConfigJson(json(updated)));

return updated.getCookieReplacements();
return updated.getHeaderReplacements();
}
}

+ 14
- 2
bubble-server/src/main/java/bubble/rule/AppRuleDriver.java 查看文件

@@ -46,6 +46,7 @@ public interface AppRuleDriver {
String REDIS_FILTER_LISTS = "filterLists";
String REDIS_FLEX_LISTS = "flexLists"; // used in mitmproxy and dnscrypt-proxy for flex routing
String REDIS_FLEX_EXCLUDE_LISTS = "flexExcludeLists"; // used in mitmproxy and dnscrypt-proxy for flex routing
String REDIS_RESPONSE_HEADER_MODIFIER_LISTS = "responseHeaderModifierLists"; // used in mitmproxy
String REDIS_LIST_SUFFIX = "~UNION";

default Set<String> getPrimedRejectDomains () { return null; }
@@ -54,6 +55,7 @@ public interface AppRuleDriver {
default Set<String> getPrimedFilterDomains () { return null; }
default Set<String> getPrimedFlexDomains () { return null; }
default Set<String> getPrimedFlexExcludeDomains () { return null; }
default Set<String> getPrimedResponseHeaderModifiers () { return null; }

static void defineRedisRejectSet(RedisService redis, String ip, String list, String[] rejectDomains) {
defineRedisSet(redis, ip, REDIS_REJECT_LISTS, list, rejectDomains);
@@ -79,12 +81,22 @@ public interface AppRuleDriver {
defineRedisSet(redis, ip, REDIS_FLEX_EXCLUDE_LISTS, list, flexExcludeDomains);
}

static void defineRedisSet(RedisService redis, String ip, String listOfListsName, String listName, String[] domains) {
static void defineRedisResponseHeaderModifiersSet(RedisService redis, String ip, String list,
String[] modifiers) {
defineRedisSet(redis, ip, REDIS_RESPONSE_HEADER_MODIFIER_LISTS, list, modifiers);
}

/**
* `settings` parameter may be list of domains or any other list of strings - i.e. list of JSONs with specific setup
* for the prime option of the driver.
*/
static void defineRedisSet(RedisService redis, String ip, String listOfListsName, String listName,
String[] settings) {
final String listOfListsForIp = listOfListsName + "~" + ip;
final String unionSetName = listOfListsForIp + REDIS_LIST_SUFFIX;
final String ipList = listOfListsForIp + "~" + listName;
final String tempList = ipList + "~"+now()+randomAlphanumeric(5);
redis.sadd_plaintext(tempList, domains);
redis.sadd_plaintext(tempList, settings);
redis.rename(tempList, ipList);
redis.sadd_plaintext(listOfListsForIp, ipList);
final Long count = redis.sunionstore(unionSetName, redis.smembers(listOfListsForIp));


bubble-server/src/main/java/bubble/rule/request/CookieReplacement.java → bubble-server/src/main/java/bubble/rule/request/HeaderReplacement.java 查看文件

@@ -4,6 +4,7 @@
*/
package bubble.rule.request;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
@@ -11,20 +12,20 @@ import lombok.Setter;
import lombok.experimental.Accessors;

@NoArgsConstructor @Accessors(chain=true)
public class CookieReplacement implements Comparable<CookieReplacement> {
public class HeaderReplacement implements Comparable<HeaderReplacement> {

public String getId() { return regex; }
public void setId(String id) {} // noop
@JsonIgnore public String getId() { return regex; }
@JsonIgnore public void setId(String id) {} // noop

@Getter @Setter private String regex;
@Getter @Setter private String replacement;

public CookieReplacement(@NonNull final String regex, @NonNull final String replacement) {
public HeaderReplacement(@NonNull final String regex, @NonNull final String replacement) {
this.regex = regex;
this.replacement = replacement;
}

@Override public int compareTo(@NonNull final CookieReplacement o) {
@Override public int compareTo(@NonNull final HeaderReplacement o) {
return getRegex().compareTo(o.getRegex().toLowerCase());
}
}

+ 6
- 9
bubble-server/src/main/java/bubble/rule/request/RequestProtectorConfig.java 查看文件

@@ -18,20 +18,17 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
@Slf4j @Accessors(chain=true)
public class RequestProtectorConfig {

@Getter @Setter private Set<CookieReplacement> cookieReplacements = new TreeSet<>();
public boolean hasCookieReplacements() { return !empty(cookieReplacements); }
public boolean hasCookieReplacementFor(@NonNull final String regex) {
return hasCookieReplacements() && cookieReplacements.stream().anyMatch(r -> r.getRegex().equals(regex));
}
@Getter @Setter private Set<HeaderReplacement> headerReplacements = new TreeSet<>();
public boolean hasHeaderReplacements() { return !empty(headerReplacements); }

@NonNull public RequestProtectorConfig addCookieReplacement(@NonNull final String regex,
@NonNull public RequestProtectorConfig addHeaderReplacement(@NonNull final String regex,
@NonNull final String replacement) {
cookieReplacements.add(new CookieReplacement(regex, replacement));
headerReplacements.add(new HeaderReplacement(regex, replacement));
return this;
}

@NonNull public RequestProtectorConfig removeCookieReplacement(@NonNull final String regex) {
if (hasCookieReplacements()) cookieReplacements.removeIf(r -> r.getRegex().equals(regex));
@NonNull public RequestProtectorConfig removeHeaderReplacement(@NonNull final String regex) {
if (hasHeaderReplacements()) headerReplacements.removeIf(r -> r.getRegex().equals(regex));
return this;
}
}

+ 9
- 25
bubble-server/src/main/java/bubble/rule/request/RequestProtectorRuleDriver.java 查看文件

@@ -9,46 +9,30 @@ import bubble.model.app.AppMatcher;
import bubble.model.app.AppRule;
import bubble.model.app.BubbleApp;
import bubble.model.device.Device;
import bubble.resources.stream.FilterHttpRequest;
import bubble.rule.AbstractAppRuleDriver;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.input.ReaderInputStream;
import org.cobbzilla.util.io.regex.RegexFilterReader;
import org.cobbzilla.util.json.JsonUtil;

import java.io.InputStream;
import java.util.Iterator;

import static org.cobbzilla.util.string.StringUtil.UTF8cs;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class RequestProtectorRuleDriver extends AbstractAppRuleDriver {

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

@Override public Set<String> getPrimedResponseHeaderModifiers() {
final RequestProtectorConfig config = getRuleConfig();
return config.getHeaderReplacements().stream().map(JsonUtil::json).collect(Collectors.toSet());
}

@Override public void init(JsonNode config, JsonNode userConfig, BubbleApp app, AppRule rule, AppMatcher matcher,
Account account, Device device) {
super.init(config, userConfig, app, rule, matcher, account, device);

// refresh list
final RequestProtectorConfig ruleConfig = getRuleConfig();
ruleConfig.getCookieReplacements();
}

@Override public InputStream doFilterResponse(FilterHttpRequest filterRequest, InputStream in) {
final RequestProtectorConfig config = getRuleConfig();
if (!config.hasCookieReplacements()) return in;

final Iterator<CookieReplacement> crIterator = config.getCookieReplacements().iterator();
CookieReplacement cr = crIterator.next();
RegexFilterReader reader = new RegexFilterReader(in, new HttpHeaderReplacementFilter(cr.getRegex(),
cr.getReplacement()));
while (crIterator.hasNext()) {
cr = crIterator.next();
reader = new RegexFilterReader(reader, new HttpHeaderReplacementFilter(cr.getRegex(),
cr.getReplacement()));
}

return new ReaderInputStream(reader, UTF8cs);
ruleConfig.getHeaderReplacements();
}
}

+ 114
- 67
bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java 查看文件

@@ -142,73 +142,7 @@ public class StandardAppPrimerService implements AppPrimerService {
dataDAO.registerCallback(app.getUuid(), dataCallback.createCallback(account, app, configuration));
}
for (Device device : devices) {
final Set<String> rejectDomains = new HashSet<>();
final Set<String> blockDomains = new HashSet<>();
final Set<String> whiteListDomains = new HashSet<>();
final Set<String> filterDomains = new HashSet<>();
final Set<String> flexDomains = new HashSet<>();
final Set<String> flexExcludeDomains = new HashSet<>();
for (AppMatcher matcher : matchers) {
final AppRuleDriver appRuleDriver = rule.initDriver(app, driver, matcher, account, device);
final Set<String> rejects = appRuleDriver.getPrimedRejectDomains();
if (empty(rejects)) {
log.debug("_prime: no rejectDomains for device/app/rule/matcher: " + device.getName() + "/" + app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
rejectDomains.addAll(rejects);
}
final Set<String> blocks = appRuleDriver.getPrimedBlockDomains();
if (empty(blocks)) {
log.debug("_prime: no blockDomains for device/app/rule/matcher: " + device.getName() + "/" + app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
blockDomains.addAll(blocks);
}
final Set<String> whiteList = appRuleDriver.getPrimedWhiteListDomains();
if (empty(whiteList)) {
log.debug("_prime: no whiteListDomains for device/app/rule/matcher: " + device.getName() + "/" + app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
whiteListDomains.addAll(whiteList);
}
final Set<String> filters = appRuleDriver.getPrimedFilterDomains();
if (empty(filters)) {
log.debug("_prime: no filterDomains for device/app/rule/matcher: " + device.getName() + "/" + app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
filterDomains.addAll(filters);
}
final Set<String> flexes = appRuleDriver.getPrimedFlexDomains();
if (empty(flexes)) {
log.debug("_prime: no flexDomains for device/app/rule/matcher: " + device.getName() + "/" + app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
flexDomains.addAll(flexes);
}
final Set<String> flexExcludes = appRuleDriver.getPrimedFlexExcludeDomains();
if (empty(flexExcludes)) {
log.debug("_prime: no flexExcludeDomains for device/app/rule/matcher: " + device.getName() + "/" + app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
flexExcludeDomains.addAll(flexExcludes);
}
}
if (!empty(rejectDomains) || !empty(blockDomains) || !empty(filterDomains) || !empty(flexDomains) || !empty(flexExcludeDomains)) {
for (String ip : accountDeviceIps.get(device.getUuid())) {
if (!empty(rejectDomains)) {
AppRuleDriver.defineRedisRejectSet(redis, ip, app.getName() + ":" + app.getUuid(), rejectDomains.toArray(String[]::new));
}
if (!empty(blockDomains)) {
AppRuleDriver.defineRedisBlockSet(redis, ip, app.getName() + ":" + app.getUuid(), blockDomains.toArray(String[]::new));
}
if (!empty(whiteListDomains)) {
AppRuleDriver.defineRedisWhiteListSet(redis, ip, app.getName() + ":" + app.getUuid(), whiteListDomains.toArray(String[]::new));
}
if (!empty(filterDomains)) {
AppRuleDriver.defineRedisFilterSet(redis, ip, app.getName() + ":" + app.getUuid(), filterDomains.toArray(String[]::new));
}
if (!empty(flexDomains)) {
AppRuleDriver.defineRedisFlexSet(redis, ip, app.getName() + ":" + app.getUuid(), flexDomains.toArray(String[]::new));
}
if (!empty(flexExcludeDomains)) {
AppRuleDriver.defineRedisFlexExcludeSet(redis, ip, app.getName() + ":" + app.getUuid(), flexExcludeDomains.toArray(String[]::new));
}
}
}
defineRedisSets(account, accountDeviceIps, app, matchers, rule, driver, device);
}
}
}
@@ -219,4 +153,117 @@ public class StandardAppPrimerService implements AppPrimerService {
}
}

private void defineRedisSets(Account account, Map<String, List<String>> accountDeviceIps, BubbleApp app,
List<AppMatcher> matchers, AppRule rule, RuleDriver driver, Device device) {
final Set<String> rejectDomains = new HashSet<>();
final Set<String> blockDomains = new HashSet<>();
final Set<String> whiteListDomains = new HashSet<>();
final Set<String> filterDomains = new HashSet<>();
final Set<String> flexDomains = new HashSet<>();
final Set<String> flexExcludeDomains = new HashSet<>();
final Set<String> requestHeaderModifiers = new HashSet<>();

boolean areAllSetsEmpty = true;
for (AppMatcher matcher : matchers) {
final AppRuleDriver appRuleDriver = rule.initDriver(app, driver, matcher, account, device);

final Set<String> rejects = appRuleDriver.getPrimedRejectDomains();
if (empty(rejects)) {
log.debug("_prime: no rejectDomains for device/app/rule/matcher: " + device.getName()
+ "/" + app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
rejectDomains.addAll(rejects);
areAllSetsEmpty = false;
}

final Set<String> blocks = appRuleDriver.getPrimedBlockDomains();
if (empty(blocks)) {
log.debug("_prime: no blockDomains for device/app/rule/matcher: " + device.getName() + "/"
+ app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
blockDomains.addAll(blocks);
areAllSetsEmpty = false;
}

final Set<String> whiteList = appRuleDriver.getPrimedWhiteListDomains();
if (empty(whiteList)) {
log.debug("_prime: no whiteListDomains for device/app/rule/matcher: " + device.getName() + "/"
+ app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
whiteListDomains.addAll(whiteList);
areAllSetsEmpty = false;
}

final Set<String> filters = appRuleDriver.getPrimedFilterDomains();
if (empty(filters)) {
log.debug("_prime: no filterDomains for device/app/rule/matcher: " + device.getName() + "/"
+ app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
filterDomains.addAll(filters);
areAllSetsEmpty = false;
}

final Set<String> flexes = appRuleDriver.getPrimedFlexDomains();
if (empty(flexes)) {
log.debug("_prime: no flexDomains for device/app/rule/matcher: " + device.getName() + "/"
+ app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
flexDomains.addAll(flexes);
areAllSetsEmpty = false;
}

final Set<String> flexExcludes = appRuleDriver.getPrimedFlexExcludeDomains();
if (empty(flexExcludes)) {
log.debug("_prime: no flexExcludeDomains for device/app/rule/matcher: " + device.getName() + "/"
+ app.getName() + "/" + rule.getName() + "/" + matcher.getName());
} else {
flexExcludeDomains.addAll(flexExcludes);
areAllSetsEmpty = false;
}

final Set<String> modifiers = appRuleDriver.getPrimedResponseHeaderModifiers();
if (empty(modifiers)) {
log.debug("_prime: no responseHeaderModifiers for device/app/rule/matcher: "
+ device.getName() + "/" + app.getName() + "/" + rule.getName() + "/"
+ matcher.getName());
} else {
requestHeaderModifiers.addAll(modifiers);
areAllSetsEmpty = false;
}
}

if (areAllSetsEmpty) return;

for (String ip : accountDeviceIps.get(device.getUuid())) {
if (!empty(rejectDomains)) {
AppRuleDriver.defineRedisRejectSet(redis, ip, app.getName() + ":" + app.getUuid(),
rejectDomains.toArray(String[]::new));
}
if (!empty(blockDomains)) {
AppRuleDriver.defineRedisBlockSet(redis, ip, app.getName() + ":" + app.getUuid(),
blockDomains.toArray(String[]::new));
}
if (!empty(whiteListDomains)) {
AppRuleDriver.defineRedisWhiteListSet(redis, ip, app.getName() + ":" + app.getUuid(),
whiteListDomains.toArray(String[]::new));
}
if (!empty(filterDomains)) {
AppRuleDriver.defineRedisFilterSet(redis, ip, app.getName() + ":" + app.getUuid(),
filterDomains.toArray(String[]::new));
}
if (!empty(flexDomains)) {
AppRuleDriver.defineRedisFlexSet(redis, ip, app.getName() + ":" + app.getUuid(),
flexDomains.toArray(String[]::new));
}
if (!empty(flexExcludeDomains)) {
AppRuleDriver.defineRedisFlexExcludeSet(redis, ip, app.getName() + ":" + app.getUuid(),
flexExcludeDomains.toArray(String[]::new));
}
if (!empty(requestHeaderModifiers)) {
AppRuleDriver.defineRedisResponseHeaderModifiersSet(redis, ip, app.getName() + ":" + app.getUuid(),
requestHeaderModifiers.toArray(String[]::new));
}
}
}

}

+ 11
- 11
bubble-server/src/main/resources/models/apps/request/bubbleApp_request.json 查看文件

@@ -15,16 +15,16 @@
{"name": "replacement", "truncate": false}
],
"configViews": [{
"name": "manageCookieReplacements",
"name": "manageHeaderReplacements",
"scope": "app",
"root": "true",
"fields": [ "regex", "replacement" ],
"actions": [
{"name": "removeCookieReplacement", "index": 10},
{"name": "removeHeaderReplacement", "index": 10},
{
"name": "addCookieReplacement", "scope": "app", "index": 10,
"name": "addHeaderReplacement", "scope": "app", "index": 10,
"params": [ "regex", "replacement" ],
"button": "addCookieReplacement"
"button": "addHeaderReplacement"
}
]
}]
@@ -41,7 +41,7 @@
"template": true,
"driver": "RequestProtectorRuleDriver",
"priority": -1000,
"config": { "cookieReplacements": [] }
"config": { "headerReplacements": [] }
}],
"AppMessage": [{
"locale": "en_US",
@@ -54,21 +54,21 @@
"value": "Change or remove parts of request/response - i.e. remove cross-domain cookies from response"
},

{ "name": "config.view.manageCookieReplacements", "value": "Manage Cookie Replacements" },
{ "name": "config.view.manageHeaderReplacements", "value": "Manage Header Replacements" },
{ "name": "config.field.regex", "value": "RegEx" },
{
"name": "config.field.regex.description",
"value": "Regular expression compared with full set cookie string value"
"value": "Regular expression compared with full header's line string value"
},
{ "name": "config.field.replacement", "value": "Replacement" },
{
"name": "config.field.replacement.description",
"value": "May use reference from regex as in Java's replaceAll method"
"value": "May use reference from regex as in python's re.Pattern.sub method. If set to empty string, found header will be fully removed from response"
},
{ "name": "config.action.addCookieReplacement", "value": "Add" },
{ "name": "config.action.removeCookieReplacement", "value": "Remove" },
{ "name": "config.action.addHeaderReplacement", "value": "Add" },
{ "name": "config.action.removeHeaderReplacement", "value": "Remove" },

{ "name": "err.requestProtector.cookieRegexRequired", "value": "RegEx field is required" }
{ "name": "err.requestProtector.headerRegexRequired", "value": "RegEx field is required" }
]
}]
}

+ 20
- 0
bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_api.py 查看文件

@@ -450,6 +450,26 @@ def original_flex_ip(client_addr, fqdns):
return None


def response_header_modify(flow):
return None if flow.response is None else _header_modify(flow.client_conn.address[0], flow.response.headers)


def _header_modify(client_addr, headers):
modifiers_set = 'responseHeaderModifierLists~' + client_addr + '~UNION'
modifiers = REDIS.smembers(modifiers_set)

repl_count = 0
if modifiers:
for modifier in modifiers:
modifier_config = json.loads(modifier)
repl_count += headers.replace(modifier_config['regex'], modifier_config['replacement'])

if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('_header_modify: replacing headers - replacements count: ' + repl_count)

return repl_count


def health_check_response(flow):
# if bubble_log.isEnabledFor(DEBUG):
# bubble_log.debug('health_check_response: special bubble health check request, responding with OK')


+ 2
- 1
bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py 查看文件

@@ -16,7 +16,7 @@ from bubble_api import CTX_BUBBLE_MATCHERS, CTX_BUBBLE_ABORT, CTX_BUBBLE_LOCATIO
is_bubble_special_path, is_bubble_health_check, health_check_response, special_bubble_response, \
CTX_BUBBLE_REQUEST_ID, CTX_CONTENT_LENGTH, CTX_CONTENT_LENGTH_SENT, CTX_BUBBLE_FILTERED, \
HEADER_CONTENT_TYPE, HEADER_CONTENT_ENCODING, HEADER_LOCATION, HEADER_CONTENT_LENGTH, \
HEADER_USER_AGENT, HEADER_FILTER_PASSTHRU, HEADER_CONTENT_SECURITY_POLICY, REDIS, redis_set
HEADER_USER_AGENT, HEADER_FILTER_PASSTHRU, HEADER_CONTENT_SECURITY_POLICY, REDIS, redis_set, response_header_modify
from bubble_flex import process_flex

import logging
@@ -285,6 +285,7 @@ def responseheaders(flow):
else:
flex_flow = None
bubble_filter_response(flow, flex_flow)
response_header_modify(flow)


def bubble_filter_response(flow, flex_flow):


Loading…
取消
儲存