Skip to content

Releases: aelassas/servy

Servy 9.8

Choose a tag to compare

@aelassas aelassas released this 24 Aug 10:47

This release includes bug fixes, security patches, code quality improvements, and documentation updates. The full changelog is listed below.

Full Changelog

Click to expand release notes!
  • fix(security): Set-ServyExePermissions.ps1 - the -TargetAccount regex forbids spaces, so 11 of the 23 aliases in ServiceAccounts.cs are rejected, including the 'NT AUTHORITY\LocalService' and 'NT AUTHORITY\NetworkService' const values the product itself stores (#5479)
  • fix(core): ConfigParser.cs / ServiceInstallValidator.cs - Enum.TryParse ORs comma-separated names on non-Flags enums, so "RestartService, RestartProcess" silently installs as RestartComputer (#5507)
  • fix(core): ServiceManager.cs / LogonAsServiceGrant.cs - the EnsureLogOnAsServiceRight guard matches 1 of the 23 built-in account aliases ValidateCredentials accepts, so installing under .\NetworkService aborts with a Cannot resolve SID error (#5531)
  • fix(core): LogonAsServiceGrant.cs - HasLogonAsService only sees directly-assigned rights, so an account granted SeServiceLogonRight through a group is treated as lacking it and Ensure attempts a privileged LSA write (#5532)
  • fix(core): SecureData.cs - DecryptV2 discards the TryHashData result and verifies whatever is in the stackalloc buffer; Encrypt discards TryToBase64Chars and would persist a marker followed by NUL characters (#5542)
  • fix(core): SecureDataIntegrityException.cs / ServiceRepository.cs - two of the seven throw sites are policy refusals, not integrity failures, so an intact legacy record is shown to the user as corrupt and has its credential fields scrubbed (#5545)
  • fix(core): ServiceDependenciesValidator.cs - the name regex has no '+', so a load-order group dependency (+TDI) is rejected even though the SCM accepts one in lpDependencies (#5547)
  • fix(core): ServiceManager.cs - SetServiceDescription documents 'a null value removes the description', but a NULL lpDescription is Win32 for 'leave unchanged'; the empty string is what deletes it (#5554)
  • fix(core): Strings.resx / Helper.cs (Core) - Msg_InvalidServiceName is shown for four rejection branches but describes only the character one, so a service named CON is told to avoid nine characters it does not contain (sibling of #5169) (#5583)
  • fix(core): EnvironmentVariableParser.cs / StartOptionsParser.cs - 3 of 4 FormatException arms echo the whole KEY=VALUE record, and SafeParseEnvVars writes it unmasked to the service log (residual of #5029) (#5621)
  • fix(core): Strings.resx / ServiceDependenciesValidator.cs - the '+' load-order prefix added by #5547 reached the regex but none of the four documented character lists, so the rejection message still tells the user '+' is not allowed (#5650)
  • fix(infra): ServiceRepository.cs / SecureDataLegacyBlockedException.cs - the #5545 fix added the policy-refusal exception but no consumer branches on it, so an intact legacy record is still labelled corrupt and still scrubbed (#5568)
  • fix(infra): ServiceRepository.cs - UpsertBatchAsync preserves credentials from a SafeDecrypt-scrubbed row, so a Manager refresh writes NULL over the stored Password of any service whose secrets will not decrypt (#5572)
  • fix(infra): ServiceRepository.cs - SearchAsync is the only name lookup left on SQLite's ASCII-only LIKE, so the Manager search box cannot find a non-ASCII service name typed in the wrong case (#5573)
  • fix(infra): DatabaseInitializer.cs / SQLiteDbInitializer.cs - UNICODE_NOCASE is registered after connection.Open(), so a pooled connection fails every write to Services with 'no such collation sequence' (#5631)
  • fix(service): Servy fails to restart process (#5584)
  • fix(service): ProcessWrapper.cs / ProcessExtensions.cs - StopTree and StopDescendants leak the unvisited Process handles when the Toolhelp32 snapshot throws, and Service.cs swallows the fault without a log line (residual of #3770) (#5625)
  • fix(service): IServiceHelper.cs - LogStartupArguments' Security Note assigns masking to the logger, but all 8 masking calls are in the caller and every mask member is private/internal to ServiceHelper (#5627)
  • fix(service): ProcessLauncher.cs / EnvironmentVariableHelper.cs - PYTHONIOENCODING is on the ProtectedVariables denylist AND set by ApplyLanguageFixes, so the user's value is refused with a security warning and replaced (undoes #1141 for 1 of its 4 keys) (#5629)
  • fix(service): Service.cs - EvaluateExitOutcome's isHealthCheck term can never widen either condition, and the comment above it describes telemetry the code never collects (#5633)
  • fix(restarter): ServiceHelper.cs / Program.cs (Restarter) - the host kills Servy.Restarter.exe at 240s while RestartTimeoutSeconds accepts up to 86400, so a configured budget over 4 minutes aborts mid-restart and leaves the service stopped (#5618)
  • fix(ui): SplashWindow.xaml / AppBootstrapper.cs - the Topmost splash sits over the owner-less extraction-failure MessageBox, so startup blocks on a dialog hidden behind the splash (#5646)
  • fix(desktop): Strings.resx (Servy) / ServiceDto.cs - Info_PreLaunchTimeout says fire-and-forget disables logging, which it does not, and omits that the configured Pre-Launch Stdout/Stderr paths are silently ignored (#5651)
  • fix(desktop): Strings.resx (Servy) - Info_UserAccount omits the built-in and virtual service identities the Log On tab accepts, and the rule that their password fields must be left empty (#5653)
  • fix(desktop): ServiceCommands.cs (Servy) - OpenSecurityHardeningGuideAsync is the 1 of 31 message-box calls not awaited, so the error dialog no longer blocks the caller and a dispatcher fault is lost (#5658)
  • fix(manager): DependenciesViewModel.cs - LoadDependencyTreeAsync cancels its CTS but never passes the token to GetDependencies, so a superseded SCM walk runs to completion (#5560)
  • fix(manager): DependenciesViewModel.cs / ConsoleViewModel.cs - switching from a running service to a stopped one leaves the previous PID on screen; the #3313 latch cannot clear it and only PerformanceViewModel resets on selection (#5591)
  • fix(manager): MainViewModel.cs / LogsViewModel.cs - Dispose(bool) is virtual instead of override, so SearchableViewModelBase.Dispose never runs and its atomic guard stays 0 (#5593)
  • fix(cli): Msg_ServiceOperationFailed duplicates the service name - {1} is the action string, which already embeds it, so start/stop/restart/uninstall failures log "service 'X' for 'X'" (#5490)
  • fix(cli): the install --help paste reproduces the pre-#5547 --deps character set and the incomplete --user account list; the wiki documents load-order group dependencies nowhere (#5656)
  • fix(psm1): Servy.psm1 - the 32,000-character guard tells the user to shorten the environment variables and parameters, which are the only inputs that never reach the command line (#5492)
  • fix(psm1): Servy.psm1 - Invoke-ServyCli never re-checks HasExited after the poll loop, so a CLI that exits during the final sleep is reported as a timeout and a kill (#5493)
  • fix(notifications): ServyFailureEmail.ps1 - Server/From/To are read untrimmed while Port/UseSsl/TimeoutMs are trimmed, so an indented smtp-config.xml value is rejected or silently drops the alert (residual of #1626) (#5484)
  • fix(style): Format-SourceHygiene.ps1 - trims trailing whitespace from .md, which .editorconfig's [*.md] section sets to false; two trailing spaces are a Markdown hard break (residual of #5032) (#5475)
  • fix(style): Servy.CLI.IntegrationTests.csproj - the last of #4944's six BOM'd project files still carries a UTF-8 BOM, and Format-SourceHygiene.ps1 only strips it as a side effect of a whitespace fix the file does not need (#5476)
  • fix(setup): servy.iss / publish-sc.ps1 - the installer's [Files] paths hardcode Release and net10.0-windows, so build-config.ps1's BuildConfiguration key is honoured by the portable package but not by the installer (#5465)
  • fix(setup): servy.iss - the install path is substituted into the Task Scheduler XMLs unescaped, so an install directory containing '&' produces malformed task definitions (residual of #1345) (#5466)
  • fix(setup): servy.iss - AddToPath's presence check does not Trim/NormalizeFolder the existing PATH entries the way RemoveFromPath does, so an entry with a trailing backslash gets a duplicate appended (residual of #4989) (#5467)

Servy 9.7

Choose a tag to compare

@aelassas aelassas released this 21 Aug 17:18
  • feat(security): add Set-ServyExePermissions.ps1 to harden service binaries with explicit Read & Execute rights
  • feat(security): update service account documentation and UI to require Set-ServyExePermissions.ps1 hardening
  • fix(core): preserve file ACLs across atomic resource extractions in ResourceHelper.cs
  • fix(cli): align install command options sequence with core service schema
  • test: migrate test runner and coverage collection from VSTest to MTP
  • chore(deps): update dependencies
  • ci(publish.yml): signed exe injected into Resources without a matching pdb, leaving embedded debug symbols stale for the shipped binary (#5436)

Servy 9.6

Choose a tag to compare

@aelassas aelassas released this 19 Aug 07:41

This release includes UI/UX improvements for low and HD resolutions, bug fixes, security patches, code quality improvements, and documentation updates. The full changelog is listed below.

Full Changelog

Click to expand release notes!
  • fix(core): ResourceHelper.cs - log messages still say GetHostProcessLastWriteTimeUTC after the #4804 Utc rename (#5154)
  • fix(core): Logger.cs - Initialize(null) wipes the re-init baseline while the writer stays live, turning every setter into a silent no-op (#5155)
  • fix(core): ServiceMapper.cs - ToDomain silently mutates its input DTO (HydrateDefaults fills nulls in place), losing null-vs-default semantics the persistence layer relies on (#5157)
  • fix(core): NativeMethodsHelpers.cs - GetFileIdentity's string.Empty pre-initialization makes IsDifferentFrom's SAFE DEFAULT unreachable; a failed content probe reads as 'proven same' (regression of #1456) (#5160)
  • fix(core): Strings.resx (Servy.Core) - style drift between sibling messages: Pre-Launch/pre-launch casing, two range-message phrasings, terminal-punctuation flips, lone contraction (#5163)
  • fix(core): SecureData.cs - raw-legacy security block is swallowed by its own catch filter; Decrypt still returns ciphertext as plaintext (the #1371 fix never took effect) (#5167)
  • fix(core): Strings.resx / ServiceDependenciesValidator.cs - Msg_InvalidServiceDependencyName omits 'periods' again; the #1294 fix regressed when the sentence was rebuilt for #2600/#3388 (#5169)
  • fix(core): ServiceManager.cs - UninstallServiceAsync alone rethrows unexpected exceptions; Install/Start/Stop convert them to OperationResult.Failure (#5176)
  • fix(core): ServiceManager.cs - InstallServiceAsync's second IsServiceInstalled call drops the cancellation token its sibling at line 253 forwards (#5177)
  • fix(core): ServicePathValidator.cs - FindAllViolations reflects over typeof(T), not target.GetType(); a base-typed call silently validates nothing (#5181)
  • fix(core): ServiceValidationRules.cs - the CpuAffinity error is the only rule gated on 'errorMessage != null'; if the guard ever fired it would accept the invalid config silently (#5182)
  • fix(core): AffinityHelper.cs - comma-only affinity strings bypass validation and silently return IntPtr.Zero, while the hex branch rejects the same zero mask loudly (#5307)
  • fix(core): ServiceDependenciesValidator.cs - the length error omits the offending dependency name, the defect #1601 fixed in ServiceValidationRules before this check was written (#5341)
  • fix(core): ServiceManager.cs / ServiceHelper.cs - only the SCM-registering call site zeroes the pre-stop timeout when no pre-stop hook exists, so the runtime stop wait outlives the pre-shutdown deadline (residual of #4465) (#5367)
  • fix(core): ImportGuard.cs - the fileStream null guard is unreachable once IsValid is established, and Msg_ImportReadFailure is dead with it (#5370)
  • fix(infra): ServiceRepository.cs - HandleCorruptServiceDecryption's UI marker is written into the stored Description on the next save and stacks on every load/save cycle (#5186)
  • fix(service): ServiceHelper.cs - MaskRawArguments masks only the last token of an unquoted value, so the credential itself leaks whenever a positional argument follows it (#5223)
  • fix(service): ServiceHelper.cs - RestartService logs a non-zero restarter exit code at Info level; the failure case is the only path that reaches the line (#5226)
  • fix(service): ProcessWrapper.cs - SendCtrlC reads Marshal.GetLastWin32Error after process.HasExited has clobbered it, so attach failures classify on a stale error code (#5232)
  • fix(service): ProcessLauncher.cs / Service.cs - centralizing hook launches dropped the per-hook audit prefix; every hook's expansion warnings now read '[ProcessLauncher.Start]' (#5234)
  • fix(service): Service.cs - clean-exit path honors RecoveryOnCleanExit without the health-monitoring gate, leaving a zombie service that neither stops nor recovers (#5235)
  • fix(service): Service.cs - StartPreStopProcess's LogIssue still drops the Exception in the ignore-failure branch (the pre-stop half of #1484's All-fixed close) (#5236)
  • fix(service): Service.cs - RunFireAndForgetPreLaunch logs launch failure at Error even when PreLaunchIgnoreFailure is true, contradicting its own LogErrorAsWarning setting (#5238)
  • fix(service): Service.cs - SetProcessCpuAffinity's early-return warning names a nonexistent SetProcessAffinity method (#5239)
  • fix(service): Service.cs - the pre-launch back-off SCM pulse loop passes _logger while both kill-path pulse loops deliberately pass null, logging one Info line per wait slice (#5242)
  • fix(service): RotatingStreamWriterAdapter.cs / TimerAdapter.cs - Dispose nulls the inner instance before raising _disposed, so a racing WriteLine surfaces as NullReferenceException instead of the ObjectDisposedException Service.cs deliberately swallows (#5245)
  • fix(service): Service.cs - InitiateRecoveryAsync calls the restart-attempts helpers without the CancellationToken every sibling caller passes (sites missed by the #1093 fix) (#5250)
  • fix(service): Service.cs - EmitHeartbeatPing logs the full unmasked heartbeat URL on a non-success status code, while both sibling log lines in the same method call MaskUrl (#5383)
  • fix(service): EnvironmentVariableHelper.cs - ProtectedVariables omits JDK_JAVA_OPTIONS and the OpenSSL/TLS trust-store family, both inside categories the list already enumerates (#5388)
  • fix(ui): AppBootstrapper.cs - the shared extraction-failure message says 'Manager cannot start safely', so the Servy desktop app reports the wrong application name in its startup error (#5246)
  • fix(desktop): fix height issues on low resolutions
  • fix(desktop): adjust main window MinHeight for 1080p+ displays
  • fix(desktop): Strings.resx (Servy) - Info_FailureProgram still says the failure program runs 'when the process fails to start'; the last surviving copy of the wording fixed by #2280/#3832/#2279 (#5259)
  • fix(desktop): Strings.resx (Servy) - automation names 'Cpu Affinity' and 'Heartbeat Url' keep the identifier casing #4830 fixed in the visible labels (#5260)
  • fix(desktop): Strings.resx (Servy) - 'expansion, example:' comma splice in three info texts vs '. Example:' in the fourth (#5262)
  • fix(desktop): ServiceCommands.cs (Servy) - import rejection is the only failure logged at Info, and the message reads 'XML File ... not valid' (#5264)
  • fix(desktop): MainWindow.xaml (Servy) - FailureProgramParameters and PreLaunchParameters get single-line editors while their four sibling parameter fields are 80 px multi-line boxes (#5269)
  • fix(desktop,manager): ServiceCommands.cs (Servy + Manager) - a null Process.Start result is logged as a launch failure but never surfaced to the user, unlike the exception path (#5263)
  • fix(desktop,cli): Strings.resx (Servy) - Info_ServiceDependencies says 'without spaces or special characters', contradicting the validator that deliberately allows both (#3240) (#5261)
  • fix(manager): Strings.resx (Servy / Servy.Manager) - StartupType labels still drifted after #4797, plus new one-word drift in Msg_ServiceAlreadyExists (#5165)
  • fix(manager): Servy.Manager.csproj - None Remove set omits the two embedded Servy.Service binaries, and resx metadata order is flipped vs the desktop twin (#5197)
  • fix(manager): HistoryResult.cs - the Lines remark promises snapshot immutability, but the constructor aliases the caller's list instead of copying it (#5198)
  • fix(manager): LogTailer.cs - both #4867 holes partially survive: the threshold flush still publishes the torn line, and a mid-pass exception after a flush still replays published lines (#5200)
  • fix(manager): LogTailer.cs - rollback comments describe a re-scan that provably never happens; lastTerminatedLineEndOffset always equals fs.Position at its only use site (#5201)
  • fix(manager): LogTailer.cs - the CancellationToken added for #3658 never reaches LoadHistory, so an in-flight history load still cannot be cancelled (#5203)
  • fix(manager): ConsoleViewModel.cs - stderr-only services still load only half the history budget (residual of #3309) (#5204)
  • fix(manager): SearchableViewModelBase.cs - XML docs and the error log use invented jargon: 'tracking execution task container handle', 'format constraint', 'critical anomaly' (#5207)
  • fix(manager): MainViewModel.cs - RefreshAllServicesAsync's 'parallel' step is provably sequential (the async lambda never yields) and runs all process-metric collection on the UI thread from OnTick (#5209)
  • fix(manager): MainWindow.xaml.cs (Manager) - F5 on the Dependencies tab calls LoadDependencyTreeAsync directly, bypassing RefreshCommand's CanExecute/re-entrancy gate its sibling branches respect (#5219)
  • fix(cli): ExportServiceCommand.cs - the existence check decrypts every stored secret it never reads; last surviving sibling of the #3296/#1839 decrypt:false cleanup (#5267)
  • fix: Servy.csproj (+ 4 sibling executables) - StripSymbols is set in every executable project but is a Native AOT-only property; no project publishes with AOT (#5265)
  • ci: fix Node.js 20 runner deprecation warnings

Servy 9.5

Choose a tag to compare

@aelassas aelassas released this 15 Aug 14:31

This release brings ARM64 support to the Patch My PC Enterprise catalog, bug fixes, security patches, code quality improvements, and documentation updates. Full changelog listed below.

Full Changelog

Click to expand release notes!
  • feat: add ARM64 support to the Patch My PC Enterprise catalog
  • fix: resolve DeepInstinct false positives for net48 and ARM64 builds
  • fix(core): log and report legacy DPAPI unprotect fallback
  • fix(core): validate path buffer size bounds before uint to int cast in PathSecurityGuard.cs
  • fix(core): ProcessHelper.cs - move RAM sampling inside per-PID lock for consistent metrics snapshot
  • fix(core): Logger.cs - ensure old log writer is disposed in try-finally during swap
  • fix(core): EventLogReader.cs - Event log field failures swallowed without diagnostics
  • fix(core): ServiceManager.cs - StartServiceAsync's inline timeout calc omits PreLaunchRetryAttempts, reintroducing the #2211 false-timeout bug (#5043)
  • fix(core): AppConfig.cs - HandleExeRegexTimeout uses FromSeconds on a millisecond constant, yielding a 2000-second timeout (#5104)
  • fix(core): ServiceAccounts.cs - alias hash-sets carry case-only-duplicate literals redundant under OrdinalIgnoreCase (#5105)
  • fix(core): ServiceHelper.cs - CalculateStartTimeout can throw OverflowException from two individually-valid PreLaunch fields (#5106)
  • fix(core): EventLogLogger.cs - ScopedEventLogLogger's own log level is never consulted for Info/Warn/Error (residual of #1443) (#5107)
  • fix(core): ProtectedKeyProvider.cs - InvalidateCache wipes both cached Key and IV even when only one material migrates (#5109)
  • fix(core): ServiceManager.cs - Unicode-casing rename silently reverts when the legacy OS service is already gone (#5110)
  • fix(core): PathSecurityGuard.cs / ExportServiceCommand.cs - late validation failure leaves the OpenOrCreate stub file behind (regression of closed #2167) (#5111)
  • fix(core): ServiceManager.cs - InstallServiceAsync's stale-casing DeleteAsync runs unguarded, unlike the sibling SCM-cleanup branch (#5129)
  • fix(infra): SQLiteDbInitializer.cs - Missing DROP before CREATE TABLE IF NOT EXISTS in migration in ApplyVersion4()
  • fix(service): Servy.Service.exe - orphan 5-byte dummy placeholder in Servy.Service's own Resources folder (#5045)
  • fix(service): ServiceHelper.cs - LogStartupArguments Recovery block logs every health-monitoring-gated field but omits EnableHealthMonitoring itself (#5118)
  • fix(service): Service.cs - RunFireAndForgetPreLaunch drops the process wrapper without disposing it when UnderlyingProcess is not a native Process (#5119)
  • fix(service,restarter): DatabaseValidator.cs - CVE-2025-6965 SQLite version check missing from Servy.Service and Servy.Restarter (#5044)
  • fix(restarter): ServiceRestarter.cs - missing exception type in transitional-error retry filter
  • fix(restarter): ServiceRestarter.cs - Stop()/Start() command calls still catch only InvalidOperationException, not Win32Exception (last sibling of #2110/#4385/#4088) (#5050)
  • fix(restarter): ServiceRestarter.cs - HandleTransitionalError treats a vanished service like a busy SCM, burning the full timeout instead of returning ServiceNotFound (#5116)
  • fix(ui): AppBootstrapper.cs - bare catch in AppDomain unhandled-exception handler
  • fix(ui): CursorService.cs - application.Current re-checked non-atomically before dispatcher use
  • fix(ui): Strings.resx (Servy.UI) - Msg_InvalidPath has no consumer anywhere in the project (#5051)
  • fix(desktop): ServiceCommands.cs (Servy) - InstallServiceAsync, UninstallServiceAsync and Start/Stop/Restart swallow OperationCanceledException as unexpected error (#5053)
  • fix(desktop): MainWindow.xaml (Servy) - MaxLines=2147483647 repeated raw on 7 multi-line TextBoxes instead of a shared resource (#5122)
  • fix(desktop): MainWindow.xaml (Servy) - RequiredFields legend hardcodes Foreground=Gray while every other hint text uses the system brush (#5123)
  • fix(desktop): ServiceCommands.cs (Servy) - Export/Import/OpenManager show generic 'Unexpected error' on UnauthorizedAccessException (#5120)
  • fix(manager): Manager views - inactive-selection brush is #0078D7 in three list surfaces but #E5E5E5 in the dependency tree; #0078D7 repeated as a literal in four files (#4833)
  • fix(manager): PerformanceViewModel.cs - ApplyTickAsync calls SetPidText twice on the PID-changed branch (#5046)
  • fix(manager): ConsoleView/DependenciesView/PerformanceView.xaml - Loaded="UserControl_Loaded" targets a private base-class method (#5047)
  • fix(manager): MainViewModel.cs - Dispose(bool) disposes Performance/Console/DependenciesVM but not LogsVM (#5048)
  • fix(manager): MainWindow.xaml.cs - constructor's logsViewModel parameter is unused; App.xaml.cs constructs an orphan LogsViewModel that is never disposed (#5049)
  • fix(manager): ConsoleView.xaml - LogList uses VirtualizationMode=Standard while every sibling list uses Recycling (#5115)
  • fix(cli): ConsoleHelper.cs - ConsoleHelper cleanup only handles IOException
  • fix(cli): ServiceStatusCommand.cs - status of a missing service prints a blank field, never the documented 'NotInstalled' (the #4060 fix corrected the wrong premise) (#5153)
  • fix(Format-SourceHygiene.ps1): line-ending normalization target contradicts stated intent
  • fix(signpath.ps1): plaintext API token fallback only warns, does not block
  • fix: All 8 csproj - AllowUnsafeBlocks is enabled everywhere but no project uses unsafe code (#5052)
  • fix: Update-FileHelpers.ps1 - ExpectMatch checks the combined match count across MultiEdit patterns, masking a single failed edit (#5101)
  • chore: csproj files - PackageReference versions duplicated across 20 projects with no Directory.Packages.props (#5054)
  • ci(test.yml): PowerShell *.test.ps1 verification scripts are never run by CI (#5150)
  • ci(sonar.yml): dotnet tool update for the SonarCloud scanner has no exit-code check (#5146)
  • ci(changelog.yml): CHANGELOG.md - Download-section asset sizes render with 1 decimal when the second digit is 0, inconsistent with the other 420 lines (#5099)
  • docs(wiki): update documentation

Servy 9.4

Choose a tag to compare

@aelassas aelassas released this 13 Aug 06:41
  • feat(config): add EnableSizeRotation option to control log size rotation for Servy's internal log files
  • fix(core): correct property ordering in XML and JSON exports
  • fix(core): ServiceDependenciesValidator.cs - dependency names are charset-checked but never length-checked, unlike Helper.IsServiceNameValid (#5038)
  • fix(service): add debug logs for heartbeat ping URLs
  • fix(desktop): resolve PasswordBox styling issues
  • fix(desktop): crisp textbox cursor on low resolutions
  • fix(setup/net48): prevent uninstall conflict between x64 and ARM64 installers (#4990)
  • chore(deps): update dependencies
  • style: normalize source code line endings to CRLF
  • style: add .gitattributes to enforce CRLF line endings
  • ci: fix Node.js 20 runner deprecation warnings
  • docs(wiki): update documentation

Servy 9.3

Choose a tag to compare

@aelassas aelassas released this 11 Aug 16:04
  • fix(core): reject unknown properties in JSON and XML imports (#4653)
  • fix(core): ServiceControllerWrapper.cs - GetDependenciesInternal's injectable factory cannot exercise tree building; every mock wrapper is cast away (#5027)
  • fix(service): EnableEventLog=false does not suppress the ServiceBase.AutoLog entries written under the "Servy" event source (#5036)
  • fix(desktop,manager): crisp textbox cursor on low resolutions
  • fix(setup): resolve InitializeSetup issues and type mismatches
  • ci(publish.yml): Verify-PublishExclusions.ps1 - the script contains a byte-identical copy of publish.yml's filter block rather than invoking it, so all nine assertions pass no matter how the real filter changes (#4684)

Servy 9.2

Choose a tag to compare

@aelassas aelassas released this 09 Aug 08:46
  • fix(core): AppFoldersHelper.cs / AppConfig.cs - vault subfolder names 'recovery' and 'logs' are duplicated literals that can drift between default and custom vaults (#4791)
  • fix(core): ResourceHelper.cs - the staleness check is one-directional, so a version downgrade never re-extracts and the newer wrapper stays in the vault silently (#4803)
  • fix(core): ServiceHelper.cs (Core) - StopServicesAsync never settles transitional SCM states, so stopping a StartPending service fails the batch (stop-side sibling of the #2025 fix) (#4807)
  • fix(core): ServiceAccounts.cs / NativeMethodsHelpers.cs - display alias sets and RunnableServiceAccounts have drifted apart again; BUILTIN/dot/spaced forms accepted at install show raw in the Manager UI (#5021)
  • fix(core): EnvironmentVariablesValidator.cs - the value-side illegal-character message echoes the full value, unlike its key-side twin (residual of #4716) (#5029)
  • fix(core): ServiceManager.cs - ToScmStartType coerces AutomaticDelayedStart but not Unknown, sending dwStartType=0 (SERVICE_BOOT_START) to the SCM (#5031)
  • fix(infra): DapperExecutor.cs - BeginTransaction/BeginTransactionAsync are the only members without the Busy/Locked retry policy; transaction acquisition fails on the first SQLITE_BUSY the class remarks promise to ride out (#5007)
  • fix(desktop,manager): SplashWindow.xaml (Servy + Manager) - AllowsTransparency=True on a fully opaque window adds layered-window overhead for nothing (#4841)
  • fix(cli): ExportServiceCommand.cs - createdByUs length heuristic deletes a pre-existing empty file on write failure (residual of #2039) (#5000)
  • fix(psm1): Servy.psm1 / EnvironmentVariablesValidator.cs - the module's EnvVars pre-validation regex rejects keys with spaces and multi-space separators that the CLI's authoritative validator accepts (#5012)
  • fix(notifications): Write-ServyLog.ps1 - retention pruning glob ${baseName}_*${ext} matches sibling live logs and their rotations, deleting files it does not own (#4994)
  • fix(setup): setup/servy.iss - uninstall leaves HKLM\Software\Servy (AddedToPath marker) behind; the key is written but never deleted (#4779)
  • fix(setup): setup/servy.iss - ShouldAddCurrentUser compares the localized account name to 'SYSTEM' while claiming to replicate the C# SID comparison (#4781)
  • fix(setup): setup/servy.iss - x64 and arm64 installers share {autopf}\Servy but have different AppIds; on ARM64 both install side-by-side and uninstalling one breaks the other (#4990)
  • fix(setup): tools-config.ps1 - Resolve-Tool validates paths with bare Test-Path: bracketed paths fail as wildcards and a directory passes as a tool (#4997)
  • fix: Format-SourceHygiene.ps1 - Set-Content -Encoding UTF8 emits a BOM on PowerShell 5.1, re-introducing what #4944/#4966 cleaned (11 of 21 .github files now BOM'd, was 2) (#5033)
  • ci(resolve-version/action.yml): latest-release API fallback is a single-shot Invoke-RestMethod; one transient API error fails the job with a misleading 'no tag' message (residual of the #3635 class) (#4956)
  • ci(download-with-retry/action): negative max_retries yields zero attempts and a silently successful step (boundary residual of the #4663 fix) (#4953)
  • ci(download-with-retry/action.yml): every failure is retried and logged as 'Transient', including permanent 404/403s (sibling of the #1384 class) (#4954)
  • ci(bump-version.yml): pushes to main/net48 via single-shot Invoke-Git push while dot-sourcing the Invoke-PushWithRetry helper its three sibling workflows use for exactly this race (#4959)
  • ci(sbom.yml): generate-sbom.ps1 - BaseVersion is unvalidated (siblings publish.ps1/publish-sc.ps1 call Assert-ServyVersion); a malformed value is stamped silently into the published SBOM (#4984)
  • ci(scoop): setup/scoop/servy.json - description drifted from the README feature sentence: 'CPU affinity' is missing (autoupdate never rewrites the description) (#4987)
  • fix: various code quality, enhancements, and inconsistency issues

Servy 9.1

Choose a tag to compare

@aelassas aelassas released this 07 Aug 14:36
  • feat(desktop): add recovery action help button (#4828)
  • fix(cli): Servy.CLI.dll flagged by ESET Server Security (#5024)
  • fix(core): AffinityHelper.cs - hex affinity masks skip the processor-bounds check the range/list forms enforce; invalid masks pass validation and silently fail at runtime (#4788)
  • fix(core): AffinityHelper.cs - both 'start < 0' and 'core < 0' guards are provably unreachable: the '-' split consumes every minus sign before parsing (#4789)
  • fix(core): HandleHelper.cs - 'match.Success' is always true for matches enumerated from Regex.Matches; the term is provably constant (#4792)
  • fix(core): ProcessHelper.cs - GetProcessTree's TryGetValue-miss continue is provably unreachable: both enqueue sites record the start time first (#4794)
  • fix(core): ProcessKiller.cs - CriticalSystemProcesses protects the Hyper-V management services but not the per-VM worker processes (vmwp, vmmem) (#4799)
  • fix(restarter): Servy.Restarter Program.cs - logs 'Successfully restarted service' and exits 0 when the service does not exist in the SCM (void RestartService hides its vanished-service clean returns) (#4893)
  • fix(desktop,manager): app.manifest (Servy + Manager) - no DPI-awareness declaration; both WPF apps blur on mixed-DPI monitor setups (#4842)
  • fix(manager): DependenciesView.xaml - five DataTriggers bind 'IsCycle' but the model property is 'IsCyclic', so all cycle visuals are dead (#4816)
  • fix(manager): LogTailer.cs - flush-torn handling still leaks a truncated line through the threshold flush and replays published lines on exception-reopen (residual of #4084/#3302) (#4867)
  • fix(setup/servy.iss): SetupAppRunningError says '%n%Please', so the dialog renders a literal '%' and loses the blank line (#4778)
  • fix(setup/servy.iss): task XMLs declare UTF-8 but are saved with ANSI SaveStringsToFile; a non-ASCII install path corrupts them (#4780)
  • fix(license): handle64.exe / handle64a.exe - Sysinternals binaries committed and redistributed in four projects with no attribution, version record, or redistribution license (#4756)
  • ci(security.yml): .gitleaks.toml - allowlist-only config without [extend] replaces the default ruleset, so the CI secret scan runs with zero rules (#4749)
  • fix: various code quality, robustness and inconsistency issues
  • docs: fix various issues in the documentation (wiki)

Servy 9.0

Choose a tag to compare

@aelassas aelassas released this 04 Aug 19:14
  • fix(core): ServiceValidationRules.cs - the shared validator hardcodes the 12 [ServicePath] fields that the CLI-import and service-startup validators reflect over, so a new path property is silently unvalidated for the Manager and desktop app (#4401)
  • fix(core): AppFoldersHelperTests.cs / AppFoldersHelper.cs - EnsureFolders_ValidPaths looks temp-dir scoped but EnsureFolders unconditionally re-ACLs the real C:\ProgramData\Servy vault plus its recovery and logs folders (#4491)
  • fix(core): InstallServiceOptions.cs (Core) - HeartbeatUrl and its two companions omit the 'Only used when EnableHealthMonitoring is true' caveat their five siblings carry, and the gate silently drops every ping (#4498)
  • fix(core): NativeMethodsHelpers.cs / NativeMethodsHelpersTests.cs - AtomicSecureMove guards with IsNullOrWhiteSpace but its message says 'null or empty' and neither theory has a whitespace row; ValidateCredentials in the same file does all three correctly (#4506)
  • fix(core): ServiceDependenciesValidator.cs - Validate emits one error per duplicate occurrence while Parse de-duplicates the same token stream (#4513)
  • fix(core): SecurityHelper.cs - the breakInheritance:true non-admin fallback cannot fall back: both downstream catch filters require !breakInheritance (#4556)
  • fix(core): EventLogService.cs - a whitespace-only sourceName disables both provider filters and skips the allowlist (IsNullOrWhiteSpace at line 55 vs IsNullOrEmpty at line 130) (#4561)
  • fix(core): IServiceManager.cs / ServiceManager.cs - GetDependencies checks cancellation before its argument guard, so the documented ArgumentException becomes an OperationCanceledException; its three sibling reads do the opposite (#4564)
  • fix(core): SafeWinProcessHandle.cs - DangerousGetHandle hides the non-virtual base member with 'new', so the documented "IntPtr.Zero when closed" guarantee is bypassed through any base-typed reference (#4640)
  • fix(manager): PerformanceViewModel.cs - AddPoint takes both the history queue and the MetricType that already selects it; a mismatched pair compiles and silently renders the wrong graph (#4374)
  • fix(manager): MainViewModel.cs (Manager) - five minor items: ServiceCommands setter (null guard / notification / field placement), ServicesView private set, Refresh() naming, cursor reset on early returns, no disposed guard on CreateAndStartTimer (#4369)
  • fix(manager): ServiceRowViewModel.cs - ConfigureCommand is the only one of the ten row commands built without the shared CanExecuteServiceCommand predicate (#4594)
  • fix(cli): BaseCommand.cs - a failed service operation with a blank OperationResult.ErrorMessage logs nothing at all; the #3387 fix hardened only the user-facing path (residual) (#4701)
  • ci(publish.yml): $env:SEVEN_ZIP assignment in the install step is a no-op; only the GITHUB_ENV export on the next line reaches the packaging step (#4664)
  • ci(publish.yml): setup/servy.iss and setup/servy-arm64.iss are 575 of 582 lines identical; the 7 differing lines are all expressible with an Inno /DArch define, which both callers already use for /DMyAppVersion (#4688)
  • ci(dependabot.yml): schedule.interval is inert while open-pull-requests-limit is 0, so the nuget/github-actions daily-vs-weekly split implies a cadence difference that does not exist (#4662)
  • ci(scoop.yml): the Extras branch probe is wrapped in Invoke-Git, which exits on non-zero; 'rev-parse --verify --quiet' always exits 1 on a fresh clone, so run_extras_pr:true can never succeed (#4667)
  • ci(changelog.yml): 'git add' runs before the porcelain check, so the following 'git diff --staged' else branch is unreachable; choco.yml and bump-version.yml order the same two guards the other way (#4659)
  • ci(wiki.yml): the 'workspace files are present' guard uses 'ls -A .', which always lists .git after actions/checkout, so the empty/inaccessible branch is unreachable (#4678)
  • ci(security.yml): the concurrency group falls back to github.sha for push/schedule/dispatch, making it unique per run and inert; all five sibling workflows key on github.ref (residual of #1098) (#4673)
  • ci(download-with-retry/action.yml): output_path is run through ExpandString, which evaluates $(...) subexpressions as code; no caller uses the environment-variable expansion it was added for (#4657)
  • fix: various robustness, security, inconsistency, and code quality issues (check GitHub Issues for more details)

Servy 8.9

Choose a tag to compare

@aelassas aelassas released this 30 Jul 08:50
  • fix(core): JsonServiceSerializer.cs - FormatLineInfo casts the exception to IJsonLineInfo, which no Newtonsoft exception implements, so JSON parse errors never report line/position (#4459)
  • fix(core): ServiceControllerWrapper.cs - the 'if (deps != null)' guard in BuildDependencyTree's disposal finally is provably always true (#4460)
  • fix(core): ServiceDtoImportValidator.cs - the manual CR/LF strip duplicates Logger's central SanitizeToSingleLine, and renders the same name differently from every other log line (#4463)
  • fix(service): ProcessLauncher.cs - three provably-always-true path-emptiness terms at lines 205, 239 and 249, already folded into psi.RedirectStandardOutput/Error at 148-149 (#4404)
  • fix(ui): AppBootstrapper.cs - critical-configuration failure tells operators to check an appsettings.json file that no app ships (#4437)
  • fix(desktop): MainViewModel.cs (Servy) - three minor items: ManagerCommand is the only busy-block command without the !IsBusy guard, LoadServiceConfiguration missed the #3858 Async rename, trailing whitespace at 915 (#4447)
  • fix(desktop,manager): DesignTimeAppConfig.cs / DesignTimeMocks.cs / DesignTimeServiceCommands.cs - the five design-time stand-ins follow four different implementation and documentation conventions (#4440)
  • fix(manager): ConsoleViewModel.cs - t.IsFaulted is provably always true inside a TaskContinuationOptions.OnlyOnFaulted continuation (#4347)
  • fix(manager): ConsoleViewModel.cs - ConsoleSearchText setter lacks the equality guard every sibling setter uses; a no-op write cancels the in-flight debounce and re-filters the whole buffer (#4349)
  • fix(manager): Console/DependenciesViewModel.cs - SetPidText runs on every monitoring tick, defeating the change-detection branch immediately above it (#4353)
  • fix(manager): MainViewModel.cs (Manager) - Select All raises HasSelectedServices once per row; the _isUpdatingSelectAll guard covers UpdateSelectAllState but not the notification (O(N²)) (#4367)
  • fix(manager): PerformanceViewModel.cs - AddPoint's two 'valueHistory.Count > 0' guards (319, 361) are provably always true after the unconditional Enqueue at 311 (#4373)
  • fix(cli): UninstallServiceCommand.cs - when the SCM service is already gone, the pipeline fails before onSuccess, so the repository row can never be cleaned from the CLI (#4235)
  • fix(cli): Program.cs (Servy.CLI) - status/export still run the full runtime bootstrap (wrapper-exe extraction, event source, DB init); a corrupt servy.db breaks even 'status' (follow-up to #1760) (#4242)
  • fix: linker.shared.xml - three trimmer roots preserve capabilities the trimmed projects do not have (Claims unused, FileSystemWatcher only in an untrimmed project, no STA code at all) (#4445)
  • ci: resolve-version/action.yml - unused step 'id: resolve' left over from the removed outputs block (#2832) (#4172)
  • ci: bump-version.yml - 'update-index --chmod=+x bump-version.ps1' is a no-op (mode already 100755, and Windows execution never needs it) (#4174)
  • ci: loc.yml - badge deploy lacks force_orphan, so the loc branch accumulates one junk commit per push to main forever (#4177)
  • ci: scoop.yml - step env re-maps HASH/VERSION to themselves; sibling Extras step proves the mapping is unnecessary (#4191)
  • ci: bump-version.ps1 - no -DryRun preview mode, unlike sibling bump-runtime.ps1 (#4202)