Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Package

on:
push:
branches: [ main ]
branches: [ main, prj-hatsune-miku ]

workflow_dispatch:

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"dependencies": {
"adm-zip": "^0.5.9"
}
"adm-zip": "^0.5.9",
"es-main": "^1.2.0"
},
"type": "module"
}
74 changes: 62 additions & 12 deletions scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@
* nover = Creates a package without version number
* ver = Creates a package with version number as specified in version.txt
*/
import { createRequire } from "module";
const require = createRequire(import.meta.url);

const fs = require('fs');
const path = require('path');

const distribution = 'build/dist';

let working_directory = process.cwd();

function set_working_directory (cwd) {
if (fs.existsSync(path.join(cwd, 'LICENSE')))

if (fs.existsSync(path.join(cwd, 'LICENSE'))) {
working_directory = cwd;
else
} else
set_working_directory(path.join(cwd, '..'));
}

set_working_directory(working_directory);

const distribution = `${working_directory}/build/dist`;
const args = process.argv;
args.shift();
args.shift()
Expand All @@ -35,13 +37,16 @@ const [build_type, versioning] = args;
console.log(`[FastForward.build] ${build_type} (${versioning ? versioning : 'dev'}): Creating package...`);

const builds = [];
import ff_builder from './build_js/firefox.js';
import chrm_builder from './build_js/chromium.js';

const builders = {
firefox: require('./build_js/firefox.js'),
chromium: require('./build_js/chromium.js')
firefox: ff_builder,
chromium: chrm_builder
};

if (fs.existsSync('build'))
fs.rmSync('build', {recursive: true});
if (fs.existsSync(`${working_directory}/build`))
fs.rmSync(`${working_directory}/build`, {recursive: true});

fs.mkdirSync(distribution, {recursive: true});

Expand Down Expand Up @@ -69,7 +74,7 @@ function copyFolderRecursiveSync( source, target ) {
}

