diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e8d8645f7..18b61fbb1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index d23cad0d4..e44be67ae 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,7 +1,7 @@ name: Close inactive issues on: schedule: - - cron: "30 1 * * *" + - cron: "0 0 14,28 * *" workflow_dispatch: jobs: diff --git a/docs/Bypassed.md b/docs/Bypassed.md index 85246cd60..664509fe2 100644 --- a/docs/Bypassed.md +++ b/docs/Bypassed.md @@ -90,66 +90,66 @@ This is a list of websites bypassed in the MV2 version of the extension. | https://njiirx.com | 🛑 | | https://healthykk.com | 🛑 | | https://linkasm.com | 🛑 | -| https://dxdrive.com | ❌ | -| https://getwallpapers.com | ❌ | -| https://sammobile.com | ❌ | -| https://ydfile.com | ❌ | -| https://mobilemodsapk.com | ❌ | -| https://dlandroid.com | ❌ | -| https://download.modsofapk.com | ❌ | +| https://dxdrive.com | 🛑 | +| https://getwallpapers.com | ✅ | +| https://sammobile.com | ✅ | +| https://ydfile.com | ✅ | +| https://mobilemodsapk.com | ✅ | +| https://dlandroid.com | ✅ | +| https://download.modsofapk.com | ✅ | | https://punchsubs.net | 🛑 | -| https://zedge.net | ❌ | -| https://fex.net | ❌ | -| https://k2s.cc | ❌ | +| https://zedge.net | ✅ | +| https://fex.net | ✅ | +| https://k2s.cc | ✅ | | https://muhammadyoga.me | 🛑 | -| https://u.to | ❌ | +| https://u.to | ✅ | | https://skiplink.io | 🛑 | | https://uploadfree.info | 🛑 | | https://freeupload.info | 🛑 | | https://fstore.biz | 🛑 | | https://thesimsresource.com/ | ❌ | -| https://firefaucet.win/ | ❌ | -| https://sfirmware.com/ | ❌ | -| https://apkily.com/ | ❌ | -| https://androidtop.net/ | ❌ | -| https://emulator.games/ | ❌ | -| https://curseforge.com/ | ❌* | +| https://firefaucet.win/ | ✅ | +| https://sfirmware.com/ | ✅ | +| https://apkily.com/ | 🛑 | +| https://androidtop.net/ | ✅ | +| https://emulator.games/ | ✅ | +| https://curseforge.com/ | ✅* | | https://racaty.com | 🛑 | -| https://longfiles.com | ❌ | -| https://filepuma.com | ❌ | -| https://portableapps.com | ❌ | -| https://indishare.org | ❌ | +| https://longfiles.com | 🛑 | +| https://filepuma.com | ✅ | +| https://portableapps.com | ✅ | +| https://indishare.org | ✅ | | https://datei.to | 🛑 | | https://keisekai.fun | 🛑 | -| https://solvetube.site | ❌ | -| https://lkc21.net | ❌ | +| https://solvetube.site | ✅ | +| https://lkc21.net | ✅ | | https://layarkacaxxi.org | 🛑 | | https://fastforward.team | ✅ | | https://acortame.xyz | 🛑 | | https://linkvertise.com | ✅ | | https://linkvertise.net | ✅ | | https://link-to.net | ✅ | -| https://filefactory.com | ❌ | -| https://file-upload.com | ❌ | -| https://asdfiles.com | ❌ | -| https://mega4up.com | ❌ | -| https://up-load.io | ❌ | -| https://cosmobox.org | ❌ | +| https://filefactory.com | ✅ | +| https://file-upload.com | ✅ | +| https://asdfiles.com | ✅ | +| https://mega4up.com | ✅ | +| https://up-load.io | ✅ | +| https://cosmobox.org | ✅ | | https://rockfile.co | 🛑 | -| https://devdrive.cloud | ❌ | +| https://devdrive.cloud | ✅ | | https://srt.am | ✅ | | https://complete2unlock.com | ✅ | | https://won.pe | 🛑 | | https://gotoo.loncat.in | 🛑 | -| https://idnation.net | ❌ | +| https://idnation.net | ✅ | | https://mazika2day.com | 🛑 | -| https://ux9.de | ❌ | -| https://softpedia.com | ❌ | +| https://ux9.de | ✅ | +| https://softpedia.com | ✅ | | https://rapidcrypt.net | 🛑 | | https://rom.io | 🛑 | -| https://show.co | ❌ | +| https://show.co | 🛑 | | https://vcrypt.net | 🛑 | -| https://1link.club | ❌ | +| https://1link.club | ✅ | | https://bomurl.com | 🛑 | | https://4snip.pw/out/ | 🛑 | | https://4snip.pw/decode/ | 🛑 | @@ -158,7 +158,7 @@ This is a list of websites bypassed in the MV2 version of the extension. | https://elsfile.org | 🛑 | | https://goou.in | 🛑 | | https://manualsbooks.com | ❌ | -| https://ryn.cc | ❌ | +| https://ryn.cc | ✅ | | https://connect-trojan.net | 🛑 | | https://binbox.io | 🛑 | | https://lnk2.cc | ❌ | @@ -188,8 +188,8 @@ This is a list of websites bypassed in the MV2 version of the extension. | https://linkerload.com | ❌ | | https://dawnstation.com | 🛑 | | https://hokiwikiped.net | 🛑 | -| https://spaste.com/s/ | ❌ | -| https://spaste.com/site/ | ❌ | +| https://spaste.com/s/ | ✅ | +| https://spaste.com/site/ | ✅ | | https://get-click2.blogspot.com | ❌ | | https://informations-library.blogspot.com | ❌ | | https://media-blue.blogspot.com | ❌ | @@ -344,24 +344,33 @@ This is a list of websites bypassed in the MV2 version of the extension. | https://shorten.sh | 🛑 | | https://urapk.com | ❌ | | https://expertvn.com | ❌ | -| https://mediafile.cloud | ❌ | +| https://mediafile.cloud | 🛑 | | https://mlwbd.pw | 🛑 | -| https://uploadking.net | ❌ | -| https://5play.ru | ❌ | -| https://daunshorte.kertashitam.com | ❌ | +| https://uploadking.net | ✅ | +| https://5play.ru | ✅ | +| https://daunshorte.kertashitam.com | 🛑 | | https://swatchseries.to/ | 🛑 | -| https://tl.gd | ❌ | +| https://tl.gd | ✅ | | https://apkmodo.com | 🛑 | | https://multifilemirror.com | 🛑 | | https://welcome.indihome.co.id | 🛑 | -| https://techrfour.com | ❌ | -| https://gaminplay.com | ❌ | +| https://techrfour.com | 🛑 | +| https://gaminplay.com | 🛑 | | https://dl.helow.id | 🛑 | | https://dl.ocanoke.com | 🛑 | | https://tudofinanceiro.club | 🛑 | -| https://apkhubs.com | ❌ | -| https://favpng.com | ❌ | -| https://sh.st | ❌ | +| https://apkhubs.com | ✅ | +| https://favpng.com | ✅ | +| https://sh.st | ✅ | +| http://clkmein.com | ✅ | +| http://viid.me | ✅ | +| http://xiw34.com | ✅ | +| http://corneey.com | ✅ | +| http://gestyy.com | ✅ | +| http://cllkme.com | ✅ | +| http://festyy.com | ✅ | +| http://destyy.com | ✅ | +| http://ceesty.com | ✅ | | https://maukredit.online | 🛑 | | https://ay.link | 🛑 | | https://shtms.co | 🛑 | diff --git a/scripts/build_js/injection_script_template.js b/scripts/build_js/injection_script_template.js index 7f006b610..e31c826bf 100644 --- a/scripts/build_js/injection_script_template.js +++ b/scripts/build_js/injection_script_template.js @@ -6,12 +6,14 @@ function matchingBypass(bypasses) { for (const [key] of Object.entries(bypasses)) { if (key.charAt(0) === '/' && key.charAt(key.length - 1) === '/') { let pattern = new RegExp(key.substring(1, key.length - 1)); - return pattern.test(location.href) ? key : null; + if (pattern.test(location.href)) { + return key; + } } else if (key === location.host) { return key; } - return null; } + return null; } if (matchingBypass(bypasses)) { diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index b7927e91c..44bdae8fb 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -36,25 +36,49 @@ "message": "Enable website bypasses." }, "optionsNavigationDelay": { - "message": "Take me to destinations after % second(s)." + "message": "Bypass Link Shorteners after % second(s)." }, "optionsTrackerBypass": { - "message": "Bypass trackers (such as bit.ly and goo.gl) using Unshorten.me." + "message": "Bypass trackers" }, "optionsInstantNavigationTrackers": { - "message": "Instantly take me to destinations of trackers." + "message": "Bypass Trackers immediately." }, "optionsBlockIPLoggers": { - "message": "Block IP loggers if they can't be bypassed." + "message": "Block IP loggers." }, "optionsCrowdBypass": { - "message": "Crowd Bypass: Give and take the destinations of unbypassable shorteners." + "message": "Crowd Bypass." }, "optionsCrowdAutoOpen": { - "message": "Open crowd-sourced destinations in a new tab after % second(s)." + "message": "Open Crowd destinations after % second(s)." }, "optionsCrowdAutoClose": { - "message": "Close tab containing the crowd-sourced destination after % second(s) of visiting it." + "message": "Close Crowd destination after % second(s)." + }, + "optionsNavigationDelayDescription": { + "message": "Automatically bypass link shorteners such as Adf.ly, sub2unlock.com, after a certain amount of seconds. Recommended: On, 0 seconds." + }, + "optionsTrackerBypassDescription": { + "message": "Automatically bypass trackers such as t.co and goo.gl using the Unshorten.me API. Recommended: On." + }, + "optionsInstantNavigationTrackersDescription": { + "message": "Bypass trackers immediately and automatically, instead of providing a link to the destination. Recommended: On." + }, + "optionsBlockIPLoggersDescription": { + "message": "If FastForward can't bypass a link that will log your IP, we will block it instead. Recommended: On." + }, + "optionsCrowdBypassDescription": { + "message": "Crowd Bypass: Use FastForward's online database of bypassed links to instantly bypass popular sites. Recommended: On." + }, + "optionsCrowdAutoOpenDescription": { + "message": "Open Crowd Bypass destinations in a new tab rather than the current one, so you don't lose your place. Recommended: Off" + }, + "optionsCrowdAutoCloseDescription": { + "message": "Close Crowd Bypass destination after a few seconds. Useful if most of the sites you visit have a direct download link. Recommended: Off" + }, + "crowdBypassTempDisabledDescription": { + "message": "Crowd bypass has been temporarily disabled when FastForward was updated. Please re-enable it." }, "privacyPolicy": { "message": "Privacy Policy" @@ -134,8 +158,8 @@ "optionsWhitelist": { "message": "Whitelist" }, - "optionsWhitelistDescription": { - "message": "A list of sites formatted as domain.tld or subdomain.domain.tld separated with a newline that will not be bypassed by FastForward. You can use * as a wildcard." + "optionsWhitelistDescription": { + "message": "A list of sites formatted as domain.tld or subdomain.domain.tld that won't be bypassed by FastForward. Seperate domains with new lines. Use * as a wildcard." }, "tempDisableCrowdBypassButton": { "message": "Temporarily disable crowd bypass" @@ -152,5 +176,5 @@ "trackerBypassedError": { "message": "Oops! Something went wrong. Redirecting to the original link..." } - + } diff --git a/src/bypasses/androidtop.js b/src/bypasses/androidtop.js new file mode 100644 index 000000000..0fa1a2bf5 --- /dev/null +++ b/src/bypasses/androidtop.js @@ -0,0 +1,16 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Androidtop extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + // If the variable downloadTimer is defined, then use clearInterval to stop the timer. + document.querySelector('.counterhide').setAttribute('style', 'opacity: 0; visibility: hidden; height: 0px;'); + document.querySelector('.download-result').setAttribute('style', 'opacity: 1; visibility: visible;'); + } +} + +export const matches = ['androidtop.net']; diff --git a/src/bypasses/apkhubs.js b/src/bypasses/apkhubs.js new file mode 100644 index 000000000..8d980bb51 --- /dev/null +++ b/src/bypasses/apkhubs.js @@ -0,0 +1,16 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Apkhubs extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + this.helpers.ifElement("a#downloadbtn", a => { + this.helpers.safelyNavigate(a.href) + }) + } +} + +export const matches = ['apkhubs.com']; diff --git a/src/bypasses/clictune.js b/src/bypasses/clictune.js index 3098007df..cbf61bc75 100644 --- a/src/bypasses/clictune.js +++ b/src/bypasses/clictune.js @@ -16,4 +16,4 @@ export default class Clictune extends BypassDefinition { } } -export const matches = ['www.dlink2.com'] +export const matches = ['www.dlink2.net', 'www.dlink2.com', 'www.clictune.com'] diff --git a/src/bypasses/curseforge.js b/src/bypasses/curseforge.js new file mode 100644 index 000000000..1609b756a --- /dev/null +++ b/src/bypasses/curseforge.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Curseforge extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + window.setInterval = f => setInterval(f, 100) + } +} + +export const matches = ['curseforge.com']; diff --git a/src/bypasses/favpng.js b/src/bypasses/favpng.js new file mode 100644 index 000000000..df721c8f3 --- /dev/null +++ b/src/bypasses/favpng.js @@ -0,0 +1,22 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Favpng extends BypassDefinition { + constructor() { + super(); + this.ensure_dom = true; + } + + execute() { + const scripts = document.getElementsByTagName('script'); + for (let i = 0; i < scripts.length; i++) { + let script = scripts[i]; + if (script.textContent.includes('https://download.favpng.com/api_download.php?')) { + let startIndex = script.textContent.indexOf('https://download.favpng.com/api_download.php?'); + let endIndex = script.textContent.indexOf('"', startIndex); + this.helpers.safelyNavigate(script.textContent.substring(startIndex, endIndex)); + } + } + } +} + +export const matches = ['favpng.com']; diff --git a/src/bypasses/filefactory.js b/src/bypasses/filefactory.js new file mode 100644 index 000000000..233aaa3b3 --- /dev/null +++ b/src/bypasses/filefactory.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Filefactory extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + this.helpers.insertInfoBox("Unfortunately, the download server will ensure that you have waited before allowing you to download the file.") + } +} + +export const matches = ['https://filefactory.com', 'https://file-upload.com', 'https://asdfiles.com', 'https://mega4up.com', ' https://up-load.io', 'https://cosmobox.org', 'https://devdrive.cloud']; diff --git a/src/bypasses/filepuma.js b/src/bypasses/filepuma.js new file mode 100644 index 000000000..8e646c0e6 --- /dev/null +++ b/src/bypasses/filepuma.js @@ -0,0 +1,22 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Filepuma extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + // Find the last script in the page + let scripts = document.getElementsByTagName('script'); + let lastScript = scripts[scripts.length - 1]; + // Get the script's source + let scriptSrc = lastScript.innerHTML; + // Find the first location.href in the script and get the value of location.href + let url = scriptSrc.split('location.href = "')[1].split('"')[0]; + alert(url) + + } +} + +export const matches = ['filepuma.com']; diff --git a/src/bypasses/firefaucet.js b/src/bypasses/firefaucet.js new file mode 100644 index 000000000..1598a7469 --- /dev/null +++ b/src/bypasses/firefaucet.js @@ -0,0 +1,15 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Firefaucet extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + window.setInterval = f => setInterval(f, 1); + + } +} + +export const matches = ['firefaucet.win', 'sfirmware.com', 'emulator.games']; diff --git a/src/bypasses/fiveplay.js b/src/bypasses/fiveplay.js new file mode 100644 index 000000000..777f6482e --- /dev/null +++ b/src/bypasses/fiveplay.js @@ -0,0 +1,20 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Fiveplay extends BypassDefinition { + constructor() { + super(); + } + + execute() { + // Find the div with the class "download_btn", find the a tag within the div, and return the href attribute + let downloadBtnGroup = document.querySelector('.download-btn-group'); + if (downloadBtnGroup) { + let anchorElement = downloadBtnGroup.querySelector('a'); + if (anchorElement) { + this.helpers.safelyAssign(anchorElement.getAttribute('href')); + } + } + } +} + +export const matches = ['5play.ru']; diff --git a/src/bypasses/get2clickblogspot.js b/src/bypasses/get2clickblogspot.js new file mode 100644 index 000000000..5a13e33ef --- /dev/null +++ b/src/bypasses/get2clickblogspot.js @@ -0,0 +1,26 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Get2clickblogspot extends BypassDefinition { + constructor() { + super(); + } + + execute() { + // Find the div with the class "hidden btn waves-effect waves-light blue darken-2 white-text" and change it to "btn waves-effect waves-light blue darken-2 white-text" + document.querySelector('.hidden.btn.waves-effect.waves-light.blue.darken-2.white-text').className = 'btn.waves-effect.waves-light.blue.darken-2.white-text'; + + //Find the div with the class btn blue darken-2 waves-effect waves-light white-text hidden and make it btn blue darken-2 waves-effect waves-light white-text + document.querySelector('.btn.blue.darken-2.waves-effect.waves-light.white-text.hidden').className = 'btn.blue.darken-2.waves-effect.waves-light.white-text'; + //Remove the disabled attribute from the div element above + document.querySelector('.btn.blue.darken-2.waves-effect.waves-light.white-text').removeAttribute('disabled'); +//Wait until the page is changed + window.addEventListener('load', () => { + //The url will look like this https://get-click2.blogspot.com/2019/01/0xc60f.live?m=1 + //return the part of the url that is after the date, in this case it is 0xc60f.live?m=1 + let url = location.href.replace(/.*\/\d+\/(.*)/, '$1'); + this.helpers.safelyNavigate(url); + }); + } +} + +export const matches = ['get-click2.blogspot.com/']; diff --git a/src/bypasses/getwallpapers.js b/src/bypasses/getwallpapers.js new file mode 100644 index 000000000..6912beb12 --- /dev/null +++ b/src/bypasses/getwallpapers.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Getwallpapers extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + window.setInterval = f => setInterval(f, 1) + } +} + +export const matches = ['getwallpapers.com', 'https://sammobile.com', "https://ydfile.com", "https://mobilemodsapk.com", "https://dlandroid.com", "https://download.modsofapk.com", "https://zedge.net ", "https://fex.net", "https://k2s.cc", " https://u.to"]; diff --git a/src/bypasses/idnation.js b/src/bypasses/idnation.js new file mode 100644 index 000000000..d2da7fb53 --- /dev/null +++ b/src/bypasses/idnation.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Idnation extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + this.helpers.ifElement("#linko[href]", b => this.helpers.safelyNavigate(b.href)) + } +} + +export const matches = ['idnation.net']; diff --git a/src/bypasses/indishare.js b/src/bypasses/indishare.js new file mode 100644 index 000000000..279365a21 --- /dev/null +++ b/src/bypasses/indishare.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Indishare extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + window.setTimeout = f => setTimeout(f, 1) + } +} + +export const matches = ['indishare.org', 'solvetube.site']; diff --git a/src/bypasses/lkc21.js b/src/bypasses/lkc21.js new file mode 100644 index 000000000..4d7ad5cd6 --- /dev/null +++ b/src/bypasses/lkc21.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Lkc21 extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + window.setTimeout = f => setTimeout(f, 100) + } +} + +export const matches = ['lkc21.net']; diff --git a/src/bypasses/lnk2.js b/src/bypasses/lnk2.js new file mode 100644 index 000000000..99c684166 --- /dev/null +++ b/src/bypasses/lnk2.js @@ -0,0 +1,20 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Lnk2 extends BypassDefinition { + constructor() { + super(); + this.ensure_dom = true; + } + + execute() { + //If the url doesn't contain /go/, use the insertInfoBox + if (window.location.href.includes('/go/')) { + document.getElementById('getLink').removeAttribute('disabled'); + document.getElementById('getLink').click(); + } else { + this.helpers.insertInfoBox('Please complete the captcha, then we can bypass you'); + } + } +} + +export const matches = ['lnk2.cc']; diff --git a/src/bypasses/manualsbooks.js b/src/bypasses/manualsbooks.js new file mode 100644 index 000000000..b5bc026a6 --- /dev/null +++ b/src/bypasses/manualsbooks.js @@ -0,0 +1,17 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Manualsbooks extends BypassDefinition { + constructor() { + super(); + } + + execute() { + // Wait for 2 seconds + + newElement.parentNode.replaceChild(downloadButton, newElement), clearInterval(id); + this.helpers.safelyNavigate(this.helpers.parseTarget(document.getElementById("download"))) + + } +} + +export const matches = ['manualsbooks.com'] diff --git a/src/bypasses/onelink.js b/src/bypasses/onelink.js new file mode 100644 index 000000000..3d783d5db --- /dev/null +++ b/src/bypasses/onelink.js @@ -0,0 +1,20 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Onelink extends BypassDefinition { + constructor() { + super(); + this.ensure_dom = true; + } + + execute() { + window.setInterval = f => setInterval(f, 1); + let b = document.getElementById('go_next'); + if (b && this.helpers.isGoodLink(b.href)) { + this.helpers.safelyAssign(b.href); + } else { + this.helpers.ifElement('#download', b => this.helpers.safelyNavigate(b.href)); + } + } +} + +export const matches = ['1link.club']; diff --git a/src/bypasses/portableapps.js b/src/bypasses/portableapps.js new file mode 100644 index 000000000..f5a34a074 --- /dev/null +++ b/src/bypasses/portableapps.js @@ -0,0 +1,22 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Portableapps extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + //Check if the url contains "/downloading" + if (window.location.href.indexOf('/downloading') > -1) { + let url = window.location.href; + let urlSplit = url.split('&f='); + let urlSplit2 = urlSplit[1].split('&'); + let finalUrl = "https://download2.portableapps.com/portableapps/PortableApps.comPlatform/" + urlSplit2 + //Open finalUrl in a new tab + window.open(finalUrl, '_blank'); + } + } +} + +export const matches = ['portableapps.com']; diff --git a/src/bypasses/ryn.js b/src/bypasses/ryn.js new file mode 100644 index 000000000..85bf1933f --- /dev/null +++ b/src/bypasses/ryn.js @@ -0,0 +1,18 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Ryn extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + if (typeof countdown == 'function') { + document.write(''); + countdown(); + this.helpers.safelyNavigate(document.querySelector('#link > a').href); + } + } +} + +export const matches = ['ryn.cc']; diff --git a/src/bypasses/softpedia.js b/src/bypasses/softpedia.js new file mode 100644 index 000000000..d4970a1ad --- /dev/null +++ b/src/bypasses/softpedia.js @@ -0,0 +1,19 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Softpedia extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + this.helpers.ifElement('meta[http-equiv=\'refresh\'][content]', m => { + let c = m.content.replace('; url=', ';url='); + if (c.indexOf(';url=') > -1) { + this.helpers.safelyAssign(c.split(';url=')[1]); + } + }); + } +} + +export const matches = ['softpedia.com']; diff --git a/src/bypasses/spaste.js b/src/bypasses/spaste.js new file mode 100644 index 000000000..0e42615b2 --- /dev/null +++ b/src/bypasses/spaste.js @@ -0,0 +1,25 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Spaste extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + this.helpers.insertInfoBox('Please complete the captcha to continue'); + const doTheThing = f => setTimeout(() => { + let item = document.querySelector('#currentCapQue').textContent; + document.querySelectorAll('.markAnswer').forEach(as => { + if (as.querySelector('img').getAttribute('src').toLowerCase().indexOf(item) > -1) { + as.click(); + } + }); + f(); + }, 200); + document.querySelector('#captchaVerifiedStatus').click(); + doTheThing(() => doTheThing(() => doTheThing(() => document.querySelector('#template-contactform-submit').click()))); + } +} + +export const matches = ['spaste.com/s', 'spaste.com/site']; diff --git a/src/bypasses/tlgd.js b/src/bypasses/tlgd.js new file mode 100644 index 000000000..e61d4d48b --- /dev/null +++ b/src/bypasses/tlgd.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Tlgd extends BypassDefinition { + constructor() { + super(); + this.ensure_dom = true + } + + execute() { + this.helpers.safelyAssign("http://www.twitlonger.com/show" + location.pathname) + } +} + +export const matches = ['tl.gd']; diff --git a/src/bypasses/uploadking.js b/src/bypasses/uploadking.js new file mode 100644 index 000000000..edf163af9 --- /dev/null +++ b/src/bypasses/uploadking.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Uploadking extends BypassDefinition { + constructor() { + super(); + this.ensure_dom = true + } + + execute() { + this.helpers.ifElement("form[name='F1']", f => this.helpers.countIt(() => f.submit())) + } +} + +export const matches = ['uploadking.net']; diff --git a/src/bypasses/ux9.js b/src/bypasses/ux9.js new file mode 100644 index 000000000..f8263003a --- /dev/null +++ b/src/bypasses/ux9.js @@ -0,0 +1,14 @@ +import BypassDefinition from './BypassDefinition.js'; + +export default class Ux9 extends BypassDefinition { + constructor() { + super(); + // custom bypass required bases can be set here + } + + execute() { + this.helpers.safelyAssign(window.ux_secure.fullUrl); + } +} + +export const matches = ['ux9.de']; diff --git a/src/html/consent.html b/src/html/consent.html index c789066c6..35c5128af 100644 --- a/src/html/consent.html +++ b/src/html/consent.html @@ -18,11 +18,11 @@ } p { color: #ffffff; - font-size: 28px; + font-size: 16px; line-height: 1.5; } .buttons { - border: none; /* Increase border width for better visibility */ + border: 4px; /* Increase border width for better visibility */ border-image: linear-gradient( 135deg, var(--ff-aqua) 0%, @@ -50,7 +50,7 @@

