From 775bb7813d9e6602583f68044f7ccce83a245edd Mon Sep 17 00:00:00 2001 From: Vibe Myass Date: Wed, 11 Mar 2026 01:52:57 +0000 Subject: [PATCH] Handle empty successful snapshot output in high-level client --- src/LibNftables/NftablesClient.cs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/LibNftables/NftablesClient.cs b/src/LibNftables/NftablesClient.cs index 7742285..42e6d19 100644 --- a/src/LibNftables/NftablesClient.cs +++ b/src/LibNftables/NftablesClient.cs @@ -77,10 +77,21 @@ public sealed class NftablesClient : INftablesClient NftApplyRequest request = NftApplyRequest.FromText(_options.SnapshotCommandText, dryRun: false); CommandExecutionResult result = Execute(request, forceDryRun: null, ct); string snapshotText = result.Output ?? string.Empty; + string errorText = result.Error ?? string.Empty; if (string.IsNullOrWhiteSpace(snapshotText)) { - throw new NftException("Snapshot returned an empty ruleset output.", nativeErrorOutput: result.Error); + if (ContainsAny(errorText, "Operation not permitted", "Permission denied", "CAP_NET_ADMIN")) + { + throw new NftPermissionException("Snapshot requires elevated privileges.", 0, result.Error); + } + + if (!string.IsNullOrWhiteSpace(errorText)) + { + throw new NftException("Snapshot returned an empty ruleset output.", nativeErrorOutput: result.Error); + } + + snapshotText = "flush ruleset"; } return new NftSnapshot(snapshotText, System.DateTimeOffset.UtcNow); @@ -175,5 +186,23 @@ public sealed class NftablesClient : INftablesClient } } + private static bool ContainsAny(string value, params string[] candidates) + { + if (string.IsNullOrWhiteSpace(value)) + { + return false; + } + + foreach (string candidate in candidates) + { + if (value.Contains(candidate, System.StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + private sealed record CommandExecutionResult(string? Output, string? Error); }