meta_package_manager.sudo module

Privilege-escalation machinery for the mutating fan-outs.

This module owns sudo credential priming (prime_sudo()) and its background keepalive (_start_sudo_keepalive), escalation-policy resolution (_resolved_sudo), sudo-failure detection (_is_sudo_auth_failure), and the hidden-prompt stall watchdog (_StallWatchdog). The execution engine (meta_package_manager.execution) consumes the policy pieces to wrap and diagnose escalated commands; the CLI calls prime_sudo() at the top of each mutating subcommand.

Why priming exists: a concurrent state-changing command mutes per-manager output and feeds each child stdin=/dev/null, so a sudo password prompt raised mid-run (by mpm’s own sudo --non-interactive or by a manager that escalates internally, like Homebrew cask) lands invisibly on /dev/tty and can stall the run up to the mutating timeout. Priming first probes the credential cache non-interactively: found warm, it is silently kept alive for the whole run; found cold on a terminal, the managers mpm itself escalates get a single up-front password prompt, naming them and branded [mpm]. Internal escalators never prompt up front: their rare cold-cache escalation is covered by the silent-call stall notice instead, raised while the hidden prompt can still be answered.

Note

Windows reaches only a corner of this module. gsudo gives it an escalator to probe and a cache to warm, and Microsoft’s own sudo.exe backs it up from 24H2, but no manager there escalates by default, so prime_sudo() returns on the empty selection unless --sudo or a [mpm.overrides.<id>] sudo = true entry asks for it. The internal escalators stay macOS-only managers, so the watchdog is not armed there either.

Neither reaches an unattended run the way a Unix escalator does. sudo.exe refuses every token whose elevation type is Default, which is what a network logon carries: an SSH session holding a full administrator token at High integrity is still turned away with You are not allowed to run sudo. So a scripted or remote Windows run escalates through gsudo or not at all.

Todo

Prime Microsoft’s sudo.exe once there is anything to prime. It caches nothing, so every escalation of a run raises a UAC dialog of its own, which is why ESCALATORS ranks it behind gsudo rather than beside it. microsoft/sudo#7 is the request that would give it a cache for prime_sudo() to warm; gsudo has the same gap open at gerardog/gsudo#378 for its own password path. Nothing else blocks the backend: the command line, the --preserve-env environment and the child’s exit code all survive the elevation, measured on build 26100.1742 from a Medium-integrity shell, the one path that really brokers it, so microsoft/sudo#117 never reaches the shapes this project builds.

