Implement auto-updating of the injection script

This commit is contained in:
Tim "timmyRS" Speckhals 2019-12-20 14:07:40 +01:00
parent 7908bb9f64
commit f9585c27d7
9 changed files with 297 additions and 1646 deletions

View File

@ -42,7 +42,7 @@ $build = createZip("Universal Bypass.zip");
$firefox = createZip("Universal Bypass for Firefox.zip");
foreach($index as $fn)
{
if($fn == "README.md")
if($fn == "README.md" || $fn == "injection_script.js")
{
continue;
}

View File

@ -1,4 +1,12 @@
# Privacy Policy
# Universal Bypass Privacy Policy
## Auto-updating
In order to provide up-to-date bypasses, Universal Bypass sends a request to Github every hour, and if a new commit is found, it will download the latest "injection script."
For this, [Github's privacy policy](https://help.github.com/en/github/site-policy/github-privacy-statement) applies.
## Crowd Bypass
When Crowd Bypass (Options > "Give and take the destinations of unbypassable shorteners.") is enabled, a request to my server is being sent for it to work, of which I store nothing.

View File

@ -17,6 +17,15 @@
"changelog": {
"message": "Changelog"
},
"update": {
"message": "Check for updates"
},
"updateYes": {
"message": "Sucessfully updated injection script!"
},
"updateNo": {
"message": "Already up-to-date."
},
"faq": {
"message": "Frequently Asked Questions (FAQ)"
},

View File

@ -47,7 +47,7 @@ isGoodLink=link=>{
},
countIt=()=>brws.storage.local.set({bypass_counter:++bypassCounter})
//Install handler
// Install handler
brws.runtime.onInstalled.addListener(details=>{
if(details.reason=="install")
{
@ -55,8 +55,8 @@ brws.runtime.onInstalled.addListener(details=>{
}
})
//Keeping track of options
var enabled=true,instantNavigation=true,trackerBypassEnabled=true,instantNavigationTrackers=false,blockIPLoggers=true,crowdEnabled=true,infoBoxEnabled=true,userscript="",bypassCounter=0,refererCache={}
// Keeping track of options
var bypassCounter=0,enabled=true,instantNavigation=true,trackerBypassEnabled=true,instantNavigationTrackers=false,blockIPLoggers=true,crowdEnabled=true,infoBoxEnabled=true,userScript=""
brws.storage.sync.get(["disable","navigation_delay","no_tracker_bypass","no_instant_navigation_trackers","allow_ip_loggers","crowd_bypass_opt_out","crowd_open_delay","no_info_box"],res=>{
if(res)
{
@ -98,7 +98,8 @@ brws.storage.local.get(["userscript","bypass_counter"],res=>{
{
if(res.userscript)
{
userscript=res.userscript
userScript=res.userscript
refreshInjectionScript()
}
if(res.bypass_counter)
{
@ -157,16 +158,88 @@ brws.storage.onChanged.addListener(changes=>{
}
if(changes.userscript)
{
userscript=changes.userscript.newValue
userScript=changes.userscript.newValue
refreshInjectionScript()
}
})
//Messaging
// Injection Script Management
let injectionScript = "", upstreamInjectionScript = "", upstreamCommit, channel = {}
const downloadInjectionScript = () => new Promise(callback => {
const finishDownload = () => {
channel = {}
;["stop_watching","crowd_path","crowd_query","crowd_queried","crowd_contribute","adlinkfly_info","adlinkfly_target"].forEach(name => {
upstreamInjectionScript = upstreamInjectionScript.split("{{channel."+name+"}}").join(channel[name] = "data-"+Math.random().toString().substr(2))
})
;["crowdWait","crowdDisabled","infoBoxHide"].forEach(name => {
upstreamInjectionScript = upstreamInjectionScript.split("{{msg."+name+"}}").join(brws.i18n.getMessage(name).split("\\").join("\\\\").split("\"").join("\\\""))
})
upstreamInjectionScript = upstreamInjectionScript.split("{{icon/48.png}}").join(brws.runtime.getURL("icon/48.png"))
refreshInjectionScript()
callback(true)
}
chrome.runtime.getPackageDirectoryEntry(root => root.createReader().readEntries(res => {
let found = false
res.forEach(file => {
if(file.name == "injection_script.js")
{
found = true
file.file(file => {
const reader = new FileReader()
reader.onloadend = () => {
upstreamInjectionScript = reader.result
finishDownload()
}
reader.readAsText(file)
})
}
})
if(!found)
{
let xhr = new XMLHttpRequest()
xhr.onload = () => {
const latestCommit = JSON.parse(xhr.responseText).sha
if(latestCommit == upstreamCommit)
{
callback(false)
}
else
{
upstreamCommit = latestCommit
xhr = new XMLHttpRequest()
xhr.onload = () => {
upstreamInjectionScript = xhr.responseText
finishDownload()
}
xhr.open("GET", "https://raw.githubusercontent.com/timmyRS/Universal-Bypass/" + upstreamCommit + "/injection_script.js", true)
xhr.send()
}
}
xhr.open("GET", "https://api.github.com/repos/timmyRS/Universal-Bypass/commits/master", true)
xhr.send()
}
}))
}),
refreshInjectionScript = () => {
injectionScript = upstreamInjectionScript + "\n" + userScript
}
downloadInjectionScript()
brws.alarms.create("update-injection-script", {periodInMinutes: 60})
brws.alarms.onAlarm.addListener(alert => {
console.assert(alert.name == "update-injection-script")
downloadInjectionScript()
})
// Messaging
brws.runtime.onMessage.addListener((req, sender, respond) => {
switch(req.type)
{
case "can-run":
respond({enabled, crowdEnabled, infoBoxEnabled, userscript})
case "content":
respond({enabled, channel, crowdEnabled, infoBoxEnabled, injectionScript})
break;
case "options":
respond({upstreamCommit, bypassCounter, userScript})
break;
case "open-tab":
@ -194,32 +267,43 @@ brws.runtime.onMessage.addListener((req, sender, respond) => {
}
})
brws.runtime.onConnect.addListener(port => {
console.assert(port.name == "adlinkfly-info")
port.onMessage.addListener(msg => {
let xhr=new XMLHttpRequest(),t="",iu=msg
xhr.onload=()=>{
let i=new DOMParser().parseFromString(xhr.responseText,"text/html").querySelector("img[src^='//api.miniature.io']")
if(i)
{
let url=new URL(i.src)
if(url.search&&url.search.indexOf("url="))
switch(port.name)
{
case "update":
downloadInjectionScript().then(success => port.postMessage({success, upstreamCommit}))
break;
case "adlinkfly-info":
port.onMessage.addListener(msg => {
let xhr=new XMLHttpRequest(),t="",iu=msg
xhr.onload=()=>{
let i=new DOMParser().parseFromString(xhr.responseText,"text/html").querySelector("img[src^='//api.miniature.io']")
if(i)
{
t=decodeURIComponent(url.search.split("url=")[1].split("&")[0])
let url=new URL(i.src)
if(url.search&&url.search.indexOf("url="))
{
t=decodeURIComponent(url.search.split("url=")[1].split("&")[0])
}
}
port.postMessage(t)
}
port.postMessage(t)
}
xhr.onerror=()=>port.postMessage(t)
if(iu.substr(-1) != "/")
{
iu += "/"
}
xhr.open("GET", iu+"info", true)
xhr.send()
})
xhr.onerror=()=>port.postMessage(t)
if(iu.substr(-1) != "/")
{
iu += "/"
}
xhr.open("GET", iu+"info", true)
xhr.send()
})
break;
default:
console.warn("Invalid connection:", port)
}
})
//Internal redirects to extension URLs to bypass content script limitations
// Internal redirects to extension URLs to bypass content script limitations
brws.webRequest.onBeforeRequest.addListener(details=>{
if(details.url.substr(38)=="1")
{
@ -236,6 +320,17 @@ brws.webRequest.onBeforeRequest.addListener(details=>{
return encodedRedirect(arr[0],arr[1])
},{types:["main_frame"],urls:["*://universal-bypass.org/bypassed?target=*&referer=*"]},["blocking"])
brws.webRequest.onBeforeRequest.addListener(details=>{
countIt()
return {redirectUrl:brws.runtime.getURL("html/crowd-bypassed.html")+details.url.substr(43)}
},{types:["main_frame"],urls:["https://universal-bypass.org/crowd-bypassed?*"]},["blocking"])
brws.webRequest.onBeforeRequest.addListener(details=>{
return {redirectUrl:brws.runtime.getURL("html/options.html")+details.url.substr(36)}
},{types:["main_frame"],urls:["https://universal-bypass.org/options"]},["blocking"])
// Navigation handling including presenting referer header to destinations
var refererCache={}
brws.webRequest.onBeforeRequest.addListener(details=>{
let arr=details.url.substr(45).split("&referer=")
arr[0]=(new URL(decodeURIComponent(arr[0]))).toString()
@ -280,16 +375,7 @@ brws.webRequest.onCompleted.addListener(details=>{
}
},{types:["main_frame"],urls:["<all_urls>"]})
brws.webRequest.onBeforeRequest.addListener(details=>{
countIt()
return {redirectUrl:brws.runtime.getURL("html/crowd-bypassed.html")+details.url.substr(43)}
},{types:["main_frame"],urls:["https://universal-bypass.org/crowd-bypassed?*"]},["blocking"])
brws.webRequest.onBeforeRequest.addListener(details=>{
return {redirectUrl:brws.runtime.getURL("html/options.html")+details.url.substr(36)}
},{types:["main_frame"],urls:["https://universal-bypass.org/options"]},["blocking"])
//Preflight Bypasses
// Preflight Bypasses
brws.webRequest.onBeforeRequest.addListener(details=>{
if(enabled)
{

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,3 @@
var brws=(typeof browser=="undefined"?chrome:browser)
const brws=(typeof browser=="undefined"?chrome:browser)
document.querySelectorAll("[data-message]").forEach(e=>e.textContent=brws.i18n.getMessage(e.getAttribute("data-message")))
document.querySelectorAll("[data-message-nbsp]").forEach(e=>e.innerHTML=brws.i18n.getMessage(e.getAttribute("data-message-nbsp")).split(" ").join("&nbsp;"))

View File

@ -8,7 +8,9 @@
</head>
<body>
<h1>Universal Bypass</h1>
<p><span data-message="version"></span> <span id="version"></span> &middot; <a href="https://universal-bypass.org/changelog" target="_blank" data-message="changelog"></a> &middot; <a href="https://universal-bypass.org/faq" target="_blank" data-message="faq"></a></p>
<p><span data-message="version"></span> <span id="version"></span> &middot; <a href="#" data-message-nbsp="update"></a> &middot; <a href="https://universal-bypass.org/changelog" target="_blank" data-message-nbsp="changelog"></a> &middot; <a href="https://universal-bypass.org/faq" target="_blank" data-message-nbsp="faq"></a> &middot; <a href="https://github.com/timmyrs/Universal-Bypass/blob/master/PRIVACY.md" target="_blank" data-message-nbsp="privacyPolicy"></a></p>
<p data-message="updateYes" style="display:none;margin-top:15px"></p>
<p data-message="updateNo" style="display:none;margin-top:15px"></p>
<div id="counter" style="display:none;margin-top:15px">
<span data-message="bypassCounter"></span> <a href="https://universal-bypass.org/support" target="_blank" data-message="support"></a>
</div>
@ -19,7 +21,7 @@
<input type="checkbox" id="option-tracker-bypass"> <label for="option-tracker-bypass"><span data-message="optionsTrackerBypass"></span> (Bit.ly, Goo.gl, T.co, ...)</label> (<a href="https://apimon.de/privacy" target="_blank" data-message="privacyPolicy"></a>)<br>
<input type="checkbox" id="option-instant-navigation-trackers"> <label for="option-instant-navigation-trackers" data-message="optionsInstantNavigationTrackers"></label><br>
<input type="checkbox" id="option-block-ip-loggers"> <label for="option-block-ip-loggers" data-message="optionsBlockIPLoggers"></label><br>
<input type="checkbox" id="option-crowd-bypass"> <label for="option-crowd-bypass" data-message="optionsCrowdBypass"></label> (<a href="https://github.com/timmyrs/Universal-Bypass/blob/master/PRIVACY.md" target="_blank" data-message="privacyPolicy"></a>)<br>
<input type="checkbox" id="option-crowd-bypass"> <label for="option-crowd-bypass" data-message="optionsCrowdBypass"></label><br>
<input type="checkbox" id="option-crowd-open-delay-toggle"> <span data-message="optionsCrowdAutoOpen"></span><br>
<input type="checkbox" id="option-info-box"> <label for="option-info-box" data-message="optionsInfoBox"></label>
</ul>

View File

@ -1,17 +1,7 @@
//Bypass counter
brws.storage.local.get(["bypass_counter"],res=>{
if(res.bypass_counter>1)
{
const p=document.querySelector("[data-message='bypassCounter']")
p.innerHTML=p.innerHTML.replace("%","<b>"+res.bypass_counter+"</b>")
document.getElementById("counter").style.display="block"
}
})
//Options
document.getElementById("version").textContent=brws.runtime.getManifest().version
document.querySelector("[data-message='optionsNavigationDelay']").innerHTML=document.querySelector("[data-message='optionsNavigationDelay']").innerHTML.replace("%",'<input id="option-navigation-delay" type="number" min="0" max="60" skip="1" style="width:34px">')
document.querySelector("[data-message='optionsCrowdAutoOpen']").innerHTML=document.querySelector("[data-message='optionsCrowdAutoOpen']").innerHTML.replace("%",'<input id="option-crowd-open-delay" type="number" min="0" max="60" skip="1" style="width:34px">')
document.querySelector("[data-message='optionsUserscriptsDescription']").innerHTML=document.querySelector("[data-message='optionsUserscriptsDescription']").textContent.replace("GitHub","<a href='https://github.com/timmyrs/Universal-Bypass/blob/master/injection_script.js' target='_blank'>GitHub</a>")
const enabledCheckbox=document.getElementById("option-enabled"),
enabledLabel=document.querySelector("label[for='option-enabled']"),
navigationDelayInput=document.getElementById("option-navigation-delay"),
@ -22,8 +12,109 @@ blockIPLoggersCheckbox=document.getElementById("option-block-ip-loggers"),
crowdBypassCheckbox=document.getElementById("option-crowd-bypass"),
crowdOpenDelayInput=document.getElementById("option-crowd-open-delay"),
crowdOpenDelayCheckbox=document.getElementById("option-crowd-open-delay-toggle"),
infoBoxCheckbox=document.getElementById("option-info-box")
let navigationDelayInputTimer,crowdOpenDelayInputTimer
infoBoxCheckbox=document.getElementById("option-info-box"),
instantNavigationTrackersLogic = () => {
if(!trackerBypassCheckbox.checked||(navigationDelayCheckbox.checked&&navigationDelayInput.value==0))
{
instantNavigationTrackersCheckbox.setAttribute("disabled","disabled")
}
else
{
instantNavigationTrackersCheckbox.removeAttribute("disabled")
}
},
crowdOpenDelayLogic = () => {
if(crowdOpenDelayCheckbox.checked)
{
crowdOpenDelayInput.removeAttribute("disabled")
}
else
{
crowdOpenDelayInput.setAttribute("disabled","disabled")
}
},
defaultUserScript=`// Some examples of what you can do with custom bypasses:
domainBypass("example.com", () => {
// Triggered on example.com and subdomains (e.g. www.example.com)
ensureDomLoaded(() => {
// Triggered as soon as the DOM is ready
// You can use ifElement to check if an element is available via document.querySelector:
ifElement("a#skip_button[href]", a => {
safelyNavigate(a.href)
// safelyNavigate makes sure the given URL is valid
}, () => {
// Optional function to be called if the given element is not available
})
})
// You can also use awaitElement to wait until an element is available via a query selector:
awaitElement("a#skip_button[href]", a => {
safelyNavigate(a.href)
})
})
domainBypass(/example\\.(com|org)/, () => {
// Triggered if the regex matches any part of the hostname
})
hrefBypass(/example\\.(com|org)/, () => {
// Triggered if the regex matches any part of the URL
})
// Enjoy! Your changes will be saved automatically.
`
let navigationDelayInputTimer,crowdOpenDelayInputTimer,saveTimer,updating=false,
hash=location.hash.toString().replace("#","")
editor=ace.edit("userscript",{mode:"ace/mode/javascript",theme:"ace/theme/monokai"})
if(hash)
{
document.querySelector("[for='"+hash+"']").className="highlight"
}
brws.runtime.sendMessage({type: "options"}, res => {
document.getElementById("version").textContent=brws.runtime.getManifest().version+"-"+(res.upstreamCommit?res.upstreamCommit.substr(0,7):"dev")
document.querySelector("[data-message-nbsp='update']").onclick=()=>{
if(updating)
{
return
}
updating=true
let port=brws.runtime.connect({name: "update"})
port.onMessage.addListener(res => {
document.getElementById("version").textContent=brws.runtime.getManifest().version+"-"+(res.upstreamCommit?res.upstreamCommit.substr(0,7):"dev")
let e=document.querySelector("[data-message='update"+(res.success?"Yes":"No")+"']")
e.style.display="block"
setTimeout(()=>{
e.style.display="none"
updating=false
},3000)
port.disconnect()
})
}
if(res.bypassCounter > 1)
{
const p=document.querySelector("[data-message='bypassCounter']")
p.innerHTML=p.innerHTML.replace("%","<b>"+res.bypassCounter+"</b>")
document.getElementById("counter").style.display="block"
}
if(res.userScript)
{
editor.setValue(res.userScript)
}
else
{
editor.setValue(defaultUserScript)
}
editor.resize()
editor.clearSelection()
editor.on("change", ()=>{
clearInterval(saveTimer)
saveTimer=setTimeout(()=>{
brws.storage.local.set({
userscript: editor.getValue()
})
},500)
})
})
brws.storage.sync.get(["disable","navigation_delay","no_tracker_bypass","no_instant_navigation_trackers","allow_ip_loggers","crowd_bypass_opt_out","crowd_open_delay","no_info_box"],res=>{
if(res==undefined)
{
@ -167,85 +258,3 @@ brws.storage.sync.get(["disable","navigation_delay","no_tracker_bypass","no_inst
})
}
})
function instantNavigationTrackersLogic()
{
if(!trackerBypassCheckbox.checked||(navigationDelayCheckbox.checked&&navigationDelayInput.value==0))
{
instantNavigationTrackersCheckbox.setAttribute("disabled","disabled")
}
else
{
instantNavigationTrackersCheckbox.removeAttribute("disabled")
}
}
function crowdOpenDelayLogic()
{
if(crowdOpenDelayCheckbox.checked)
{
crowdOpenDelayInput.removeAttribute("disabled")
}
else
{
crowdOpenDelayInput.setAttribute("disabled","disabled")
}
}
//Highlight option from hash
let hash=location.hash.toString().replace("#","")
if(hash)
{
document.querySelector("[for='"+hash+"']").className="highlight"
}
//Custom Bypasses
var example=`// Some examples of what you can do with custom bypasses:
domainBypass("example.com", () => {
// Triggered on example.com and subdomains (e.g. www.example.com)
ensureDomLoaded(() => {
// Triggered as soon as the DOM is ready
// You can use ifElement to check if an element is available via document.querySelector:
ifElement("a#skip_button[href]", a => {
safelyNavigate(a.href)
// safelyNavigate makes sure the given URL is valid
}, () => {
// Optional function to be called if the given element is not available
})
})
// You can also use awaitElement to wait until an element is available via a query selector:
awaitElement("a#skip_button[href]", a => {
safelyNavigate(a.href)
})
})
domainBypass(/example\\.(com|org)/, () => {
// Triggered if the regex matches any part of the hostname
})
hrefBypass(/example\\.(com|org)/, () => {
// Triggered if the regex matches any part of the URL
})
// Enjoy! Your changes will be saved automatically.
`,saveTimer,editor=ace.edit("userscript",{mode:"ace/mode/javascript",theme:"ace/theme/monokai"}),
span=document.querySelector("[data-message='optionsUserscriptsDescription']")
span.innerHTML=span.textContent.replace("GitHub","<a href='https://github.com/timmyrs/Universal-Bypass/blob/master/content_script.js' target='_blank'>GitHub</a>")
brws.storage.local.get(["userscript"],res=>{
if(res&&res.userscript)
{
editor.setValue(res.userscript)
}
else
{
editor.setValue(example)
brws.storage.local.set({
userscript: example
})
}
editor.resize()
editor.clearSelection()
editor.on("change", ()=>{
clearInterval(saveTimer)
saveTimer=setTimeout(()=>{
brws.storage.local.set({
userscript: editor.getValue()
})
},500)
})
})

View File

@ -15,6 +15,7 @@
"version_name": "12.0-dev",
"author": "Tim \"timmyRS\" Speckhals",
"permissions": [
"alarms",
"storage",
"webRequest",
"webRequestBlocking",