diff --git a/.github/workflows/purgecache.yml b/.github/workflows/purgecache.yml deleted file mode 100644 index fee53ea..0000000 --- a/.github/workflows/purgecache.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Purge jsDelivr cache - -on: - push: - branches: - - "main" - -jobs: - eslint: - name: Run ESLint - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Set up NodeJS LTS - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install dependencies - run: npm i - - name: Run ESLint - run: npm run lint - release: - name: Create a Release - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Set up NodeJS LTS - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Create a Release - uses: List-KR/semver-release@2.2.0 - needs: [eslint] - jsdelivrpurge: - name: Purge jsDelivr cache - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - steps: - - name: Set up NodeJS LTS - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Run jsDelivr-Purge - uses: List-KR/jsdelivr-purge@6.0.0 - needs: [eslint, release] diff --git a/package.json b/package.json index a0f4591..53e9e80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@list-kr/microshield", - "version": "4.10.7", + "version": "4.11.0", "description": "", "type": "module", "scripts": { @@ -14,7 +14,9 @@ "lint": "tsc --noEmit && eslint sources" }, "keywords": [], - "files": ["dist"], + "files": [ + "dist" + ], "author": { "name": "PiQuark6046", "email": "piquark6046@proton.me", @@ -44,6 +46,7 @@ "@list-kr/microshield-token-parser": "^2.0.3", "@types/node": "^22.5.4", "crypto-random-string": "^5.0.0", + "error-stack-parser": "^2.1.4", "parse-domain": "^8.2.2" }, "devDependencies": { diff --git a/sources/banner-ios-blocker.txt b/sources/banner-ios-blocker.txt index 1f7241e..e5ec8de 100644 --- a/sources/banner-ios-blocker.txt +++ b/sources/banner-ios-blocker.txt @@ -8,7 +8,7 @@ // @downloadURL https://cdn.jsdelivr.net/npm/@list-kr/microshield@latest/dist/microShield-ios-blocker.user.js // @license Apache-2.0 // -// @version 4.10.7 +// @version 4.11.0 // @author PiQuark6046 and contributors // // @match *://ygosu.com/* diff --git a/sources/banner-ios-recovery.txt b/sources/banner-ios-recovery.txt index a0a096c..3f8d443 100644 --- a/sources/banner-ios-recovery.txt +++ b/sources/banner-ios-recovery.txt @@ -8,7 +8,7 @@ // @downloadURL https://cdn.jsdelivr.net/npm/@list-kr/microshield@latest/dist/microShield-ios-recovery.user.js // @license Apache-2.0 // -// @version 4.10.7 +// @version 4.11.0 // @author PiQuark6046 and contributors // // @match *://ygosu.com/* diff --git a/sources/banner.txt b/sources/banner.txt index d287358..8c9d9e1 100644 --- a/sources/banner.txt +++ b/sources/banner.txt @@ -8,7 +8,7 @@ // @downloadURL https://cdn.jsdelivr.net/npm/@list-kr/microshield@latest/dist/microShield.user.js // @license Apache-2.0 // -// @version 4.10.7 +// @version 4.11.0 // @author PiQuark6046 and contributors // // @match *://ygosu.com/* diff --git a/sources/src/adshield/validators.ts b/sources/src/adshield/validators.ts index b593145..657fc52 100644 --- a/sources/src/adshield/validators.ts +++ b/sources/src/adshield/validators.ts @@ -92,7 +92,8 @@ export const AdshieldDomains = [ export const AdshieldKeywords = [ ...AdshieldDomains, 'failed to load website', - 'blocking software' + 'blocking software', + 'bad filtering rule' ] const AdshieldDomainsize = AdshieldHostableDomains.length diff --git a/sources/src/index-ios-blocker.ts b/sources/src/index-ios-blocker.ts index c45f267..98f4569 100644 --- a/sources/src/index-ios-blocker.ts +++ b/sources/src/index-ios-blocker.ts @@ -38,10 +38,11 @@ const Hook = () => { ProtectFunctionDescriptors(Win.document, 'createElement') ProtectFunctionDescriptors(Win.document, 'createElementNS') ProtectFunctionDescriptors(Win, 'alert', {CheckArguments: true}) - ProtectFunctionDescriptors(Win, 'confirm', {CheckArguments: true}) - ProtectFunctionDescriptors(Win, 'atob', {CheckOutputs: true}) - ProtectFunctionDescriptors(Win, 'decodeURI') - ProtectFunctionDescriptors(Win, 'decodeURIComponent') + ProtectFunctionDescriptors(Win, 'confirm', {CheckArguments: true, ReturnAs: 'Undefined'}) + ProtectFunctionDescriptors(Win, 'atob', {CheckOutputs: true, ReturnAs: 'error-report.com'}) + ProtectFunctionDescriptors(Win.location, 'reload') + ProtectFunctionDescriptors(Win, 'decodeURI', {ReturnAs: 'Undefined'}) + ProtectFunctionDescriptors(Win, 'decodeURIComponent', {ReturnAs: 'Undefined'}) ProtectFunctionDescriptors(Win.Promise.prototype, 'catch') ProtectFunctionDescriptors(Win.Promise, 'resolve') ProtectFunctionDescriptors(Win.console, 'log', {CheckErrorStack: ['jjang0u.com']}) diff --git a/sources/src/index.ts b/sources/src/index.ts index 6ce59a5..b0765ad 100644 --- a/sources/src/index.ts +++ b/sources/src/index.ts @@ -39,10 +39,11 @@ const Hook = () => { ProtectFunctionDescriptors(Win.document, 'createElement') ProtectFunctionDescriptors(Win.document, 'createElementNS') ProtectFunctionDescriptors(Win, 'alert', {CheckArguments: true}) - ProtectFunctionDescriptors(Win, 'confirm', {CheckArguments: true}) - ProtectFunctionDescriptors(Win, 'atob', {CheckOutputs: true}) - ProtectFunctionDescriptors(Win, 'decodeURI') - ProtectFunctionDescriptors(Win, 'decodeURIComponent') + ProtectFunctionDescriptors(Win, 'confirm', {CheckArguments: true, ReturnAs: 'Undefined'}) + ProtectFunctionDescriptors(Win, 'atob', {CheckOutputs: true, ReturnAs: 'error-report.com'}) + ProtectFunctionDescriptors(Win.location, 'reload') + ProtectFunctionDescriptors(Win, 'decodeURI', {ReturnAs: 'Undefined'}) + ProtectFunctionDescriptors(Win, 'decodeURIComponent', {ReturnAs: 'Undefined'}) ProtectFunctionDescriptors(Win.Promise.prototype, 'catch') ProtectFunctionDescriptors(Win.Promise, 'resolve') ProtectFunctionDescriptors(Win.console, 'log', {CheckErrorStack: ['jjang0u.com']}) diff --git a/sources/src/utils/secret.ts b/sources/src/utils/secret.ts index 1c9f35d..170e48d 100644 --- a/sources/src/utils/secret.ts +++ b/sources/src/utils/secret.ts @@ -1,9 +1,11 @@ import cryptoRandomString from 'crypto-random-string' +import * as ErrorStackParser from 'error-stack-parser' import {AdshieldKeywords, IsAdShieldCall} from '../adshield/validators.js' import {Config} from '../config.js' import {GenerateCallStack} from './call-stack.js' import {CreateDebug} from './logger.js' import {HasSubstringSetsInString} from './string.js' +import {MatchSpecificSeq} from './sequence.js' type unsafeWindow = typeof window // eslint-disable-next-line @typescript-eslint/naming-convention @@ -27,24 +29,42 @@ export type ProtectedFunctionCreationOptions = Partial<{ CheckOutputs: boolean; // eslint-disable-next-line @typescript-eslint/no-explicit-any CheckArgumentFunctions: Array<(argArray: any[]) => boolean>; + ReturnAs: 'Banned' | 'Undefined' | string }> const PprintCall = (Name?: string, WasBlocked?: boolean, V?: unknown) => { Debug(WasBlocked ? '-' : '+', 'name=' + Name, 'v=', V, 'stack=', GenerateCallStack()) } +let StackTraces: ReturnType[] = [] + export const ProtectFunction = (F: F, Options: ProtectedFunctionCreationOptions) => new Proxy(F, { apply(Target, ThisArg, ArgArray) { + let ReturnAs: 'Banned' | 'Undefined' | 'Normal' | string = 'Normal' + const ErrorInstance = new Error() const E = () => { PprintCall(Options.Name, true, ArgArray) - throw new Error() + if (typeof Options.ReturnAs !== 'undefined' && + MatchSpecificSeq(ErrorStackParser.parse(ErrorInstance), [/[A-Za-z]{1,3}/, undefined, /[A-Za-z]{1,3}/, /Generator\./, /Generator\./])) { + ReturnAs = Options.ReturnAs + } else { + ReturnAs = 'Banned' + } + + if (ReturnAs === 'Banned') { + throw new Error() + } } if (IsAdShieldCall()) { E() } + if (StackTraces.some(StackTrace => JSON.stringify(ErrorStackParser.parse(ErrorInstance)) === JSON.stringify(StackTrace))) { + E() + } + if (Options.CheckArguments) { for (const Arg of ArgArray.filter(Arg => typeof Arg === 'string') as string[]) { if (HasSubstringSetsInString(Arg, AdshieldKeywords)) { @@ -93,7 +113,13 @@ export const ProtectFunction = (F: F, Options: ProtectedFunc PprintCall(Options.Name, false, ArgArray) } - return Reflect.apply(Target, ThisArg, ArgArray) as unknown + if (ReturnAs === 'Normal') { + return Reflect.apply(Target, ThisArg, ArgArray) + } else if (ReturnAs === 'Undefined') { + return undefined + } else { + return ReturnAs + } }, setPrototypeOf(Target, V) { PprintCall(Options.Name, true, V) diff --git a/sources/src/utils/sequence.ts b/sources/src/utils/sequence.ts new file mode 100644 index 0000000..c698e72 --- /dev/null +++ b/sources/src/utils/sequence.ts @@ -0,0 +1,10 @@ +import type * as ErrorStackParser from 'error-stack-parser' + +export function MatchSpecificSeq(StackFrames: ReturnType, Patterns: Array): boolean { + return StackFrames.some((StackFrame, I) => + Patterns.every((Pattern, J) => { + if (I + J >= StackFrames.length) return false + return Pattern === undefined || Pattern.test(typeof StackFrames[I + J].functionName !== 'undefined' ? StackFrames[I + J].functionName as string : '') + }) + ) +}