Emulate an option a backend cannot express rather than failing on it: topgrade returns a hard error there, which its users report as a bug (topgrade-rs/topgrade#1435).

Todo

Escalate to the user owning a manager’s tree, not only to root: every escalate_args reaches root alone, where sudo --user and doas -u could reach the owner. The one legitimate case is a multi-user nix install, whose foreign-owned profiles are a first-class upstream configuration. A shared Homebrew prefix is not: Homebrew’s support tiers file “Multi-user Homebrew environments where multiple users share the same installation” as unsupported, so smoothing that setup over (as topgrade does for its brew step) would carry a burden upstream itself refuses. Stays unbuilt until a nix user asks.

Todo

Rebrand the hidden password prompt of an internal escalator with a SUDO_ASKPASS helper, once the stall notice of _StallWatchdog proves insufficient in the field. It is also the only route serving a hardened sudoers policy, whose timestamps the primed cache cannot reach (see _SUDO_CACHE_WARM). That class records why the helper was rejected, and any implementation has to answer both of its points: the raw password it handles, and the tools it never reaches (brew honors the variable, fink’s plain sudo re-exec does not).

meta_package_manager.sudo.UNANSWERED_PROMPT_DETAIL: Final = 'no password'

How a ✘ trail line words a call that ended on an unanswered password prompt.

CLIExecutor.run files it as the detail of two failures: its own timeout, when _StallWatchdog still finds the prompt on the terminal, and the failure of a tool whose own sudo gave up on the prompt (_is_prompt_timeout).

class meta_package_manager.sudo.Escalator(id, escalate_args, probe_args, passwordless_probe_args, prompt_args, refreshable, brands_prompt, remedy, binary=None, identity_args=None, identity_markers=None, retention_markers=None, retention_probe_args=None, probe_command=None, probe_success_markers=None, env_forward_template=None, gated_options=(), version_args=None)[source]

Bases: object

One privilege-escalation binary, and the argv dialects mpm drives it with.

Everything specific to an escalator lives here, so the rest of the module reasons about escalation without naming a binary. The dialects are not interchangeable: doas is not a sudo clone with another name, it takes short options only and has no way to authenticate without running a command.

id: str

The binary’s name, and the value the sudo_command override selects it by.

escalate_args: tuple[str, ...]

Argv prefix escalating a manager command, non-interactively.

Non-interactive on purpose: a prompt raised inside the concurrent fan-out lands on a terminal nobody is watching (see prime_sudo()). Emitted by CLIExecutor.build_cli and matched back by CLIExecutor.run to recognize an escalation failure, so the two sites must stay in lockstep.

probe_args: tuple[str, ...]

Argv reading the credential cache without ever prompting.

sudo answers this without running anything (--validate); doas has no such mode, so it runs true as the cheapest harmless command.

passwordless_probe_args: tuple[str, ...] | None

Argv asking whether one named command runs without a password, or None.

Completed with the command’s path by _escalation_is_passwordless. sudo --list -- <command> answers precisely, reporting the command when the policy grants it unauthenticated and failing under --non-interactive when a password would be wanted. doas has no such query and gets None.

Needed because probe_args answers a different question. sudo –validate``refuses whenever any matching``sudoers entry requires a password, even when the entry that would actually run the command is tagged NOPASSWD. openSUSE stacks exactly that pair: its stock ALL ALL=(ALL) ALL sits under the NOPASSWD rule docs/sudo.md recommends, so --validate reports a cold cache on a host where every escalation in fact runs untouched, and without this probe mpm warns that managers “may fail” before they go on to succeed.

That is a property of --validate rather than of one distribution’s policy, and Fedora reaches it by an unrelated route: its stock %wheel ALL=(ALL) ALL under the same NOPASSWD rule leaves sudo 1.9.17p2 answering a password is required``and exiting``1`, while``–list –exits``0` and a real ``sudo --non-interactive id -u returns 0. Two policies with nothing in common, one false cold cache, which is what this second probe exists to catch.

prompt_args: tuple[str, ...]

Argv authenticating the user up front, interactively, once per run.

refreshable: bool

Whether probe_args can also serve as a keepalive tick.

True only where the escalator caches credentials on a schedule mpm can reason about. doas persistence is opt-in per rule in doas.conf, so a recurring probe would report a drop on every tick for the majority of hosts, where no rule asks for it. Those escalators get no keepalive thread at all.

brands_prompt: bool

Whether the escalator can be told what password prompt to print.

sudo --prompt can; doas cannot. The notice prime_sudo() echoes before authenticating names the managers either way, so an unbranded prompt loses the [mpm] marker, not the explanation.

remedy: str

What lets escalated commands run with no password prompt mid-run, as the sentence closing each warning about a missing authorization.

It names what the host owns: a sudoers or doas.conf rule, or a polkit grant. A password typed up front cannot stand in for one where the escalator keeps no authorization for later commands: doas without persist, or polkit under auth_admin, which is what pkexec gets by default and run0 gets over SSH.

binary: str | None = None

The name to look for on PATH, where it differs from id.

Only Microsoft’s sudo.exe needs one. It ships under a name the Unix escalator already claims, and shares none of its dialect: --non-interactive and --validate are not options it has. So the two need distinct ids, for the sudo_command override to name either, while resolve_escalator() still has to look for the same file name on both platforms.

identity_args: tuple[str, ...] | None = None

Argv proving the binary on PATH is this escalator, not a stand-in.

A name on PATH is not proof of the dialect behind it: Alpine’s doas-sudo-shim installs /usr/bin/sudo as a shell script forwarding to doas, and it accepts --non-interactive alone out of everything mpm sends. So escalate_args works there while every probe dies on unrecognized option, and mpm reads that as a cold credential cache on a host where escalation in fact runs untouched.

Run by is_genuine(), which needs the argv to authenticate nothing and to cost nothing: sudo --version reports the build and exits 0 on real sudo, where the shim rejects the option and exits 1. None where no stand-in is known, which is every escalator but sudo.

identity_markers: tuple[str, ...] | None = None

Substrings identity_args prints when the binary is genuine, any one of which is proof.

Matched against <stdout> on a zero exit. Kept beside the argv because exit status alone is too weak a signal: a stand-in free to accept the option would pass on the returncode.

Several markers rather than one because a reimplementation brands its own banner: sudo --version prints Sudo version 1.9.17 on the original and sudo-rs 0.2.13 on the Rust rewrite Ubuntu ships as its default sudo since 25.10. Matching the first alone would reject sudo-rs as a stand-in, resolve_escalator() then falling through to doas on a host carrying both and inverting the documented preference. Upstream’s SUDO_RS_VERSION override replaces the number and never the sudo-rs prefix, so the prefix is what the second marker keys on.

Both markers are the ones sudo-rs itself sorts the two implementations by: its test framework reads the same banner, stripping Sudo version to recognize the original and treating everything else as its own (test-framework/sudo-test/src/lib.rs).

retention_markers: tuple[str, ...] | None = None

Substrings a cold probe_args answer prints when a password prompt would authorize the commands after it too, or None where the probe cannot tell.

Only polkit’s pkcheck can, for pkexec and run0: it adds polkit\56retains_authorization_after_challenge=1 to its <stdout> under auth_admin_keep, and nothing under auth_admin, where a prompt authorizes its own command alone. prime_sudo() then skips the prompt, since the password would buy nothing. Measured on polkit 0.104, SliTaz 5.0.

retention_probe_args: tuple[str, ...] | None = None

Argv asking polkit about retention_markers, where the credential probe cannot answer, or None to read the answer of that probe.

Only run0 needs one: its probe runs true through systemd, while pkcheck asks polkit about org.freedesktop.systemd1.manage-units, the action run0 authorizes against. systemd’s policy grants it auth_admin_keep for an active local session and auth_admin for an SSH one. Measured on Fedora 44, systemd 259, polkit 127.

probe_command: str | None = None

The stand-in command probe_args runs, where the escalator’s rules can match single commands, or None.

Only doas: permit nopass tester as root cmd /sbin/apk denies the doas -n true probe with Operation not permitted and still runs doas -n /sbin/apk unprompted, measured on Alpine’s doas 6.8.2. A denial of the probe then speaks for that command alone, so prime_sudo() does not say the managers will fail. doas -C would answer for the managers’ own commands, but it reads doas.conf as the caller, and Alpine installs that file mode 640, owned by root:root.

probe_success_markers: tuple[str, ...] | None = None

Substrings proving probe_args found escalation ready, for an escalator whose probe cannot say so through its exit code. Any one is enough.

None keeps the exit code as the whole answer, which is what sudo, doas and run0 report through. gsudo is the exception: gsudo status --json asks exactly the question prime_sudo() needs and always exits 0, so the answer lives in its output alone. Two of its fields settle it, and either is sufficient: IsElevated because a process already elevated needs no escalation, and CacheAvailable because a warm credentials cache spends without raising a UAC dialog.

env_forward_template: str | None = None

Argument forwarding one environment variable across the escalation, or None for an escalator the variables survive on their own.

Only run0 needs it. It runs the command in a transient service forked from the service manager, which inherits nothing from the caller, so the environment CLIExecutor.run forces on a call would reach run0 and stop there. That silently costs the managers relying on one: nala, tazpkg and urpmi pin their parsers against a translated locale with LC_ALL=C, and ports keeps out of an interactive dialog with BATCH=yes.

sudo and doas need no template: both reset the environment too, but through a sudoers policy whose env_keep the host owns, which is where such a decision belongs. pkexec clears it down to a fixed list, which holds LANG and every LC_* variable, so the LC_ALL=C above survives it and only BATCH=yes is lost. Carrying that one would need the pkexec env NAME=VALUE ... idiom rather than a per-variable flag, so it keeps None until a manager on a polkit-only host asks for one.

gated_options: tuple[tuple[str, str], ...] = ()

Options following escalate_args on the releases accepting them, each paired with the first such release.

supported_options() reads the installed release through version_args, and keeps the options it accepts. Only pkexec has one: --keep-cwd arrived in polkit 121 (polkit-org/polkit@af533784a330), and an older pkexec reads it as the program to run, failing every escalation on Cannot run program --keep-cwd. SliTaz 5.0 ships polkit 0.104.

version_args: tuple[str, ...] | None = None

Argv printing the installed release, for gated_options, or None where no option is gated.

property binary_name: str

The file resolve_escalator() looks for on PATH.

forward_env(args, env)[source]

Splice env_forward_template for each variable into args.

Inserted ahead of the end-of-options separator the prefix closes on, so a manager’s own flags stay shielded from the escalator’s parser. Sorted for a stable command disclosure. Returns args untouched for an escalator declaring no template, or for a call forcing no variable.

A None value asks for a variable to be unset, which needs carrying nowhere: the fresh environment on the far side of the escalation never had it.

Return type:

tuple[str, ...]

probe_says_warm(probe)[source]

Whether the probe found escalation ready to run unprompted.

Return type:

bool

resolved_probe_args()[source]

probe_args, with any :pid: token replaced by mpm’s own id.

Only pkexec needs it. Its probe is pkcheck, polkit’s own authorization query, which asks about a subject rather than about the caller and refuses to guess one: without --process it exits 126 on Subject not specified. Every other escalator answers for whoever runs it and carries no token, so the substitution is a no-op there.

Return type:

tuple[str, ...]

supported_options()[source]

The gated_options the installed release accepts.

A release that cannot be read keeps every option. A rejected option then fails the escalation with the escalator’s own error, where dropping it would change the command without a word.

Return type:

tuple[str, ...]

identity()[source]

What the identity probe found: genuine, stand-in or unrunnable.

genuine when the escalator declares no identity_args, so an escalator opts into the check rather than out of it. unrunnable keeps a binary that cannot run apart from one that runs and answers as something else, which mpm doctor reports differently.

Return type:

str

is_genuine()[source]

Whether the binary on PATH really is this escalator.

Return type:

bool

meta_package_manager.sudo.ESCALATORS: Final[tuple[Escalator, ...]] = (Escalator(id='sudo', escalate_args=('sudo', '--non-interactive'), probe_args=('sudo', '--non-interactive', '--validate'), passwordless_probe_args=('sudo', '--non-interactive', '--list', '--'), prompt_args=('sudo', '--validate'), refreshable=True, brands_prompt=True, remedy='Allow the escalated commands without a password with a `NOPASSWD` rule, or authenticate first with `sudo --validate` in the same terminal.', binary=None, identity_args=('sudo', '--version'), identity_markers=('Sudo version', 'sudo-rs'), retention_markers=None, retention_probe_args=None, probe_command=None, probe_success_markers=None, env_forward_template=None, gated_options=(), version_args=None), Escalator(id='doas', escalate_args=('doas', '-n'), probe_args=('doas', '-n', 'true'), passwordless_probe_args=None, prompt_args=('doas', 'true'), refreshable=False, brands_prompt=False, remedy='Add `persist` to the matching `doas.conf` rule to keep the authorization, or `nopass` to need no password.', binary=None, identity_args=None, identity_markers=None, retention_markers=None, retention_probe_args=None, probe_command='true', probe_success_markers=None, env_forward_template=None, gated_options=(), version_args=None), Escalator(id='run0', escalate_args=('run0', '--pipe', '--no-ask-password', '--'), probe_args=('run0', '--pipe', '--no-ask-password', 'true'), passwordless_probe_args=None, prompt_args=('run0', '--pipe', 'true'), refreshable=False, brands_prompt=False, remedy='Grant `org.freedesktop.systemd1.manage-units` with a polkit rule.', binary=None, identity_args=('run0', '--version'), identity_markers=('systemd',), retention_markers=('retains_authorization_after_challenge=1',), retention_probe_args=('pkcheck', '--action-id', 'org.freedesktop.systemd1.manage-units', '--process', '{pid}'), probe_command=None, probe_success_markers=None, env_forward_template='--setenv={name}={value}', gated_options=(), version_args=None), Escalator(id='pkexec', escalate_args=('pkexec', '--disable-internal-agent'), probe_args=('pkcheck', '--action-id', 'org.freedesktop.policykit.exec', '--process', '{pid}'), passwordless_probe_args=None, prompt_args=('pkexec', 'true'), refreshable=False, brands_prompt=False, remedy='Grant `org.freedesktop.policykit.exec` with a polkit rule, or with a `.pkla` file before polkit `0.106`.', binary=None, identity_args=None, identity_markers=None, retention_markers=('retains_authorization_after_challenge=1',), retention_probe_args=None, probe_command=None, probe_success_markers=None, env_forward_template=None, gated_options=(('--keep-cwd', '121'),), version_args=('pkexec', '--version')), Escalator(id='gsudo', escalate_args=('gsudo',), probe_args=('gsudo', 'status', '--json'), passwordless_probe_args=None, prompt_args=('gsudo', 'cache', 'on', '--pid', '0'), refreshable=False, brands_prompt=False, remedy='Warm its cache first with `gsudo cache on --pid 0`.', binary=None, identity_args=('gsudo', '--version'), identity_markers=('gsudo',), retention_markers=None, retention_probe_args=None, probe_command=None, probe_success_markers=('"IsElevated":true', '"CacheAvailable":true'), env_forward_template=None, gated_options=(), version_args=None), Escalator(id='win-sudo', escalate_args=('sudo', '--inline', '--preserve-env'), probe_args=('reg', 'query', 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Sudo', '/v', 'Enabled'), passwordless_probe_args=None, prompt_args=('sudo', 'config'), refreshable=False, brands_prompt=False, remedy='Set Windows `sudo` to inline mode in Settings, or install `gsudo`, whose cache one UAC dialog warms for the whole run.', binary='sudo', identity_args=('sudo', '--help'), identity_markers=('Sudo for Windows',), retention_markers=None, retention_probe_args=None, probe_command=None, probe_success_markers=('0x3',), env_forward_template=None, gated_options=(), version_args=None))

Every escalator mpm can drive, in the order it prefers them.

sudo comes first so a host carrying both keeps the behavior it has today, and the sudo_command override exists for the user who wants the other one. The order only decides auto-detection: an explicit override always wins.

run0 comes after them for the same reason, one step further: it needs a running polkit to authorize anything, so a host carrying a working sudo or doas keeps it, and run0 answers for the systemd hosts that ship neither.

win-sudo closes the list, behind gsudo, and the two are the whole of Windows. Microsoft’s is inbox from 24H2 where gsudo has to be installed, which is exactly why it ranks second: it caches nothing, so each escalation of a run raises its own UAC dialog, where one answered gsudo cache on covers every later one. It is the fallback for a host carrying no gsudo, not the default for a host carrying both.

pkexec sits before them, and auto-detection rarely reaches it: it ships wherever polkit does, which is nearly every desktop Linux, and those carry sudo too. SliTaz is the exception, its sudo being a su -c wrapper that fails the identity probe. It is also there for --sudo-command pkexec, and it only works where a polkit rule already grants org.freedesktop.policykit.exec, since it cannot escalate without prompting. The probe is what keeps that honest: a host without the rule reports a cold cache and its managers decline to run, rather than each of them stopping on a prompt inside the fan-out.

meta_package_manager.sudo.resolve_escalator(override: str | None = None) → Escalator | None[source]

The escalator mpm drives, or None when the host carries none.

With no override, returns the first entry of ESCALATORS whose binary is on PATH and passes is_genuine(), so a host without sudo still escalates through whatever it does have, and one whose sudo is a stand-in for another escalator drives that other one directly. An override names one by its id and is honored even when the binary is missing, so the failure names the escalator the user asked for instead of silently falling back to another one.

Cached on the override, since PATH does not move mid-run and the identity probe costs a subprocess. Tests changing what is installed must call resolve_escalator.cache_clear().

Return type:

Escalator | None

meta_package_manager.sudo.ESCALATION: Final = <meta_package_manager.sudo._EscalationChoice object>

Process-wide escalator selection.

A module-level singleton for the same reason as PLAN_RECORDER: CLIExecutor.build_cli needs it from the fan-out’s worker threads, where the click context is not reliably reachable.

class meta_package_manager.sudo.InstallRoot(path, owner_uid, owner_name)[source]

Bases: object

Ownership snapshot of the tree a manager’s global installs write into.

Built by inspect_install_root() from a manager’s own install_root probe. Diagnosis only, by decision: deciding escalation from this snapshot was assessed and rejected, since silently running a manager as root on filesystem evidence nobody reviewed is a posture change no diagnostic payoff justifies. The scoped sudo = true override stays the one road to escalating a dormant marker, and the failure-gate hint (_is_permission_failure) is what names it.

path: Path

The install root itself.

owner_uid: int

Numeric owner of the root directory.

owner_name: str

The owner’s account name, or the bare uid when no account matches.

meta_package_manager.sudo.inspect_install_root(manager)[source]

Resolve and stat manager’s install root, or None when unknowable.

None covers every dead end: a non-POSIX host (the ownership model does not apply, and pwd does not exist), a manager with no discovery verb, a probe that fails, and a resolved path that does not exist. The probe shells out, so failures of any kind are swallowed: this is diagnosis, and it must never break the command it decorates.

Return type:

InstallRoot | None

meta_package_manager.sudo.prime_sudo(ctx, managers, *, operations)[source]

Warm the sudo credential cache, up front, for a mutating fan-out.

Probes the cache non-interactively (sudo --non-interactive --validate) before considering any prompt. A warm cache (pre-authenticated sudo --validate, a NOPASSWD rule, a recent run) is silently kept fresh for the whole invocation by _start_sudo_keepalive, so every later escalation on the same terminal, mpm’s own sudo --non-interactive as well as a manager’s internal sudo (CLIExecutor.internal_sudo), spends the cache instead of blocking on an invisible prompt inside the concurrent fan-out. Only a cold cache, on an interactive terminal, with managers that mpm itself escalates in this run (_escalates_in), triggers the interactive path: a notice naming the managers and the subcommand, then a single branded sudo password prompt.

operations lists every operation the run of the subcommand can reach, the reads included. A manager escalates in this run only when one of them is in its privileged_operations, so no password is asked for a manager whose commands stay unprivileged.

Before probing, the binary of each manager that escalates in this run is audited with the same tamper test the config loader applies (config_file_is_trusted()): a binary that others can modify, handed to sudo, runs their code as root, so it draws one warning per manager (see docs/security.md).

Call at the top of each mutating subcommand, before the fan-out draws its spinner. Never prompts when:

  • the process is already root,

  • no selected manager escalates a command of this run, through mpm or internally,

  • a dry run or a plan run (no state-changing CLI is executed),

  • already primed once this invocation (idempotent),

  • a forced policy names a manager that drops the cached credentials itself: one warning says the policy does nothing for it, and the manager stays out of the prompt,

  • no escalator is on PATH, or sudo_command names an unknown one (one warning is logged),

  • the probe finds the cache already warm (keepalive only, fully silent),

  • the probe reports the user is not authorized to run sudo at all (_is_sudo_denied): one warning names the managers mpm escalates and the remedy, since a prompt could only collect a password sudo then rejects, while an internal-only selection stays silent,

  • no interactive terminal is available: one warning names the managers mpm escalates and leaves them to fail fast rather than block on a prompt no one can answer, while an internal-only selection stays silent, or

  • only internal escalators are selected on a cold cache: most such runs never escalate, so the rare mid-run prompt is covered by the silent-call stall notice instead.

Return type:

None

class meta_package_manager.sudo.EscalationDiagnosis(escalated, internal, is_root=False, escalator=None, chosen=False, path=None, skipped=(), identity='genuine', release=None, missing_options=(), credentials='', answer='', prompt_kept=None)[source]

Bases: object

How escalation stands on this host, as mpm doctor reports it.

Gathered by diagnose_escalation(), which never prompts: it runs the non-interactive probes prime_sudo() runs.

escalated: tuple[str, ...]

The selected managers whose privileged commands mpm escalates.

internal: tuple[str, ...]

The selected managers that run sudo from inside their own commands.

is_root: bool = False

Whether mpm runs as root, which leaves nothing to escalate.

escalator: Escalator | None = None

The escalator mpm drives, or None when the host carries none.

chosen: bool = False

Whether --sudo-command or the sudo_command key named the escalator.

path: str | None = None

Where the binary of the escalator resolves on PATH.

skipped: tuple[tuple[str, str], ...] = ()

Escalators on PATH that auto-detection passed over, each with what Escalator.identity() found: stand-in or unrunnable.

identity: str = 'genuine'

What Escalator.identity() found for the escalator.

release: TokenizedString | None = None

The installed release, for an escalator that reads one.

missing_options: tuple[str, ...] = ()

The gated options the installed release predates.

credentials: str = ''

What the credential probe found: ready, passwordless, denied, cold or unrunnable, or empty when nothing was probed.

answer: str = ''

The first line the credential probe printed.

prompt_kept: bool | None = None

Whether a password prompt would authorize the commands after it, or None where the probe cannot tell.

lines()[source]

The report, one fact per line.

Return type:

list[str]

meta_package_manager.sudo.diagnose_escalation(managers)[source]

Report how escalation stands on this host, without prompting.

Runs the probes prime_sudo() runs, in the same non-interactive shape: the identity check of the escalators auto-detection passed over, the release read and the credential probe. managers are the selected ones, all operations included: escalation belongs to the host, not to the managers one subcommand reaches.

Return type:

EscalationDiagnosis