Thank you for installing FastForward

- This extension collects the tab url when you choose to add a website to the whitelist, and temporarily processes the url of bypassed sites while navigating them. + This extension collects the tab url when you choose to add a website to the whitelist, and temporarily processes the url of bypassed sites while navigating them. This data never leaves your device.

Additionally, if the "Crowd Bypass" option is enabled, the extension may send some bypassed urls to our server to be processed and added to our database in accordance with our privacy policy. They are never shared. diff --git a/src/html/consent.js b/src/html/consent.js index 2d284ce8d..35bb6f35c 100644 --- a/src/html/consent.js +++ b/src/html/consent.js @@ -16,7 +16,7 @@ async function getConsentStatus() { document.querySelector('#agree').addEventListener('click', async function () { console.log("Agree button clicked."); await saveConsentStatus('consent-granted'); - window.location.href = 'options.html'; + window.close(); }); // Event listener for "Refuse" button diff --git a/src/html/options.html b/src/html/options.html index b99cf942c..d528517ea 100644 --- a/src/html/options.html +++ b/src/html/options.html @@ -29,30 +29,75 @@

- -
- - - ()
- -
- -
- - - () - ()
- -
- - +
+ + +

+
+

+
+ + () +

+
+
+ + +

+
+

+
+ + +

+
+

+
+ + () +

+ + () + +
+
+ + +

+
+
+ + +

+

-

+

-
+

diff --git a/src/html/options.js b/src/html/options.js index 6330963f8..746e38ee6 100644 --- a/src/html/options.js +++ b/src/html/options.js @@ -11,7 +11,7 @@ let defaultOptions = { optionCrowdCloseDelayToggle: false, optionCrowdCloseDelay: 15, displayContributeBanner: true, - whitelist: '', + whitelist: 'example.com\n*.example.com', }; const crowdTempDisabledMessage = document.querySelector( diff --git a/src/html/popup.html b/src/html/popup.html index 562031038..c81f3898b 100644 --- a/src/html/popup.html +++ b/src/html/popup.html @@ -12,9 +12,6 @@
diff --git a/src/html/style.css b/src/html/style.css index f1b1f0fa1..786364b0b 100644 --- a/src/html/style.css +++ b/src/html/style.css @@ -395,17 +395,22 @@ a .link-preview:hover { #contribute { position: absolute; /* display: none; */ - width: calc(100vw - 6px); + width: 60%; background-image: linear-gradient(135deg, var(--ff-aqua) 0%, var(--ff-blue) 50%, var(--ff-purple) 100%); padding: 3px; padding-left: 6px; + margin: auto; margin-top: 5px; z-index: 1; + left: 50%; + transform: translateX(-50%); + border-radius: 10px; } #contribute p { width: fit-content; display: inline-block; + margin-left: 20px; } #contribute a { @@ -416,9 +421,9 @@ a .link-preview:hover { cursor: pointer; width: fit-content; float: right; - vertical-align: top; - margin-right: 6px; - font-size: 20px; + vertical-align: middle; + margin-right: 20px; + font-size: 40px; } #close:after { @@ -429,4 +434,13 @@ a .link-preview:hover { #whitelistDesc { margin-left: 27.055px; margin-right: 27.055px; -} \ No newline at end of file +} +.option p { + color: #7a7a7a; +} +.indentedOption { + margin-left: 54.11px; +} +.indentedOption p { + color: #7a7a7a; +} diff --git a/src/js/background.js b/src/js/background.js index 58d62ab07..7d1601965 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -12,189 +12,200 @@ if (isFirefox) { browser.tabs.create({ url: 'html/consent.html', }); - } else { - // If consent has been already granted, execute background script - executeBackgroundScript(); + return; } }); + return; } }); -} else { - // For non-Firefox browsers, execute background script - executeBackgroundScript(); } -function executeBackgroundScript() { - const brws = typeof browser !== 'undefined' ? browser : chrome; - const fetchDomains = ['crowd.fastforward.team', 'redirect-api.work.ink']; //only allow requests to these domains +const brws = typeof browser !== 'undefined' ? browser : chrome; +const fetchDomains = ['crowd.fastforward.team', 'redirect-api.work.ink']; //only allow requests to these domains - async function getOptions() { - return new Promise((resolve) => { - brws.storage.local.get('options').then((result) => { - resolve(result.options); - }); +async function getOptions() { + return new Promise((resolve) => { + brws.storage.local.get('options').then((result) => { + resolve(result.options); }); - } + }); +} - function ffclipboardClear() { - brws.storage.local.set({ ff_clipboard: '{}' }); - } +function ffclipboardClear() { + brws.storage.local.set({ ff_clipboard: '{}' }); +} - function clearCrowdIgnoredURLs() { - brws.storage.local.set({ crowd_ignore: '{}' }); - } +function clearCrowdIgnoredURLs() { + brws.storage.local.set({ crowd_ignore: '{}' }); +} - function firstrun(details) { - if (details.reason == 'install' || details.reason == 'update') { - brws.tabs.create({ url: 'https://fastforward.team/firstrun' }); - ffclipboardClear(); - brws.storage.local.set({ tempDisableCrowd: 'false' }); - brws.storage.local.set({ version: brws.runtime.getManifest().version }); - brws.runtime.openOptionsPage(); //required for loading default options, to do: implement a better way - brws.declarativeNetRequest.updateDynamicRules({ - addRules: constants.beforeNavigateRules, - removeRuleIds: constants.beforeNavigateRules.map((rule) => rule.id), - }); - } +function firstrun(details) { + if (details.reason == 'install' || details.reason == 'update') { + brws.tabs.create({ url: 'https://fastforward.team/firstrun' }); + ffclipboardClear(); + brws.storage.local.set({ tempDisableCrowd: 'false' }); + brws.storage.local.set({ version: brws.runtime.getManifest().version }); + brws.runtime.openOptionsPage(); //required for loading default options, to do: implement a better way + brws.declarativeNetRequest.updateDynamicRules({ + addRules: constants.beforeNavigateRules, + removeRuleIds: constants.beforeNavigateRules.map((rule) => rule.id), + }); } +} - function preflight(details) { - let url = new URL(details.url); - if (url.hostname !== 'fastforward.team') { - return; - } - //navigate - if (url.pathname === '/bypassed') { - let ext_url = new URL(brws.runtime.getURL('')); - url.hostname = ext_url.hostname; - url.protocol = ext_url.protocol; - url.pathname = '/html' + url.pathname; - if (url.searchParams.get('crowd') === 'true') { - url.pathname = - url.pathname.split('/').slice(0, -1).join('/') + - '/crowd-bypassed.html'; - } else { - url.pathname = - url.pathname.split('/').slice(0, -1).join('/') + - '/before-navigate.html'; - } - - brws.tabs.update(details.tabId, { - url: url.href, - }); - } +function preflight(details) { + let url = new URL(details.url); + if (url.hostname !== 'fastforward.team') { + return; } + //navigate + if (url.pathname === '/bypassed') { + let ext_url = new URL(brws.runtime.getURL('')); + url.hostname = ext_url.hostname; + url.protocol = ext_url.protocol; + url.pathname = '/html' + url.pathname; + if (url.searchParams.get('crowd') === 'true') { + url.pathname = + url.pathname.split('/').slice(0, -1).join('/') + + '/crowd-bypassed.html'; + } else { + url.pathname = + url.pathname.split('/').slice(0, -1).join('/') + + '/before-navigate.html'; + } - function reEnableCrowdBypassStartup() { - brws.storage.local.get(['tempDisableCrowd']).then((result) => { - if (result.tempDisableCrowd === 'true') { - brws.storage.local.get(['options']).then((result) => { - let opt = result.options; - opt.optionCrowdBypass = true; - brws.storage.local.set({ options: opt }); - }); - } - brws.storage.local.set({ tempDisableCrowd: 'false' }); + brws.tabs.update(details.tabId, { + url: url.href, }); } +} - brws.alarms.onAlarm.addListener((alarm) => { - brws.storage.local.get(['tempDisableCrowd']).then((result) => { - if ( - alarm.name === 'enableCrowdBypass' && - result.tempDisableCrowd === 'true' - ) { - brws.storage.local.get(['options']).then((result) => { - let opt = result.options; - opt.optionCrowdBypass = true; - brws.storage.local.set({ options: opt }); - brws.storage.local.set({ tempDisableCrowd: 'false' }); - }); - } - }); +function reEnableCrowdBypassStartup() { + brws.storage.local.get(['tempDisableCrowd']).then((result) => { + if (result.tempDisableCrowd === 'true') { + brws.storage.local.get(['options']).then((result) => { + let opt = result.options; + opt.optionCrowdBypass = true; + brws.storage.local.set({ options: opt }); + }); + } + brws.storage.local.set({ tempDisableCrowd: 'false' }); }); +} - brws.runtime.onInstalled.addListener(firstrun); - brws.runtime.onStartup.addListener(() => { - ffclipboardClear(); - clearCrowdIgnoredURLs(); - reEnableCrowdBypassStartup(); - brws.storage.local.set({ version: brws.runtime.getManifest().version }); +brws.alarms.onAlarm.addListener((alarm) => { + brws.storage.local.get(['tempDisableCrowd']).then((result) => { + if ( + alarm.name === 'enableCrowdBypass' && + result.tempDisableCrowd === 'true' + ) { + brws.storage.local.get(['options']).then((result) => { + let opt = result.options; + opt.optionCrowdBypass = true; + brws.storage.local.set({ options: opt }); + brws.storage.local.set({ tempDisableCrowd: 'false' }); + }); + } }); - - brws.runtime.onMessage.addListener((request, _, sendResponse) => { - (async () => { - let options = await getOptions(); - if (options.optionCrowdBypass === false) { - return; +}); + +brws.runtime.onInstalled.addListener(firstrun); +brws.runtime.onStartup.addListener(() => { + ffclipboardClear(); + clearCrowdIgnoredURLs(); + reEnableCrowdBypassStartup(); + brws.storage.local.set({ version: brws.runtime.getManifest().version }); +}); + +brws.webRequest.onBeforeSendHeaders.addListener( + function (details) { + var headers = details.requestHeaders; + headers = headers.map(function (x) { + if (x.name === "User-Agent") { + x.value = ""; + return x; + } else { + return x; } - let url; - request.type === 'crowdQuery' - ? (url = 'https://crowd.fastforward.team/crowd/query_v1') - : (url = 'https://crowd.fastforward.team/crowd/contribute_v1'); + }); + return { requestHeaders: headers }; + }, + requestFilter, + ["requestHeaders", "blocking"] +); +brws.runtime.onMessage.addListener((request, _, sendResponse) => { + (async () => { + let options = await getOptions(); + if (options.optionCrowdBypass === false) { + return; + } + let url; + request.type === 'crowdQuery' + ? (url = 'https://crowd.fastforward.team/crowd/query_v1') + : (url = 'https://crowd.fastforward.team/crowd/contribute_v1'); - let params = new URLSearchParams(); + let params = new URLSearchParams(); - if (request.type !== 'followAndContribute') { - for (let key in request.detail) { - params.append(key, request.detail[key]); - } - } else { - for (let key in request.detail) { - if (key === 'target') { - let dest = new URL(request.detail[key]); - if (!fetchDomains.includes(dest.hostname)) { - return; - } - let res = await fetch(dest.href, { - method: 'GET', - redirect: 'follow', - }); - params.append(key, res.url); - } else { - params.append(key, request.detail[key]); + if (request.type !== 'followAndContribute') { + for (let key in request.detail) { + params.append(key, request.detail[key]); + } + } else { + for (let key in request.detail) { + if (key === 'target') { + let dest = new URL(request.detail[key]); + if (!fetchDomains.includes(dest.hostname)) { + return; } + let res = await fetch(dest.href, { + method: 'GET', + redirect: 'follow', + }); + params.append(key, res.url); + } else { + params.append(key, request.detail[key]); } } + } - let response = await fetch(url, { - method: 'POST', - body: params.toString(), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, + let response = await fetch(url, { + method: 'POST', + body: params.toString(), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }); + if (request.type === 'crowdQuery') + response.text().then((res) => { + sendResponse(res); }); - if (request.type === 'crowdQuery') - response.text().then((res) => { - sendResponse(res); - }); - })(); - return true; - }); + })(); + return true; +}); - brws.storage.onChanged.addListener(() => { - getOptions().then((options) => { - if (typeof options === 'undefined') { - return; - } - if (options.optionBlockIpLoggers === false) { - brws.declarativeNetRequest.updateEnabledRulesets({ - disableRulesetIds: ['ipLoggerRuleset'], - }); - } else { - brws.declarativeNetRequest.updateEnabledRulesets({ - enableRulesetIds: ['ipLoggerRuleset'], - }); - } - if (options.optionTrackerBypass === false) { - brws.declarativeNetRequest.updateEnabledRulesets({ - disableRulesetIds: ['trackerRuleset'], - }); - } else { - brws.declarativeNetRequest.updateEnabledRulesets({ - enableRulesetIds: ['trackerRuleset'], - }); - } - }); +brws.storage.onChanged.addListener(() => { + getOptions().then((options) => { + if (typeof options === 'undefined') { + return; + } + if (options.optionBlockIpLoggers === false) { + brws.declarativeNetRequest.updateEnabledRulesets({ + disableRulesetIds: ['ipLoggerRuleset'], + }); + } else { + brws.declarativeNetRequest.updateEnabledRulesets({ + enableRulesetIds: ['ipLoggerRuleset'], + }); + } + if (options.optionTrackerBypass === false) { + brws.declarativeNetRequest.updateEnabledRulesets({ + disableRulesetIds: ['trackerRuleset'], + }); + } else { + brws.declarativeNetRequest.updateEnabledRulesets({ + enableRulesetIds: ['trackerRuleset'], + }); + } }); -} +}); +