mas-cli / mas-cli/mas

Support restricted `sudoers` rules

Open
#1,302 1 comment 0 reactions 1 assignee View on GitHub

@rgoldberg is already working on this.

Since Aug 12, 2026.

bug
Dominant language
Swift
Stars
12.4k
Forks
299
Avg merge
1d 2h
Merged PRs (30d)
1

Description

Problem(s) addressed

request is for a least-privilege boundary for running specific mas install or update operations non-interactively.

The released 7.0 implementation and the privilege implementation currently on main expose different problems for restricted NOPASSWD configurations:

  • 7.0 re-executes mas through sudo with an internal environment assignment, which requires an additional sudoers environment exception.
  • Current main now invokes privileged system tools directly. That removes the environment-assignment failure, but an exact rule authorizing the requested mas command no longer authorizes the commands that mas passes to sudo.

The default interactive behavior can remain unchanged. This request is for a supported interface for automation that deliberately grants only a specific MAS operation, rather than broad passwordless root access.

Observed behavior in 7.0

I use an exact rule shaped like this for a declared application:

<user> ALL=(root) NOPASSWD: <mas-path> update --force 803453959

When an update requires elevation, MAS 7.0 actually executes the equivalent of:

$ sudo MAS_NO_AUTO_INDEX=1 <mas-path> update --force 803453959

sudo therefore rejects the internal environment assignment even though the mas command itself is authorized:

sudo: sorry, you are not allowed to set the following environment variables: MAS_NO_AUTO_INDEX

This comes from the 7.0 elevation implementation:

https://github.com/mas-cli/mas/blob/v7.0.0/Sources/mas/Utilities/Processes/Sudo.swift#L20-L29

A command-specific workaround is possible:

Defaults!<mas-path> env_keep += "MAS_NO_AUTO_INDEX"

However, that makes the external privilege policy depend on an internal mas optimization and does not address the privilege boundary used by current main.

Remaining gap on current main

The privilege-handling changes merged for the 7.1 release (as it currently stands from what I see) no longer re-execute the MAS binary as root.

$ sudo /usr/sbin/installer -dumplog -pkg <dynamic-temporary-path> -target /
$ sudo /bin/sh -c '<copy/chown/chmod receipt script>' -- <dynamic-paths...>

Relevant code:

Based on that merged code, the exact NOPASSWD rule for
<mas-path> update --force 803453959 cannot match these child sudo commands.

A policy would instead need to authorize installer with dynamic package paths and a root shell command with dynamic receipt paths. Such rules are broader, more brittle across MAS releases, and no longer express the intended policy:
"allow MAS to update this app ID."

This also leaves the restricted rule suggested in the discussion of #1095 without an equivalent supported shape for the new privilege model:

https://github.com/mas-cli/mas/issues/1095#issuecomment-3592153547

Motivation

This matters for brew bundle, declarative workstation configuration, and other unattended update workflows. Homebrew itself cannot safely be run as root, while granting broad passwordless access to generic system tools defeats the purpose of using an app-specific sudoers rule.

Proposed solution(s)

a supported way to authorize a specific MAS operation and app ID without requiring any of the following:

  • SETENV or a global environment exception;
  • broad passwordless access to /usr/sbin/installer;
  • passwordless access to /bin/sh with dynamic arguments;
  • running Homebrew or the entire caller as root; or
  • policies coupled to MAS temporary paths and internal subprocesses.

For example, MAS could expose a root-owned privileged helper whose stable command line contains the requested operation and app IDs. The helper could validate and manage installer packages and receipts internally. A restricted policy could then remain shaped like:

<user> ALL=(root) NOPASSWD: <mas-helper-path> update 803453959
Related

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.