From b5c49e8a3f49a81c31776dc5ad9bc4c441e3fefb Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Mon, 14 Sep 2020 08:19:15 -0400 Subject: [PATCH] WIP. adding shadowban support for linkedin --- .../java/bubble/dao/app/AppMatcherDAO.java | 8 ++ .../resources/stream/FilterHttpRequest.java | 2 +- .../stream/StandardAppPrimerService.java | 2 + .../META-INF/bubble/bubble.properties | 2 +- .../bubble/rule/social/block/site/LI.js.hbs | 97 +++++++++++++++++++ .../user_block/li/bubbleApp_userBlock_li.json | 23 +++++ .../li/bubbleApp_userBlock_li_data.json | 12 +++ .../li/bubbleApp_userBlock_li_matchers.json | 15 +++ .../models/manifest-app-user-block.json | 2 + .../roles/mitmproxy/files/bubble_modify.py | 5 +- 10 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 bubble-server/src/main/resources/bubble/rule/social/block/site/LI.js.hbs create mode 100644 bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li.json create mode 100644 bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_data.json create mode 100644 bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_matchers.json diff --git a/bubble-server/src/main/java/bubble/dao/app/AppMatcherDAO.java b/bubble-server/src/main/java/bubble/dao/app/AppMatcherDAO.java index 49b7f969..bdc2027d 100644 --- a/bubble-server/src/main/java/bubble/dao/app/AppMatcherDAO.java +++ b/bubble-server/src/main/java/bubble/dao/app/AppMatcherDAO.java @@ -67,6 +67,14 @@ public class AppMatcherDAO extends AppTemplateEntityDAO { return super.preCreate(matcher); } + @Override public AppMatcher postCreate(AppMatcher matcher, Object context) { + final BubbleApp app = appDAO.findByUuid(matcher.getApp()); + if (app == null) return die("postCreate("+ matcher.getUuid()+"): app not found: "+ matcher.getApp()); + ruleEngineService.flushCaches(); + primerService.prime(app); + return super.postCreate(matcher, context); + } + @Override public AppMatcher postUpdate(AppMatcher matcher, Object context) { final BubbleApp app = appDAO.findByUuid(matcher.getApp()); if (app == null) return die("postUpdate("+ matcher.getUuid()+"): app not found: "+ matcher.getApp()); diff --git a/bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java b/bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java index 13aee225..62869254 100644 --- a/bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java +++ b/bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java @@ -43,7 +43,7 @@ public class FilterHttpRequest { @Getter @Setter private String contentSecurityPolicy; public boolean hasContentSecurityPolicy () { return !empty(contentSecurityPolicy); } - public static final Pattern NONCE_PATTERN = Pattern.compile("\\s+script-src\\s+.*'nonce-([^']+)'"); + public static final Pattern NONCE_PATTERN = Pattern.compile(";\\s*script-src\\s+.*'nonce-([^']+)'"); @Getter(lazy=true) private final String scriptNonce = initScriptNonce(); private String initScriptNonce () { diff --git a/bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java b/bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java index a17f3fe7..b9f64a3e 100644 --- a/bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java +++ b/bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java @@ -214,6 +214,8 @@ public class StandardAppPrimerService implements AppPrimerService { } } catch (Exception e) { die("_prime: "+shortError(e), e); + } finally { + log.info("_primeApps: completed"); } } diff --git a/bubble-server/src/main/resources/META-INF/bubble/bubble.properties b/bubble-server/src/main/resources/META-INF/bubble/bubble.properties index a09b8007..ebfd4488 100644 --- a/bubble-server/src/main/resources/META-INF/bubble/bubble.properties +++ b/bubble-server/src/main/resources/META-INF/bubble/bubble.properties @@ -1 +1 @@ -bubble.version=Adventure 1.1.1 +bubble.version=Adventure 1.1.2 diff --git a/bubble-server/src/main/resources/bubble/rule/social/block/site/LI.js.hbs b/bubble-server/src/main/resources/bubble/rule/social/block/site/LI.js.hbs new file mode 100644 index 00000000..3a259786 --- /dev/null +++ b/bubble-server/src/main/resources/bubble/rule/social/block/site/LI.js.hbs @@ -0,0 +1,97 @@ +{{JS_PREFIX}}_supports_keywords = true; + +const {{JS_PREFIX}}_site_host = location.protocol + '//' + window.location.hostname + '/'; + +function {{JS_PREFIX}}_apply_blocks(blocked_users) { + const comments = document.querySelector('#comments'); + if (comments === null || comments.length === 0) { + console.warn('No comments found, not filtering'); + return; + } + {{JS_PREFIX}}_consider_block(comments.querySelectorAll('feed-shared-update-v2'), blocked_users); +} + +function {{JS_PREFIX}}_author_from_href(href) { + if (typeof href === 'undefined' || href === null) return null; + let h = href.startsWith({{JS_PREFIX}}_site_host) ? href.substring({{JS_PREFIX}}_site_host.length) : href; + const qPos = h.indexOf('?'); + if (qPos !== -1) { + h = h.substring(0, qPos); + } + if (h.endsWith('/')) h = h.substring(0, h.length - 1); + if (!h.startsWith('in/') && !h.startsWith('company/')) { + return null; + } + const slashPos = h.indexOf('/'); + const name = h.substring(slashPos); + if (name.length > 35 && name.indexOf('-') === -1 && name.indexOf('_') === -1) return null; + console.log("author_from_href: found "+name+' from '+href); + return name; +} + +function {{JS_PREFIX}}_remove_article_from_dom(comment) { + comment.parentNode.removeChild(comment); +} + +function {{JS_PREFIX}}_create_block_control(article, authorName, articleLink) { + let linkClass = articleLink.className; + if (linkClass && linkClass.indexOf('{{JS_PREFIX}}_link_decorated') !== -1) { + return null; + } else { + articleLink.className = articleLink.className ? articleLink.className + ' {{JS_PREFIX}}_link_decorated' : '{{JS_PREFIX}}_link_decorated'; + } + const imgHolder = {{JS_PREFIX}}_create_block_img(); + const blockSpan = document.createElement('span'); + const blockLink = document.createElement('a'); + blockLink.style.zIndex = '{{APP_CONTROLS_Z_INDEX}}' + blockLink.style.cursor = 'pointer'; + blockLink.addEventListener("click", function (e) { + {{JS_PREFIX}}_remove_article_from_dom(article, authorName); + {{JS_PREFIX}}_block_user(authorName); + e.stopPropagation(); + e.preventDefault(); + return false; + }); + blockLink.appendChild(imgHolder); + blockSpan.appendChild(document.createTextNode('\u00A0\u00A0')); + blockSpan.appendChild(blockLink); + blockSpan.id = 'blockSpan_'+{{JS_PREFIX}}_uuidv4(); + console.log('adding block control on '+authorName); + return blockSpan; +} + +function {{JS_PREFIX}}_consider_block(articles, blocked_users) { + if (articles && articles.length && articles.length > 0) { + for (let i=0; iPromoted", + "data": "true" + }] + } +}] diff --git a/bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_matchers.json b/bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_matchers.json new file mode 100644 index 00000000..fba42acd --- /dev/null +++ b/bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_matchers.json @@ -0,0 +1,15 @@ +[{ + "name": "UserBlocker", + "children": { + "AppMatcher": [{ + "name": "LIMatcher", + "site": "LinkedIn", + "template": true, + "requestCheck": true, + "requestModifier": true, + "fqdn": "www.linkedin.com", + "urlRegex": "/feed/", + "rule": "li_user_blocker" + }] + } +}] diff --git a/bubble-server/src/main/resources/models/manifest-app-user-block.json b/bubble-server/src/main/resources/models/manifest-app-user-block.json index 49e14659..218babd7 100644 --- a/bubble-server/src/main/resources/models/manifest-app-user-block.json +++ b/bubble-server/src/main/resources/models/manifest-app-user-block.json @@ -15,6 +15,8 @@ "apps/user_block/insta/bubbleApp_userBlock_insta", "apps/user_block/insta/bubbleApp_userBlock_insta_matchers", "apps/user_block/insta/bubbleApp_userBlock_insta_data", + "apps/user_block/insta/bubbleApp_userBlock_li", + "apps/user_block/insta/bubbleApp_userBlock_li_matchers", "apps/user_block/reddit/bubbleApp_userBlock_reddit", "apps/user_block/reddit/bubbleApp_userBlock_reddit_matchers" ] \ No newline at end of file diff --git a/bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py b/bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py index 2f3267c6..0d0a9119 100644 --- a/bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py +++ b/bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py @@ -2,6 +2,7 @@ # Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ # import asyncio +import base64 import json import re import urllib @@ -64,10 +65,10 @@ def ensure_bubble_csp(csp, req_id): break # if no nonce, then add our nonce if not found_nonce: - new_csp = add_csp_part(new_csp, " ".join(tokens) + " 'nonce="+req_id+"'") + new_csp = add_csp_part(new_csp, " ".join(tokens) + " 'nonce-"+base64.b64encode(bytes(req_id, 'utf-8')).decode()+"' ") else: # does not allow from self, so add self with our nonce - new_csp = add_csp_part(new_csp, tokens[0] + " 'self' 'nonce="+req_id+"'" + " ".join(tokens[1:])) + new_csp = add_csp_part(new_csp, tokens[0] + " 'self' 'nonce-"+base64.b64encode(bytes(req_id, 'utf-8')).decode()+"' " + " ".join(tokens[1:])) else: new_csp = add_csp_part(new_csp, part) return new_csp