@@ -67,6 +67,14 @@ public class AppMatcherDAO extends AppTemplateEntityDAO<AppMatcher> { | |||||
return super.preCreate(matcher); | 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) { | @Override public AppMatcher postUpdate(AppMatcher matcher, Object context) { | ||||
final BubbleApp app = appDAO.findByUuid(matcher.getApp()); | final BubbleApp app = appDAO.findByUuid(matcher.getApp()); | ||||
if (app == null) return die("postUpdate("+ matcher.getUuid()+"): app not found: "+ matcher.getApp()); | if (app == null) return die("postUpdate("+ matcher.getUuid()+"): app not found: "+ matcher.getApp()); | ||||
@@ -43,7 +43,7 @@ public class FilterHttpRequest { | |||||
@Getter @Setter private String contentSecurityPolicy; | @Getter @Setter private String contentSecurityPolicy; | ||||
public boolean hasContentSecurityPolicy () { return !empty(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(); | @Getter(lazy=true) private final String scriptNonce = initScriptNonce(); | ||||
private String initScriptNonce () { | private String initScriptNonce () { | ||||
@@ -214,6 +214,8 @@ public class StandardAppPrimerService implements AppPrimerService { | |||||
} | } | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
die("_prime: "+shortError(e), e); | die("_prime: "+shortError(e), e); | ||||
} finally { | |||||
log.info("_primeApps: completed"); | |||||
} | } | ||||
} | } | ||||
@@ -1 +1 @@ | |||||
bubble.version=Adventure 1.1.1 | |||||
bubble.version=Adventure 1.1.2 |
@@ -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; i<articles.length; i++) { | |||||
const article = articles[i]; | |||||
const firstEval = {{JS_PREFIX}}_mark_evaluated(article); | |||||
if ({{JS_PREFIX}}_includes_block_keyword(article, firstEval)) { | |||||
{{JS_PREFIX}}_remove_article_from_dom(article); | |||||
continue; | |||||
} | |||||
const articleLinks = Array.from(article.getElementsByTagName('a')); | |||||
for (let j=0; j<articleLinks.length; j++) { | |||||
const articleLink = articleLinks[i]; | |||||
const author = {{JS_PREFIX}}_author_from_href(articleLink.href); | |||||
if (author === null) continue; | |||||
if (author in blocked_users) { | |||||
{{JS_PREFIX}}_tally_author_block(author); | |||||
if (!firstEval) {{JS_PREFIX}}_untally_allow(); | |||||
{{JS_PREFIX}}_remove_article_from_dom(article); | |||||
} else if (firstEval) { | |||||
const authorSpans = Array.from(articleLink.getElementsByClassName('feed-shared-actor__name')); | |||||
if (authorSpans.length === 0) { | |||||
continue; | |||||
} | |||||
let b = {{JS_PREFIX}}_create_block_control(article, author, articleLink); | |||||
// console.log('inserting span='+b.id+' for article by '+author); | |||||
authorSpans[0].parentNode.appendChild(b); | |||||
{{JS_PREFIX}}_tally_allow(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,23 @@ | |||||
[{ | |||||
"name": "UserBlocker", | |||||
"children": { | |||||
"AppSite": [{ | |||||
"name": "LinkedIn", | |||||
"url": "https://linkedin.com", | |||||
"description": "The world's largest professional network with 706+ million users in more than 200 countries and territories worldwide", | |||||
"template": true, | |||||
"maxSecurityHosts": [ | |||||
"linkedin.com", "*.linkedin.com" | |||||
], | |||||
"enableMaxSecurityHosts": true | |||||
}], | |||||
"AppRule": [{ | |||||
"name": "li_user_blocker", | |||||
"template": true, | |||||
"driver": "JsUserBlockerRuleDriver", | |||||
"config": { | |||||
"siteJsTemplate": "bubble/rule/social/block/site/LI.js.hbs" | |||||
} | |||||
}] | |||||
} | |||||
}] |
@@ -0,0 +1,12 @@ | |||||
[{ | |||||
"name": "UserBlocker", | |||||
"children": { | |||||
"AppData": [{ | |||||
"site": "LinkedIn", | |||||
"template": true, | |||||
"matcher": "LiMatcher", | |||||
"key": "kw:_<span>Promoted</span>", | |||||
"data": "true" | |||||
}] | |||||
} | |||||
}] |
@@ -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" | |||||
}] | |||||
} | |||||
}] |
@@ -15,6 +15,8 @@ | |||||
"apps/user_block/insta/bubbleApp_userBlock_insta", | "apps/user_block/insta/bubbleApp_userBlock_insta", | ||||
"apps/user_block/insta/bubbleApp_userBlock_insta_matchers", | "apps/user_block/insta/bubbleApp_userBlock_insta_matchers", | ||||
"apps/user_block/insta/bubbleApp_userBlock_insta_data", | "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", | ||||
"apps/user_block/reddit/bubbleApp_userBlock_reddit_matchers" | "apps/user_block/reddit/bubbleApp_userBlock_reddit_matchers" | ||||
] | ] |
@@ -2,6 +2,7 @@ | |||||
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | # Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | ||||
# | # | ||||
import asyncio | import asyncio | ||||
import base64 | |||||
import json | import json | ||||
import re | import re | ||||
import urllib | import urllib | ||||
@@ -64,10 +65,10 @@ def ensure_bubble_csp(csp, req_id): | |||||
break | break | ||||
# if no nonce, then add our nonce | # if no nonce, then add our nonce | ||||
if not found_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: | else: | ||||
# does not allow from self, so add self with our nonce | # 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: | else: | ||||
new_csp = add_csp_part(new_csp, part) | new_csp = add_csp_part(new_csp, part) | ||||
return new_csp | return new_csp | ||||