From e403567b88de757009deb4df542a106a39baf9f7 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Wed, 5 Aug 2026 17:10:07 +0200 Subject: [PATCH] Pass the app's log level into the elevated run --- client/internal/daemonaddr/identity.go | 13 ++++++++----- client/ui/services/guarded.go | 5 +++++ client/ui/services/guarded_test.go | 6 ++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/client/internal/daemonaddr/identity.go b/client/internal/daemonaddr/identity.go index c4a02ee5c..b6af515b7 100644 --- a/client/internal/daemonaddr/identity.go +++ b/client/internal/daemonaddr/identity.go @@ -4,11 +4,14 @@ import "strings" // CarriesIdentity reports whether the control channel at addr conveys the // connecting process's identity to the daemon. A Unix socket carries peer -// credentials and a named pipe carries the client's token; loopback TCP carries -// neither, so on such an address the daemon cannot authorize a privileged -// operation for anybody. A client uses this to tell whether becoming privileged -// would get it anywhere: on an identity-less address it would not, and the only -// way forward is to move the daemon onto one that carries identity. +// credentials and a named pipe carries the client's token. Nothing else does, TCP +// included, and there the daemon can authorize a privileged operation for nobody +// at all: see ResolveDaemonAddr, which says as much to anyone still reaching the +// Windows daemon on the address it served before it had a pipe. +// +// A client uses this to tell whether becoming privileged would get it anywhere. +// It answers from the scheme and nothing else, so an address it does not +// recognise counts as carrying no identity. func CarriesIdentity(addr string) bool { return strings.HasPrefix(addr, "unix://") || strings.HasPrefix(addr, pipeScheme) } diff --git a/client/ui/services/guarded.go b/client/ui/services/guarded.go index ddc8f7ae0..f425428b5 100644 --- a/client/ui/services/guarded.go +++ b/client/ui/services/guarded.go @@ -126,11 +126,16 @@ func (s *Settings) SetGuardedSettings(ctx context.Context, p GuardedSettings) (S } } + // The elevated run has no window and, on Linux, an environment pkexec has + // cleared, so what it writes to stderr is all there is to go on. It follows + // this process's level so that starting the app with --log-level debug says + // something about the run behind the prompt too. args := append([]string{ "--" + FlagApplyPrivilegedSettings, "--" + FlagDaemonAddr, s.daemonAddr, "--" + FlagProfile, p.ProfileName, "--" + FlagUser, p.Username, + "--" + FlagLogLevel, log.GetLevel().String(), }, oneShotArgs(settings)...) ctx, cancel := context.WithTimeout(ctx, elevationTimeout) diff --git a/client/ui/services/guarded_test.go b/client/ui/services/guarded_test.go index fe2a116d1..42c00ce4f 100644 --- a/client/ui/services/guarded_test.go +++ b/client/ui/services/guarded_test.go @@ -7,6 +7,7 @@ import ( "errors" "testing" + log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/genproto/googleapis/rpc/errdetails" @@ -129,10 +130,15 @@ func TestSetGuardedSettingsPassesOnlyTheChangedSettings(t *testing.T) { "--" + FlagDaemonAddr, testDaemonAddr, "--" + FlagProfile, "work", "--" + FlagUser, "vma", + "--" + FlagLogLevel, log.GetLevel().String(), "--" + FlagEnableSSHRoot + "=true", } require.Len(t, elev.calls, 1, "one prompt for one change") assert.Equal(t, want, elev.calls[0], "elevated arguments") + + // argv[1] is what the polkit action is pinned to, so the marker has to stay + // first however the rest of the line grows. + assert.Equal(t, "--"+FlagApplyPrivilegedSettings, elev.calls[0][0], "the flag polkit matches on") } // Turning a setting off has to be as explicit as turning it on: a bare flag would