diff --git a/packages/targets/browser-edge/src/index.ts b/packages/targets/browser-edge/src/index.ts index bc4ed283..c990d47b 100644 --- a/packages/targets/browser-edge/src/index.ts +++ b/packages/targets/browser-edge/src/index.ts @@ -2,6 +2,10 @@ import { defineTarget, exec, manualSetup } from '@profullstack/sh1pt-core'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { isAbsolute, join } from 'node:path'; +function safeFileStem(value: string): string { + return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'edge-ext'; +} + interface Config { productId: string; // Edge Partner Center product ID sourceDir?: string; // defaults to "dist/" @@ -14,7 +18,7 @@ function sourceDir(ctx: { projectDir: string }, config: Config): string { } function packageArtifact(ctx: { outDir: string; version: string }, config: Config): string { - return join(ctx.outDir, `${config.productId}-${ctx.version}.zip`); + return join(ctx.outDir, `${safeFileStem(config.productId)}-${ctx.version}.zip`); } function packagePlan(ctx: { projectDir: string; outDir: string; version: string }, config: Config) { diff --git a/packages/targets/chat-telegram/src/index.ts b/packages/targets/chat-telegram/src/index.ts index f96bbed6..6f642eba 100644 --- a/packages/targets/chat-telegram/src/index.ts +++ b/packages/targets/chat-telegram/src/index.ts @@ -31,7 +31,7 @@ export default defineTarget({ label: 'Telegram Bot', async build(ctx, config) { ctx.log(`telegram · prepare bot manifest for @${config.botUsername}`); - return { artifact: `${ctx.outDir}/telegram-${config.botUsername}.json` }; + return { artifact: `${ctx.outDir}/telegram-${safeFileStem(config.botUsername)}.json` }; }, async ship(ctx, config) { const username = normalizeUsername(config.botUsername); @@ -98,6 +98,10 @@ async function callTelegram( return data.result; } +function safeFileStem(value: string): string { + return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'telegram-bot'; +} + function normalizeUsername(username: string): string { const clean = username.replace(/^@/, '').trim(); if (!clean) throw new Error('botUsername is required'); diff --git a/packages/targets/mobile-android/src/index.ts b/packages/targets/mobile-android/src/index.ts index f1e15e54..1732b0a3 100644 --- a/packages/targets/mobile-android/src/index.ts +++ b/packages/targets/mobile-android/src/index.ts @@ -5,6 +5,10 @@ interface Config { track?: 'internal' | 'alpha' | 'beta' | 'production'; } +function safeFileStem(value: string): string { + return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'android-app'; +} + export default defineTarget({ id: 'mobile-android', kind: 'mobile', @@ -12,7 +16,7 @@ export default defineTarget({ async build(ctx, config) { ctx.log(`build Android AAB for ${config.packageName} v${ctx.version}`); // TODO: run Gradle bundleRelease to produce signed .aab - return { artifact: `${ctx.outDir}/${config.packageName}-${ctx.version}.aab` }; + return { artifact: `${ctx.outDir}/${safeFileStem(config.packageName)}-${ctx.version}.aab` }; }, async ship(ctx, config) { const track = config.track ?? 'internal'; diff --git a/packages/targets/pkg-flatpak/src/index.ts b/packages/targets/pkg-flatpak/src/index.ts index 97a928ba..3e5b4e92 100644 --- a/packages/targets/pkg-flatpak/src/index.ts +++ b/packages/targets/pkg-flatpak/src/index.ts @@ -33,12 +33,6 @@ function renderList(values: string[], indent: string): string[] { * each non-empty and containing only alphanumeric characters or hyphens/underscores. * Must not contain path traversal characters. */ -function validateAppId(appId: string): void { - if (!appId || typeof appId !== 'string') { - throw new Error('pkg-flatpak: appId is required'); - } - // Block path traversal - if (appId.includes('/') || appId.includes('\\') || appId.includes('..')) { throw new Error(`pkg-flatpak: invalid appId "${appId}" — must not contain path separators or ".." sequences`); } const segments = appId.split('.'); diff --git a/packages/targets/pkg-snap/src/index.ts b/packages/targets/pkg-snap/src/index.ts index 5504dfb8..b452675b 100644 --- a/packages/targets/pkg-snap/src/index.ts +++ b/packages/targets/pkg-snap/src/index.ts @@ -38,13 +38,6 @@ function renderDescription(description: string): string[] { * Rules: lowercase letters, digits, and hyphens only; at least one letter; * no leading or trailing hyphen; max 40 characters. */ -function validateSnapName(snapName: string): void { - if (!snapName || typeof snapName !== 'string') { - throw new Error('pkg-snap: snapName is required'); - } - if (snapName.length > 40) { - throw new Error(`pkg-snap: snapName "${snapName}" exceeds 40 characters`); - } if (snapName.startsWith('-') || snapName.endsWith('-')) { throw new Error(`pkg-snap: snapName "${snapName}" must not start or end with a hyphen`); } diff --git a/packages/targets/pkg-winget/src/index.ts b/packages/targets/pkg-winget/src/index.ts index 2893cc29..3998adf7 100644 --- a/packages/targets/pkg-winget/src/index.ts +++ b/packages/targets/pkg-winget/src/index.ts @@ -29,13 +29,6 @@ function yamlString(value: string): string { * Must follow "Publisher.PackageName" format — at least one dot, no leading/trailing dot, * each segment non-empty and containing only alphanumeric characters, hyphens, or underscores. */ -function validatePackageId(packageId: string): void { - if (!packageId || typeof packageId !== 'string') { - throw new Error('winget: packageId is required'); - } - if (packageId.startsWith('.') || packageId.endsWith('.')) { - throw new Error(`winget: invalid packageId "${packageId}" — must not start or end with a dot`); - } const segments = packageId.split('.'); if (segments.length < 2) { throw new Error(`winget: invalid packageId "${packageId}" — must use Publisher.Package format (e.g. "Microsoft.VSCode")`);