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
26 changes: 8 additions & 18 deletions builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import * as ESBuild from 'esbuild'
import * as Zod from 'zod'
import PackageJson from '@npmcli/package-json'
import * as Semver from 'semver'
import * as TsMorph from 'ts-morph'
import * as Fs from 'node:fs'
import * as NodeHttps from 'node:https'
import * as Process from 'node:process'
Expand Down Expand Up @@ -79,35 +78,28 @@ for (const SellerEntry of IABSellersJsonData.sellers) {
}
console.log('Collected', DomainsList.size, 'unique domains from IAB Sellers.json')

const IndexFilePath = `./sources/src/#generated-${crypto.randomUUID()}.ts`
Fs.copyFileSync('./sources/src/index.ts', IndexFilePath)
Fs.chmodSync(IndexFilePath, 0o700)
const Project = new TsMorph.Project({ tsConfigFilePath: './tsconfig.json' })
const IndexFile = Project.getSourceFileOrThrow(IndexFilePath)
const TargetedDomainsArray = IndexFile.getVariableDeclaration('TargetedDomains').getInitializerIfKindOrThrow(TsMorph.SyntaxKind.ArrayLiteralExpression)
for (const Domain of DomainsList) {
TargetedDomainsArray.addElement(`'${Domain}'`)
}
await Project.save()
console.log('Updated targeted domains in', IndexFilePath, '; total domains:', DomainsList.size)

const HeaderLocation = './sources/banner.txt'
let ConvertedHeader: string = ''
for (const Line of Fs.readFileSync(HeaderLocation, 'utf-8').split('\n')) {
if (Line.includes('%%VERSION_VALUE%%')) {
ConvertedHeader += Line.replaceAll('%%VERSION_VALUE%%', Version) + '\n'
} else if (Line.includes('%%NAME%%')) {
ConvertedHeader += Line.replaceAll('%%NAME%%', BuildType === 'production' ? 'tinyShield' : 'tinyShield (Development)') + '\n'
} else if (Line === '%%DOMAIN_INJECTION%%') {
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition Line === '%%DOMAIN_INJECTION%%' uses strict equality which will fail if the line contains any leading or trailing whitespace. Since the previous conditions use .includes(), this inconsistency could cause the placeholder to not be replaced if the banner.txt file has any whitespace around the placeholder. Consider using .includes('%%DOMAIN_INJECTION%%') for consistency or .trim() === '%%DOMAIN_INJECTION%%' to handle whitespace.

Suggested change
} else if (Line === '%%DOMAIN_INJECTION%%') {
} else if (Line.trim() === '%%DOMAIN_INJECTION%%') {

Copilot uses AI. Check for mistakes.
for (const DomainEntry of DomainsList) {
ConvertedHeader += `// @match *://${DomainEntry}/*\n`
ConvertedHeader += `// @match *://*.${DomainEntry}/*\n`
}
} else {
ConvertedHeader += Line + '\n'
}
}
console.log('Generated header with userscript name and version')
console.log('Generated header with domain injections and processing')
let AttachHeaderPath = `/tmp/${crypto.randomUUID()}`
Fs.writeFileSync(AttachHeaderPath, ConvertedHeader, { encoding: 'utf-8', mode: 0o700 })
console.log('Written temporary header file to:', AttachHeaderPath)
await ESBuild.build({
entryPoints: [IndexFilePath],
entryPoints: ['./sources/src/index.ts'],
bundle: true,
minify: BuildType === 'production',
define: {
Expand All @@ -122,6 +114,4 @@ await ESBuild.build({
})
console.log('Build completed')
Fs.rmSync(AttachHeaderPath)
console.log('Temporary header file removed')
Fs.rmSync(IndexFilePath)
console.log('Temporary source file removed')
console.log('Temporary header file removed')
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"esbuild": "^0.27.0",
"eslint": "^9.38.0",
"semver": "^7.7.3",
"ts-morph": "^27.0.2",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.2",
Expand Down
3 changes: 1 addition & 2 deletions sources/banner.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
// @version %%VERSION_VALUE%%
// @author PiQuark6046 and contributors
//
// @match https://*/*
// @match http://*/*
%%DOMAIN_INJECTION%%
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The %%DOMAIN_INJECTION%% placeholder is expanded at build time into @match directives using domains fetched from https://info.ad-shield.io/sellers.json in builder.ts, but those domain strings are only validated with new URL and then interpolated directly into the userscript header. Because this remote JSON is third-party and the domain field can contain characters such as newlines, a compromised or malicious endpoint can inject additional header directives (for example @require or overly broad @match rules) into the built script, effectively turning the sellers.json feed into a code-supply source for all users. To reduce this risk, strictly sanitize the remote domain values (e.g., restrict to hostname characters and derive the value from new URL(...).hostname or similar) before using them in @match lines so they cannot break out of the intended pattern.

Copilot uses AI. Check for mistakes.
//
// @description tinyShield allows AdGuard, uBlock Origin, Brave and ABP to resist against Ad-Shield quickly.
// @description:ko tinyShield는 AdGuard, uBlock Origin, Brave 와 ABP가 애드쉴드에 빠르게 저항할 수 있도록 합니다.
Expand Down