async function run_build(type, commit_number) {
const destination = `build/FastForward.${type}`;
const destination = `${working_directory}/build/FastForward.${type}`;

fs.mkdirSync(destination, {recursive: true});

Expand All @@ -96,8 +101,14 @@ async function run_build(type, commit_number) {
console.log(`[FastForward.build.${type}] copying manifest to ${destination}`);
fs.copyFileSync(`${working_directory}/platform_spec/${type}/manifest.json`, `${destination}/manifest.json`);

console.log(`[FastForward.build.${type}] copying bypass files to ${destination}`)
copyFolderRecursiveSync(`${working_directory}/src/bypasses`, destination);

console.log(`[FastForward.build.${type}] copying helper files to ${destination}`)
copyFolderRecursiveSync(`${working_directory}/src/helpers`, destination);

console.log(`[FastForward.build.${type}] creating the manifest`);
const manifest_contents = require(`${working_directory}/${destination}/manifest.json`);
const manifest_contents = require(`${destination}/manifest.json`);
let version;
if (!versioning)
version = `0.${commit_number}.0`
Expand All @@ -108,7 +119,7 @@ async function run_build(type, commit_number) {

// replace windows OR linux style new lines if they are there
manifest_contents.version = version.replace(/\r\n/g, '').replace(/\n/g, '');
fs.writeFileSync(`${working_directory}/${destination}/manifest.json`, JSON.stringify(manifest_contents, null, 4));
fs.writeFileSync(`${destination}/manifest.json`, JSON.stringify(manifest_contents, null, 4));

await builders[type]({versioning, destination, commit_number, version: manifest_contents.version});
}
Expand All @@ -124,6 +135,45 @@ const {
} = require('child_process');

exec(`git rev-list HEAD --count`, async (error, stdout, stderr) => {
const bypasses = {};

for (const _ of fs.readdirSync(`${working_directory}/src/bypasses`)) {
if (_ === 'BypassDefinition.js') continue;

const bypass = await import(`file:///${working_directory}/src/bypasses/${_}`);
bypass.matches.map(match => { bypasses[match] = `bypasses/${_}`});
}

fs.writeFileSync(`${working_directory}/src/js/injection_script.js`, `const bypasses = ${JSON.stringify(bypasses)};

if (bypasses.hasOwnProperty(location.host)) {
const bypass_url = bypasses[location.host];

import(\`\${window.x8675309bp}\${bypass_url}\`).then(({default: bypass}) => {
import(\`\${window.x8675309bp}helpers/dom.js\`).then(({default: helpers}) => {
const bps = new bypass;
bps.set_helpers(helpers);
console.log('ensure_dom: %r', bps.ensure_dom);
if (bps.ensure_dom) {
let executed = false;
document.addEventListener('readystatechange', () => {
if (['interactive', 'complete'].includes(document.readyState) && !executed) {
executed = true;
bps.execute();
}
});
document.addEventListener("DOMContentLoaded",()=>{
if (!executed) {
executed = true;
bps.execute();
}
});
} else {
bps.execute();
}
});
});
}`)
for (const _ of builds)
await run_build(_, stdout.replace('\n', ''));
})
Expand Down
18 changes: 10 additions & 8 deletions scripts/build_js/chromium.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
if (require.main === module)
import esMain from "es-main";
if (esMain(import.meta))
throw new Error('This file is a build module, run the build.js file to actually build FastForward');

const zipper = require('adm-zip');
import zipper from "adm-zip";
import * as path from "path";

module.exports = async function ({versioning, destination, commit_number, version} = {}) {
export default async function ({versioning, destination, commit_number, version} = {}) {
console.log(`[FastForward.build.chromium] building the Chromium package`);
const z = new zipper();

z.addLocalFolder(`${process.cwd()}/${destination}`);
z.addLocalFolder(destination);

let output_file = `${process.cwd()}/build/dist/`;
let output_file = path.join(destination, '..', 'dist');

if (!versioning)
output_file += `FastForward_chromium_${commit_number}_dev.crx`;
output_file += `/FastForward_chromium_${commit_number}_dev.crx`;
else if ('nover' === versioning)
output_file += `FastForward_chromium_0.${commit_number}.crx`;
output_file += `/FastForward_chromium_0.${commit_number}.crx`;
else
output_file += `FastForward_${version}_chromium.crx`;
output_file += `/FastForward_${version}_chromium.crx`;

await z.writeZipPromise(output_file, {});
console.log('[FastForward.build.chromium] Succesfull build the Chromium package: %s', output_file)
Expand Down
19 changes: 11 additions & 8 deletions scripts/build_js/firefox.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
if (require.main === module)
import esMain from "es-main";
if (esMain(import.meta))
throw new Error('This file is a build module, run the build.js file to actually build FastForward');

const zipper = require('adm-zip');
import zipper from 'adm-zip';
import * as path from "path";
// const zipper = require('adm-zip');

module.exports = async function ({versioning, destination, commit_number, version} = {}) {
export default async function ({versioning, destination, commit_number, version} = {}) {
console.log(`[FastForward.build.firefox] building the FireFox package`);
const z = new zipper();

z.addLocalFolder(`${process.cwd()}/${destination}`);
z.addLocalFolder(destination);

let output_file = `${process.cwd()}/build/dist/`;
let output_file = path.join(destination, '..', 'dist');

if (!versioning)
output_file += `FastForward_firefox_${commit_number}_dev.xpi`;
output_file += `/FastForward_firefox_${commit_number}_dev.xpi`;
else if ('nover' === versioning)
output_file += `FastForward_firefox_0.${commit_number}.xpi`;
output_file += `/FastForward_firefox_0.${commit_number}.xpi`;
else
output_file += `FastForward_${version}_firefox.xpi`;
output_file += `/FastForward_${version}_firefox.xpi`;

await z.writeZipPromise(output_file, {});
console.log('[FastForward.build.firefox] Succesfull build the FireFox package: %s', output_file)
Expand Down
13 changes: 13 additions & 0 deletions src/bypasses/BypassDefinition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const IMPLEMENTATION_ERROR = (function_name) => { throw new Error(`${function_name} must be implemented`); }

export default class BypassDefinition {
constructor() {
this.ensure_dom = false;
}

set_helpers (helpers) {
this.helpers = helpers;
}

execute() { IMPLEMENTATION_ERROR(); }
}
67 changes: 67 additions & 0 deletions src/bypasses/linkvertise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import BypassDefinition from './BypassDefinition.js';

export default class Linkvertise extends BypassDefinition {
constructor() {
super();
// custom bypass required bases can be set here
}

execute() {
if (window.location.href.toString().includes("?r=")) {
const urlParams = new URLSearchParams(window.location.search);
const r = urlParams.get('r')
this.helpers.safelyNavigate(atob(decodeURIComponent(r)));
}

const lvt_this = this;

const rawOpen = window.XMLHttpRequest.prototype.open;

window.XMLHttpRequest.prototype.open = function () {
this.addEventListener('load', data => {
if (data.currentTarget?.responseText?.includes('tokens')) {
const response = JSON.parse(data.currentTarget.responseText);

if (!response.data.valid)
return lvt_this.helpers.insertInfoBox('Please solve the captcha, afterwards we can immediately redirect you');

const target_token = response.data.tokens['TARGET'];
const ut = localStorage.getItem("X-LINKVERTISE-UT");
const linkvertise_link = location.pathname.replace(/\/[0-9]$/, "");


fetch(`https://publisher.linkvertise.com/api/v1/redirect/link/static${linkvertise_link}?X-Linkvertise-UT=${ut}`).then(r => r.json()).then(json => {
if (json?.data.link.target_type !== 'URL') {
return insertInfoBox('Due to copyright reasons we are not bypassing linkvertise stored content (paste, download etc)');
}
if (json?.data.link.id) {
const json_body = {
serial: btoa(JSON.stringify({
timestamp: new Date().getTime(),
random: "6548307",
link_id: json.data.link.id
})),
token: target_token
}
fetch(`https://publisher.linkvertise.com/api/v1/redirect/link${linkvertise_link}/target?X-Linkvertise-UT=${ut}`, {
method: "POST",
body: JSON.stringify(json_body),
headers: {
"Accept": 'application/json',
"Content-Type": 'application/json'
}
}).then(r => r.json()).then(json => {
if (json?.data.target) {
lvt_this.helpers.safelyNavigate(json.data.target)
}
})
}
})
}
});
rawOpen.apply(this, arguments);
}
}
}

export const matches = ['linkvertise.com', 'linkvertise.net', 'link-to.net'];
100 changes: 100 additions & 0 deletions src/helpers/dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
export function insertInfoBox(text) {
let infobox_container = document.querySelector('div#ffibv1');

if (!infobox_container) {
infobox_container = document.createElement('div');
infobox_container.setAttribute('id', 'ffibv1');
infobox_container.setAttribute('style', `
z-index: 99999999; position: fixed; bottom: 0; line-height:normal;
right: 0; padding: 20px; color:#111; font-size:21px;
font-family:-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;
max-width:500px; display: flex; flex-direction: column-reverse;
`);

document.body.appendChild(infobox_container);
}
const div = document.createElement("div");
div.style = 'margin-left:20px; margin-bottom: 20px;background:#eee;border-radius:10px;padding:20px;box-shadow:#111 0px 5px 40px;cursor:pointer';
div.innerHTML = `<img src="${window.x8675309bp}icon/48.png" style="width:24px;height:24px;margin-right:8px"><span style="display:inline"></span>`;
div.setAttribute("tabindex","-1");
div.setAttribute("aria-hidden","true");
const span = div.querySelector("span");
span.textContent=text;
div.onclick= () => infobox_container.removeChild(div);
infobox_container.appendChild(div);
setTimeout(()=>infobox_container.removeChild(div), 7000);
}

export function unsafelyNavigate(target, referer=null) {
//The background script will intercept the request and redirect to html/before-navigate.html or to the target depending on the user's settings.
let url = `https://fastforward.team/bypassed?target=${encodeURIComponent(target)}`;
if (referer)
url += `&referer=${encodeURIComponent(referer)}`;

switch(target)//All values here have been tested using "Take me to destinations after 0 seconds."
{
case (/(krnl\.place|hugegames\.io)/.exec(target)||{}).input:
url+="&safe_in=15"
break;
case (/(bowfile\.com)/.exec(target)||{}).input:
url+="&safe_in=20"
break;
}

location.assign(url);
}

export function parseTarget (target) {
return target instanceof HTMLAnchorElement ? target.href : target;
}

export function safelyNavigate(target, drophash) {
target = parseTarget(target)
if(!isGoodLink(target))
return false

// bypassed=true
let url = new URL(target)
if(!drophash&&(!url||!url.hash))
target+=location.hash

unsafelyNavigate(target)
return true
}

export function isGoodLink(link) {
if(typeof link !== "string" || link.split("#")[0] === location.href.split("#")[0])
{
return false
}
try
{
let u = new URL(decodeURI(link).trim().toLocaleLowerCase())
//check if host is a private/internal ip
if (u.hostname === 'localhost' || u.hostname === '[::1]' || /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/.test(u.hostname)) {
return false
}
const parts = u.hostname.split('.');
if (parts[0] === '10' || (parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) || (parts[0] === '192' && parts[1] === '168')) {
return false
}
// Check if protocol is safe
let safeProtocols = ["http:", "https:", "mailto:", "irc:", "telnet:", "tel:", "svn:"]
if (!safeProtocols.includes(u.protocol)) {
return false
}
}
catch(e)
{
return false
}
return true
}

export default {
insertInfoBox,
safelyNavigate,
unsafelyNavigate,
parseTarget,
isGoodLink
}
Loading