opencode-polkit 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,53 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-11-05
9
+
10
+ ### Added
11
+
12
+ - `sudo -n`, `doas -n`: a non-interactive `pkcheck` on
13
+ `org.freedesktop.policykit.exec` decides whether the command runs. polkit grants
14
+ it without asking, or the shim exits `1` with `sudo: a password is required` and
15
+ the reason that dropping `-n` lets the agent answer. Measured: a successful
16
+ `pkexec` leaves no temporary authorization behind (`pkcheck --list-temp` stays
17
+ empty), so the silent second run is the agent's own password cache and no
18
+ non-interactive query can predict it; `-n` therefore reports policy, not a
19
+ guess.
20
+ - `sudo -v` performs the authentication (`pkcheck --allow-user-interaction`) and
21
+ exits `0`, `1`, `126` when the dialog is dismissed, or `127`; `sudo -nv` uses
22
+ the non-interactive check.
23
+ - `sudo -l` prints `pkaction --verbose` for the action and this session's
24
+ temporary authorizations.
25
+ - `sudo -k`/`-K` revoke polkit's temporary authorizations and say that an agent's
26
+ own password cache cannot be cleared from here.
27
+ - `sudo -H`, `-N`, `-P` are accepted as no-ops: pkexec already sets `HOME`, keeps
28
+ no timestamp and leaves groups alone.
29
+ - `sudo -s COMMAND` and `--shell` run the command through a shell, `sudo -b`
30
+ starts it in the background and returns, and `sudo -E`/
31
+ `--preserve-env[=LIST]` re-export the environment inside the command.
32
+ - `sudo --help` and `sudo -V` are answered by the real binary, so scripts that
33
+ probe sudo keep working.
34
+ - Short options bundle (`sudo -nu bob`), and unknown long options report their own
35
+ name instead of `--`.
36
+ - `opencode-polkit-env`: helper that turns `NAME=VALUE` arguments into export
37
+ statements. `-E` passes entries through argv (`env -0` into `xargs -0`), so
38
+ spaces, quotes and newlines survive, and it drops every shim directory from
39
+ `PATH` so an elevated command cannot resolve sudo back to a shim.
40
+ - `src/index.ts`: the shell tool's description gains one sentence naming the
41
+ redirect and what `-n`, `-v` and `-l` answer, so the model plans privilege with
42
+ the real contract instead of learning it from a failed call.
43
+
44
+ ### Changed
45
+
46
+ - The shims share one prologue: `fail`, the polkit subject (pinned with
47
+ `pid,start-time,uid` as `pkcheck(1)` requires), the two checks and the `pkexec`
48
+ wrapper.
49
+ - `-n` failures exit `1` like sudo, not `64`: `64` now means only that the
50
+ invocation cannot be expressed. README documents the four-way handling
51
+ (translated, no-op, checked, refused) and polkit's missing temp authorization.
52
+ - `sudo -i`, `-S`, `-A`, `sudoedit` and `visudo` still refuse, now from the shared
53
+ prelude.
54
+
8
55
  ## [0.3.1] - 2026-11-04
9
56
 
10
57
  ### Added
package/README.md CHANGED
@@ -48,8 +48,16 @@ For a local checkout, run `npm run build` once, then reference the directory:
48
48
  | `sudo apt install x` | polkit dialog, then runs as root |
49
49
  | `sudo -u bob …` | as above, as `bob` |
50
50
  | `cat x \| sudo tee y` | dialog for `tee` |
51
- | `sudo -n …` | refused: pkexec has no non-interactive mode |
52
- | `sudo -i`, `-s`, `-b` | refused, with the equivalent command |
51
+ | `sudo -n …` | runs only if polkit would authorise without asking; otherwise exit 1 with `sudo: a password is required` |
52
+ | `sudo -v` | performs the authentication, as `sudo -v` does |
53
+ | `sudo -l` | prints the polkit policy and this session's temporary authorizations |
54
+ | `sudo -k`, `-K` | revokes polkit's temporary authorizations |
55
+ | `sudo -H`, `-N`, `-P` | accepted: pkexec already does what they ask |
56
+ | `sudo -s CMD` | runs `CMD` through a shell, as root |
57
+ | `sudo -E …` | runs with the caller's environment |
58
+ | `sudo -b …` | starts the command in the background |
59
+ | `sudo --help`, `-V` | answered by the real sudo; nothing elevates |
60
+ | `sudo -i`, `-S`, `-A` | refused, with the equivalent command |
53
61
  | `sudoedit`, `visudo` | refused, with the alternative |
54
62
  | `su -c 'id'` | dialog, then runs as root |
55
63
  | `su -c 'id' bob` | as above, as `bob` (administrator password) |
@@ -57,7 +65,9 @@ For a local checkout, run `npm run build` once, then reference the directory:
57
65
  | `su -m`, `-s`, `-g`, `-w`, `--pty` | refused, with the alternative |
58
66
  | `pkexec …` | passed through |
59
67
 
60
- Refused invocations exit with status 64 and one line naming the alternative.
68
+ An option is refused only when polkit cannot express it: it needs a terminal, a
69
+ password channel polkit does not have, or an editor. Refusals exit with status 64
70
+ and one line naming the alternative.
61
71
 
62
72
  ## How it works
63
73
 
@@ -66,6 +76,16 @@ resolves those instead of the system binaries, and they forward the command to
66
76
  `pkexec`. Command text is never rewritten, so quoting, pipes and subshells need
67
77
  no special handling.
68
78
 
79
+ The `create.before` shell hook prepends the stand-in directory to the spawned
80
+ `PATH`. The directory lives in `$XDG_RUNTIME_DIR` (falling back to `/tmp`) as
81
+ `0700`, is removed when the plugin is unloaded, and also holds
82
+ `opencode-polkit-env`, the helper `sudo -E` uses to re-export an environment
83
+ pkexec stripped.
84
+
85
+ The shell tool's description gains one sentence about the redirect, so the model
86
+ plans privilege with the real contract instead of learning it from a failed
87
+ call.
88
+
69
89
  ## Authorisation
70
90
 
71
91
  `polkitd` decides whether a password is required. These commands always require
@@ -79,6 +99,14 @@ session then runs without a dialog. That cache belongs to the agent: another
79
99
  agent, a restarted agent or a dismissed dialog means the next command prompts
80
100
  again, so do not rely on it.
81
101
 
102
+ Measured on polkit 128 with `polkit-gnome`'s agent: after a successful `pkexec`,
103
+ `pkcheck --list-temp` prints nothing and a non-interactive `pkcheck` still
104
+ reports that authentication is required. polkit records no temporary
105
+ authorization for this action, so the silent second run is the agent answering
106
+ from its own copy of the password, and no user-space query can predict it. That
107
+ is why `sudo -n` reports what the policy says rather than guessing whether a
108
+ dialog would appear, and why `sudo -k` can only clear polkit's own record.
109
+
82
110
  ## Limitations
83
111
 
84
112
  - Absolute paths (`/usr/bin/sudo`) and relative ones (`./sudo`) bypass `PATH`.
@@ -87,6 +115,11 @@ again, so do not rely on it.
87
115
  - `su -c … USER` authenticates an administrator (polkit), not `USER`, so an
88
116
  account that real `su` would allow can be refused here.
89
117
  - `su -c` runs the command with `sh -c`, not with the target user's login shell.
118
+ - `sudo -E` re-exports the environment through `pkexec`'s argument list, so the
119
+ values are visible in `ps` while the command starts. Exported shell functions
120
+ are skipped, and `PATH` loses the shim directories.
121
+ - `sudo -k` cannot clear the password a desktop agent holds; only polkit's
122
+ temporary authorizations are revoked.
90
123
 
91
124
  These bypass the redirect, not polkit, which still decides whether the command
92
125
  runs.
@@ -98,11 +131,12 @@ without one, `pkexec` fails instead of prompting.
98
131
 
99
132
  ## Exit status
100
133
 
101
- | Status | Meaning |
102
- |--------|----------------------------------------------|
103
- | 126 | the polkit dialog was dismissed |
104
- | 127 | not authorised, or the program could not run |
105
- | 64 | the plugin refused the invocation |
134
+ | Status | Meaning |
135
+ |--------|--------------------------------------------------------|
136
+ | 1 | `-n` or `-v` could not be satisfied: polkit requires authentication, or refuses |
137
+ | 126 | the polkit dialog was dismissed |
138
+ | 127 | not authorised, or the program could not run |
139
+ | 64 | the plugin refused the invocation |
106
140
 
107
141
  Messages are in English.
108
142
 
package/dist/index.js CHANGED
@@ -4,6 +4,16 @@ import { installPolkitShims, removePolkitShims } from "./shims.js";
4
4
  * Only the prepend matters; the rest is a last-resort fallback.
5
5
  */
6
6
  const FALLBACK_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
7
+ /** Tool names whose description should name how elevation works here. */
8
+ const SHELL_TOOLS = ["bash", "shell"];
9
+ /** Opening words of the appended description; also the guard against appending twice. */
10
+ const CONTRACT_MARKER = "polkit redirect:";
11
+ /**
12
+ * Appended to the shell tool's description. The model plans privilege with what
13
+ * it knows about sudo, so the difference is stated where it reads the tool,
14
+ * instead of being learned from a failed call.
15
+ */
16
+ const SHELL_TOOL_CONTRACT = `${CONTRACT_MARKER} \`sudo\`, \`doas\` and \`su -c\` run through pkexec, so a desktop polkit dialog authorises them instead of a terminal password prompt. \`sudo -n\` runs the command only if polkit would authorise it without asking, \`sudo -v\` performs the authentication, and \`sudo -l\` prints the polkit policy that decides.`;
7
17
  /**
8
18
  * Redirects sudo/doas/su -c to pkexec without touching the command text.
9
19
  *
@@ -26,6 +36,20 @@ const setup = async (ctx) => {
26
36
  if (!path.startsWith(prefix))
27
37
  event.env.PATH = prefix + path;
28
38
  });
39
+ // The shell tool is the host's, so its description is extended rather than
40
+ // replaced. Every reload runs this transform again, so a description that
41
+ // already carries the line is left alone.
42
+ await ctx.tool.transform((tools) => {
43
+ for (const tool of tools.list()) {
44
+ if (!SHELL_TOOLS.includes(tool.id) && !SHELL_TOOLS.includes(tool.name))
45
+ continue;
46
+ tools.update(tool.id, (entry) => {
47
+ if (entry.description.includes(CONTRACT_MARKER))
48
+ return;
49
+ entry.description = `${entry.description}\n\n${SHELL_TOOL_CONTRACT}`;
50
+ });
51
+ }
52
+ });
29
53
  return () => removePolkitShims(shimDir);
30
54
  };
31
55
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAElE;;;GAGG;AACH,MAAM,aAAa,GAAG,8DAA8D,CAAA;AAEpF;;;;;;;;;;;;GAYG;AACH,MAAM,KAAK,GAAG,KAAK,EAAE,GAAmB,EAA2B,EAAE;IACnE,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAA;IACpC,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,CAAA;IAE5B,8EAA8E;IAC9E,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,aAAa,CAAA;QAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAA;AACzC,CAAC,CAAA;AAED;;;GAGG;AACH,eAAe,EAAE,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAA0B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAElE;;;GAGG;AACH,MAAM,aAAa,GAAG,8DAA8D,CAAA;AAEpF,yEAAyE;AACzE,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAErC,yFAAyF;AACzF,MAAM,eAAe,GAAG,kBAAkB,CAAA;AAE1C;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,GAAG,eAAe,wTAAwT,CAAA;AAEtW;;;;;;;;;;;;GAYG;AACH,MAAM,KAAK,GAAG,KAAK,EAAE,GAAmB,EAA2B,EAAE;IACnE,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAA;IACpC,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,CAAA;IAE5B,8EAA8E;IAC9E,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,aAAa,CAAA;QAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,2EAA2E;IAC3E,0EAA0E;IAC1E,0CAA0C;IAC1C,MAAM,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QACjC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAQ;YAChF,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC9B,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,eAAe,CAAC;oBAAE,OAAM;gBACvD,KAAK,CAAC,WAAW,GAAG,GAAG,KAAK,CAAC,WAAW,OAAO,mBAAmB,EAAE,CAAA;YACtE,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAA;AACzC,CAAC,CAAA;AAED;;;GAGG;AACH,eAAe,EAAE,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAA0B,CAAA"}
package/dist/shims.d.ts CHANGED
@@ -9,7 +9,9 @@ export declare const SHIM_USAGE_EXIT = 64;
9
9
  * Writes the shim scripts into a fresh private directory and returns it.
10
10
  * The caller prepends this directory to PATH; the shell then resolves
11
11
  * `sudo`/`doas`/`su` to these files instead of the system ones. The directory
12
- * is 0700 so no other local user can rewrite the scripts.
12
+ * is 0700 so no other local user can rewrite the scripts, and it holds
13
+ * `opencode-polkit-env`, the helper `sudo -E` uses to re-export an environment
14
+ * pkexec stripped.
13
15
  */
14
16
  export declare function installPolkitShims(): string;
15
17
  /** Deletes a directory created by installPolkitShims. */
package/dist/shims.js CHANGED
@@ -8,82 +8,260 @@ import { isAbsolute, join } from "node:path";
8
8
  * any output.
9
9
  */
10
10
  export const SHIM_USAGE_EXIT = 64;
11
+ /** The action pkexec checks; the checks below ask polkit about the same one. */
12
+ const POLKIT_ACTION = "org.freedesktop.policykit.exec";
11
13
  /**
12
- * The sudo shim. Lives on PATH ahead of the real sudo, so the shell — not
13
- * this plugin — decides that `sudo` means this file. It forwards to pkexec
14
- * and never parses what pkexec prints: the exit status is the whole result.
15
- *
16
- * Only options with an exact pkexec equivalent are translated. Everything
17
- * else is refused with an actionable message rather than forwarded, because
18
- * pkexec would reject it with a message that does not say what to do instead.
14
+ * The part every shim starts with: one refusal helper, and a check that the two
15
+ * polkit tools exist at all.
19
16
  */
20
- const SUDO_SHIM = `#!/bin/sh
21
- # opencode-polkit: sudo shim. Routes sudo to pkexec so polkit can authorize it.
22
- # Generated at plugin startup; do not edit.
23
-
24
- fail() {
17
+ const SHIM_PRELUDE = `fail() {
25
18
  printf 'opencode-polkit: %s\\n' "$1" >&2
26
19
  exit ${SHIM_USAGE_EXIT}
27
20
  }
28
21
 
29
22
  command -v pkexec >/dev/null 2>&1 || fail 'pkexec is not installed; install polkit'
23
+ command -v pkcheck >/dev/null 2>&1 || fail 'pkcheck is not installed; install polkit'
24
+ `;
25
+ /**
26
+ * The checks that answer what sudo's timestamp file used to answer, plus the
27
+ * pkexec wrapper. Only exit statuses are read: pkcheck's messages and its
28
+ * `polkit.result` detail are localised and addressed to humans, so parsing them
29
+ * would break on the next locale or polkit release.
30
+ */
31
+ const SHIM_CHECKS = `# The action pkexec checks: a pkcheck verdict on it describes what the next
32
+ # pkexec will do, which is how the shims answer what sudo's timestamp answered.
33
+ ACTION=${POLKIT_ACTION}
30
34
 
35
+ # pkcheck(1) warns that a bare pid races with pid reuse, so pin the start time.
36
+ subject() {
37
+ started=$(awk '{ print $22 }' /proc/$$/stat 2>/dev/null)
38
+ if [ -n "$started" ]; then
39
+ printf '%s,%s,%s' "$$" "$started" "$(id -u)"
40
+ else
41
+ printf '%s' "$$"
42
+ fi
43
+ }
44
+
45
+ # 0 grants the action without asking anyone, 2 requires authentication,
46
+ # 1 refuses it. pkcheck's own 126 (usage) and 127 (error) pass through.
47
+ check_without_prompt() {
48
+ pkcheck --action-id "$ACTION" --process "$(subject)" >/dev/null 2>&1
49
+ }
50
+
51
+ # The same check with the desktop agent allowed to answer. An agent that still
52
+ # holds the password answers without a dialog, which is why a 0 here also means
53
+ # the next pkexec runs as root without asking.
54
+ check_with_prompt() {
55
+ pkcheck --action-id "$ACTION" --process "$(subject)" --allow-user-interaction >/dev/null 2>&1
56
+ }
57
+
58
+ # -n means "run only if no one has to be asked". polkit cannot say whether the
59
+ # agent would answer from its cache, so a 2 is reported as needing a password
60
+ # and the command is not run: that is what -n promises, in both directions.
61
+ require_without_prompt() {
62
+ if [ -n "$noninteractive" ]; then
63
+ check_without_prompt
64
+ status=$?
65
+ case $status in
66
+ 0)
67
+ return 0
68
+ ;;
69
+ 1)
70
+ printf 'opencode-polkit: %s: polkit denies %s to this process\\n' "$PROG" "$ACTION" >&2
71
+ exit 1
72
+ ;;
73
+ 2)
74
+ printf '%s: a password is required (polkit requires authentication for %s; drop -n to let the desktop agent answer, which it may do from its cache)\\n' "$PROG" "$ACTION" >&2
75
+ exit 1
76
+ ;;
77
+ *)
78
+ printf 'opencode-polkit: %s: polkit check failed (status %s)\\n' "$PROG" "$status" >&2
79
+ exit 127
80
+ ;;
81
+ esac
82
+ fi
83
+ return 0
84
+ }
85
+
86
+ # pkexec is silent when the dialog is dismissed (status 126), so say what the
87
+ # status means. Every other failure path already prints its own reason.
88
+ run_pkexec() {
89
+ pkexec "$@"
90
+ status=$?
91
+ if [ $status -eq 126 ]; then
92
+ printf 'opencode-polkit: %s\\n' 'the polkit authentication dialog was dismissed' >&2
93
+ fi
94
+ exit $status
95
+ }
96
+ `;
97
+ /**
98
+ * The sudo shim. Lives on PATH ahead of the real sudo, so the shell — not this
99
+ * plugin — decides that `sudo` means this file. It forwards to pkexec and
100
+ * never parses what pkexec prints: the exit status is the whole result.
101
+ *
102
+ * Options are handled in four ways, and only the last one refuses. Those whose
103
+ * effect pkexec already has (`-H`, `-N`, `-P`) are accepted as no-ops; those
104
+ * polkit answers better than a run (`-n`, `-v`, `-l`, `-k`) become checks; those
105
+ * a wrapper can express (`-s COMMAND`, `-E`, `-b`) are translated; the rest,
106
+ * which need a terminal or a password channel polkit does not have, are refused
107
+ * with one line naming the alternative.
108
+ */
109
+ const SUDO_SHIM = `#!/bin/sh
110
+ # opencode-polkit: sudo shim. Routes sudo to pkexec so polkit can authorize it.
111
+ # Generated at plugin startup; do not edit.
112
+
113
+ ${SHIM_PRELUDE}
114
+ ${SHIM_CHECKS}
115
+ PROG=sudo
31
116
  user=
117
+ noninteractive=
118
+ preserve=
119
+ preserve_names=
120
+ background=
121
+ mode=
122
+
32
123
  while [ $# -gt 0 ]; do
33
124
  case $1 in
34
125
  --)
35
126
  shift
36
127
  break
37
128
  ;;
38
- -u)
39
- [ $# -ge 2 ] || fail 'sudo -u needs a username: -u USER PROGRAM'
40
- user=$2
41
- shift 2
42
- ;;
43
129
  --user)
44
130
  [ $# -ge 2 ] || fail 'sudo --user needs a username: --user USER PROGRAM'
45
131
  user=$2
46
132
  shift 2
47
133
  ;;
48
- -u?*)
49
- user=\${1#-u}
50
- [ -n "$user" ] || fail 'sudo -u needs a username: -u USER PROGRAM'
51
- shift
52
- ;;
53
134
  --user=*)
54
135
  user=\${1#--user=}
55
- [ -n "$user" ] || fail 'sudo --user needs a username: --user USER PROGRAM'
56
136
  shift
57
137
  ;;
58
- -n | --non-interactive)
59
- fail 'sudo -n is not supported: pkexec has no non-interactive mode, polkit decides whether authentication is needed. Drop -n.'
138
+ --non-interactive)
139
+ noninteractive=1
140
+ shift
141
+ ;;
142
+ --preserve-env)
143
+ preserve=1
144
+ shift
145
+ ;;
146
+ --preserve-env=*)
147
+ preserve=1
148
+ preserve_names=\${1#--preserve-env=}
149
+ shift
150
+ ;;
151
+ --set-home | --no-update | --preserve-groups)
152
+ shift
60
153
  ;;
61
- -v | --validate | -k | --reset-timestamp | -K | --remove-timestamp)
62
- fail 'sudo credential caching does not exist under polkit; drop -v/-k.'
154
+ --list)
155
+ mode=list
156
+ shift
63
157
  ;;
64
- -l | --list)
65
- fail 'sudo -l is not available under polkit; inspect policy with pkaction --verbose.'
158
+ --validate)
159
+ mode=validate
160
+ shift
66
161
  ;;
67
- -e | --edit)
68
- fail 'sudoedit is not supported; write a temp file and run pkexec install or pkexec cp on it.'
162
+ --reset-timestamp | --remove-timestamp)
163
+ mode=revoke
164
+ shift
69
165
  ;;
70
- -i | --login | -s | --shell)
71
- fail 'sudo -i/-s opens a login shell; run pkexec bash instead.'
166
+ --background)
167
+ background=1
168
+ shift
72
169
  ;;
73
- -S | --stdin)
74
- fail 'sudo -S reads the password from stdin; pkexec authenticates through the polkit agent. Drop -S.'
170
+ --help)
171
+ mode=help
172
+ shift
75
173
  ;;
76
- -A | --askpass)
77
- fail 'SUDO_ASKPASS does not apply to pkexec; drop -A.'
174
+ --version)
175
+ mode=version
176
+ shift
78
177
  ;;
79
- -b | --background)
80
- fail 'sudo -b backgrounds the command; append & instead.'
178
+ --stdin | --askpass | --edit | --login)
179
+ case $1 in
180
+ --edit)
181
+ fail 'sudoedit is not supported; write a temp file and run pkexec install or pkexec cp on it.'
182
+ ;;
183
+ --stdin)
184
+ fail 'sudo -S reads the password from stdin; pkexec authenticates through the polkit agent. Drop -S.'
185
+ ;;
186
+ --askpass)
187
+ fail 'SUDO_ASKPASS does not apply to pkexec; drop -A.'
188
+ ;;
189
+ *)
190
+ fail 'sudo -i opens a login shell; run pkexec bash instead.'
191
+ ;;
192
+ esac
193
+ ;;
194
+ --shell)
195
+ mode=shell
196
+ shift
81
197
  ;;
82
- -E | --preserve-env | --preserve-env=* | -H | --set-home | -P | --preserve-groups | -N | --no-update)
83
- fail 'pkexec deliberately sanitizes the environment; drop -E/-H/-P.'
198
+ --*)
199
+ fail "option has no pkexec equivalent: $1 (pkexec accepts --user, --keep-cwd, --disable-internal-agent)"
84
200
  ;;
85
201
  -?*)
86
- fail "option has no pkexec equivalent: $1 (pkexec accepts --user, --keep-cwd, --disable-internal-agent)"
202
+ rest=\${1#-}
203
+ shift
204
+ while [ -n "$rest" ]; do
205
+ letter=\${rest%"\${rest#?}"}
206
+ rest=\${rest#?}
207
+ case $letter in
208
+ u)
209
+ if [ -n "$rest" ]; then
210
+ user=$rest
211
+ rest=
212
+ else
213
+ [ $# -ge 1 ] || fail 'sudo -u needs a username: -u USER PROGRAM'
214
+ user=$1
215
+ shift
216
+ fi
217
+ ;;
218
+ n)
219
+ noninteractive=1
220
+ ;;
221
+ E)
222
+ preserve=1
223
+ ;;
224
+ H | N | P)
225
+ # pkexec already sets HOME, keeps no timestamp and leaves groups alone.
226
+ ;;
227
+ b)
228
+ background=1
229
+ ;;
230
+ l)
231
+ mode=list
232
+ ;;
233
+ v)
234
+ mode=validate
235
+ ;;
236
+ k | K)
237
+ mode=revoke
238
+ ;;
239
+ s)
240
+ mode=shell
241
+ ;;
242
+ h)
243
+ mode=help
244
+ ;;
245
+ V)
246
+ mode=version
247
+ ;;
248
+ i)
249
+ fail 'sudo -i opens a login shell; run pkexec bash instead.'
250
+ ;;
251
+ e)
252
+ fail 'sudoedit is not supported; write a temp file and run pkexec install or pkexec cp on it.'
253
+ ;;
254
+ S)
255
+ fail 'sudo -S reads the password from stdin; pkexec authenticates through the polkit agent. Drop -S.'
256
+ ;;
257
+ A)
258
+ fail 'SUDO_ASKPASS does not apply to pkexec; drop -A.'
259
+ ;;
260
+ *)
261
+ fail "option has no pkexec equivalent: -$letter (pkexec accepts --user, --keep-cwd, --disable-internal-agent)"
262
+ ;;
263
+ esac
264
+ done
87
265
  ;;
88
266
  *)
89
267
  break
@@ -91,20 +269,148 @@ while [ $# -gt 0 ]; do
91
269
  esac
92
270
  done
93
271
 
94
- # pkexec is silent when the dialog is dismissed (status 126), so say what the
95
- # status means. Every other failure path already prints its own reason.
96
- run_pkexec() {
97
- pkexec "$@"
272
+ # --help and --version describe the binary, not the authorisation, so nothing is
273
+ # elevated: answering them exactly keeps scripts that probe sudo working.
274
+ if [ "$mode" = help ] || [ "$mode" = version ]; then
275
+ for candidate in /usr/bin/sudo /bin/sudo /usr/local/bin/sudo; do
276
+ [ -x "$candidate" ] || continue
277
+ if [ "$mode" = help ]; then
278
+ exec "$candidate" --help
279
+ fi
280
+ exec "$candidate" --version
281
+ done
282
+ fail 'the real sudo is not installed, so --help and --version cannot be answered'
283
+ fi
284
+
285
+ # -l reports the policy polkit will apply and the authorizations this session
286
+ # already holds. Both halves are local and never prompt.
287
+ if [ "$mode" = list ]; then
288
+ pkaction --action-id "$ACTION" --verbose || exit 127
289
+ printf '\\nopencode-polkit: temporary authorizations in this session\\n'
290
+ pkcheck --list-temp || exit 127
291
+ exit 0
292
+ fi
293
+
294
+ # -v asks the same question sudo -v asks: can authentication succeed right now?
295
+ if [ "$mode" = validate ]; then
296
+ if [ -n "$noninteractive" ]; then
297
+ check_without_prompt
298
+ else
299
+ check_with_prompt
300
+ fi
98
301
  status=$?
99
- if [ $status -eq 126 ]; then
100
- printf 'opencode-polkit: %s\n' 'the polkit authentication dialog was dismissed' >&2
302
+ case $status in
303
+ 0)
304
+ exit 0
305
+ ;;
306
+ 1)
307
+ printf 'opencode-polkit: sudo: polkit denies %s to this process\\n' "$ACTION" >&2
308
+ exit 1
309
+ ;;
310
+ 2)
311
+ if [ -n "$noninteractive" ]; then
312
+ printf 'sudo: a password is required (polkit requires authentication for %s)\\n' "$ACTION" >&2
313
+ else
314
+ printf 'opencode-polkit: sudo: no polkit authentication agent answered\\n' >&2
315
+ fi
316
+ exit 1
317
+ ;;
318
+ 3)
319
+ printf 'opencode-polkit: %s\\n' 'the polkit authentication dialog was dismissed' >&2
320
+ exit 126
321
+ ;;
322
+ *)
323
+ printf 'opencode-polkit: sudo: polkit check failed (status %s)\\n' "$status" >&2
324
+ exit 127
325
+ ;;
326
+ esac
327
+ fi
328
+
329
+ # -k drops what polkit itself recorded. The password a desktop agent keeps is
330
+ # the agent's, not polkit's, so say that it stays.
331
+ if [ "$mode" = revoke ]; then
332
+ pkcheck --revoke-temp || exit 127
333
+ if [ $# -eq 0 ]; then
334
+ printf 'opencode-polkit: %s\\n' 'revoked the temporary polkit authorizations; a desktop agent that caches the password keeps its own copy, so the next command may still not ask' >&2
335
+ exit 0
101
336
  fi
102
- exit $status
337
+ fi
338
+
339
+ # -E: pkexec strips the environment, so name the variables again inside the shell
340
+ # that runs the command. Entries reach the helper as arguments, so spaces, quotes
341
+ # and newlines survive exactly; the helper also drops every shim directory from
342
+ # PATH, so an elevated command cannot resolve sudo back to a shim.
343
+ shim_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd 2>/dev/null)
344
+ env_helper=$shim_dir/opencode-polkit-env
345
+ [ -x "$env_helper" ] || fail "the opencode-polkit environment helper is missing: $env_helper"
346
+
347
+ # Runs in the caller's shell, never in a command substitution: an exit there
348
+ # would only end the subshell, and a name reaches eval below, so it must be a
349
+ # name before that happens.
350
+ check_preserve() {
351
+ if [ -z "$preserve_names" ]; then
352
+ command -v xargs >/dev/null 2>&1 || fail '-E needs xargs to forward the environment; name the variables instead: --preserve-env=NAME[,NAME...]'
353
+ return 0
354
+ fi
355
+ old_ifs=$IFS
356
+ IFS=,
357
+ for name in $preserve_names; do
358
+ case $name in
359
+ '' | [0-9]* | *[!A-Za-z0-9_]*)
360
+ IFS=$old_ifs
361
+ fail "not a variable name: $name"
362
+ ;;
363
+ esac
364
+ done
365
+ IFS=$old_ifs
103
366
  }
104
367
 
368
+ export_prefix() {
369
+ if [ -n "$preserve_names" ]; then
370
+ set --
371
+ old_ifs=$IFS
372
+ IFS=,
373
+ for name in $preserve_names; do
374
+ is_set=
375
+ eval "is_set=\\\${$name+yes}"
376
+ [ -n "$is_set" ] || continue
377
+ eval "value=\\\${$name}"
378
+ set -- "$@" "$name=$value"
379
+ done
380
+ IFS=$old_ifs
381
+ "$env_helper" "$@"
382
+ return
383
+ fi
384
+ env -0 | xargs -0 -n 64 "$env_helper"
385
+ }
386
+
387
+ require_without_prompt
388
+
389
+ [ $# -gt 0 ] || fail 'sudo needs a command: sudo [OPTIONS] COMMAND (an interactive shell needs a terminal)'
390
+
391
+ # -s runs the command through a shell, which pkexec expresses exactly.
392
+ if [ "$mode" = shell ]; then
393
+ set -- /bin/sh -c "$*"
394
+ fi
395
+
396
+ # The preserved environment is exported by a shell prologue, so the command keeps
397
+ # its own argv rather than being flattened into one string.
398
+ if [ -n "$preserve" ]; then
399
+ check_preserve
400
+ prefix=$(export_prefix)
401
+ set -- /bin/sh -c "\${prefix}exec \\"\\$@\\"" sh "$@"
402
+ fi
403
+
105
404
  if [ -n "$user" ]; then
106
- run_pkexec --user "$user" "$@"
405
+ set -- --user "$user" "$@"
406
+ fi
407
+
408
+ # -b returns as soon as the command is started, as sudo -b does.
409
+ if [ -n "$background" ]; then
410
+ pkexec "$@" &
411
+ exit 0
107
412
  fi
413
+
108
414
  run_pkexec "$@"
109
415
  `;
110
416
  /** The doas shim: same contract, doas option grammar. */
@@ -112,14 +418,13 @@ const DOAS_SHIM = `#!/bin/sh
112
418
  # opencode-polkit: doas shim. Routes doas to pkexec so polkit can authorize it.
113
419
  # Generated at plugin startup; do not edit.
114
420
 
115
- fail() {
116
- printf 'opencode-polkit: %s\\n' "$1" >&2
117
- exit ${SHIM_USAGE_EXIT}
118
- }
119
-
120
- command -v pkexec >/dev/null 2>&1 || fail 'pkexec is not installed; install polkit'
121
-
421
+ ${SHIM_PRELUDE}
422
+ ${SHIM_CHECKS}
423
+ PROG=doas
122
424
  user=
425
+ noninteractive=
426
+ mode=
427
+
123
428
  while [ $# -gt 0 ]; do
124
429
  case $1 in
125
430
  --)
@@ -133,14 +438,15 @@ while [ $# -gt 0 ]; do
133
438
  ;;
134
439
  -u?*)
135
440
  user=\${1#-u}
136
- [ -n "$user" ] || fail 'doas -u needs a username: -u USER PROGRAM'
137
441
  shift
138
442
  ;;
139
443
  -n)
140
- fail 'doas -n is not supported: pkexec has no non-interactive mode. Drop -n.'
444
+ noninteractive=1
445
+ shift
141
446
  ;;
142
447
  -s)
143
- fail 'doas -s opens a shell; run pkexec bash instead.'
448
+ mode=shell
449
+ shift
144
450
  ;;
145
451
  -C | -a | -L)
146
452
  fail "option has no pkexec equivalent: $1"
@@ -154,20 +460,18 @@ while [ $# -gt 0 ]; do
154
460
  esac
155
461
  done
156
462
 
157
- # pkexec is silent when the dialog is dismissed (status 126), so say what the
158
- # status means. Every other failure path already prints its own reason.
159
- run_pkexec() {
160
- pkexec "$@"
161
- status=$?
162
- if [ $status -eq 126 ]; then
163
- printf 'opencode-polkit: %s\n' 'the polkit authentication dialog was dismissed' >&2
164
- fi
165
- exit $status
166
- }
463
+ require_without_prompt
464
+
465
+ [ $# -gt 0 ] || fail 'doas needs a command: doas [OPTIONS] COMMAND (an interactive shell needs a terminal)'
466
+
467
+ if [ "$mode" = shell ]; then
468
+ set -- /bin/sh -c "$*"
469
+ fi
167
470
 
168
471
  if [ -n "$user" ]; then
169
- run_pkexec --user "$user" "$@"
472
+ set -- --user "$user" "$@"
170
473
  fi
474
+
171
475
  run_pkexec "$@"
172
476
  `;
173
477
  /**
@@ -180,13 +484,9 @@ const SU_SHIM = `#!/bin/sh
180
484
  # opencode-polkit: su shim. Routes su -c to pkexec so polkit can authorize it.
181
485
  # Generated at plugin startup; do not edit.
182
486
 
183
- fail() {
184
- printf 'opencode-polkit: %s\\n' "$1" >&2
185
- exit ${SHIM_USAGE_EXIT}
186
- }
187
-
188
- command -v pkexec >/dev/null 2>&1 || fail 'pkexec is not installed; install polkit'
189
-
487
+ ${SHIM_PRELUDE}
488
+ ${SHIM_CHECKS}
489
+ PROG=su
190
490
  user=
191
491
  cmd=
192
492
  has_cmd=
@@ -234,7 +534,7 @@ while [ $# -gt 0 ]; do
234
534
  fail 'su -w whitelists environment variables; pkexec sanitizes the environment.'
235
535
  ;;
236
536
  -P | --pty)
237
- fail 'su --pty asks for a terminal; the shell tool has none.'
537
+ fail 'su --pty asks for a terminal; the bash tool has none.'
238
538
  ;;
239
539
  -f | --fast)
240
540
  fail 'su -f is a shell flag; pass it to the shell in the command instead.'
@@ -267,24 +567,68 @@ done
267
567
  [ -n "$has_cmd" ] || fail 'su without -c opens an interactive shell; run pkexec bash instead.'
268
568
 
269
569
  # su keeps the working directory unless it is a login shell, and --keep-cwd is
270
- # how pkexec does the same. pkexec is silent when the dialog is dismissed
271
- # (status 126), so say what the status means.
272
- pkexec --keep-cwd --user "$user" sh -c "$cmd"
273
- status=$?
274
- if [ $status -eq 126 ]; then
275
- printf 'opencode-polkit: %s\\n' 'the polkit authentication dialog was dismissed' >&2
276
- fi
277
- exit $status
570
+ # how pkexec does the same.
571
+ run_pkexec --keep-cwd --user "$user" sh -c "$cmd"
572
+ `;
573
+ /**
574
+ * The environment helper: turns `NAME=VALUE` arguments into an export statement
575
+ * each, so `-E` can rebuild the environment pkexec stripped. Values arrive as
576
+ * arguments rather than text, so spaces, quotes and newlines need no escaping on
577
+ * the way in. PATH keeps every entry except the shim directories.
578
+ */
579
+ const POLKIT_ENV_HELPER = `#!/bin/sh
580
+ # opencode-polkit: rebuilds a stripped environment. Generated at plugin startup;
581
+ # do not edit.
582
+
583
+ for entry in "$@"; do
584
+ name=\${entry%%=*}
585
+ value=\${entry#*=}
586
+ case $name in
587
+ '' | [0-9]* | *[!A-Za-z0-9_]*)
588
+ continue
589
+ ;;
590
+ PATH)
591
+ filtered=
592
+ old_ifs=$IFS
593
+ IFS=:
594
+ for dir in $value; do
595
+ case \${dir##*/} in
596
+ opencode-polkit-*)
597
+ continue
598
+ ;;
599
+ esac
600
+ filtered=$filtered\${filtered:+:}$dir
601
+ done
602
+ IFS=$old_ifs
603
+ value=$filtered
604
+ ;;
605
+ esac
606
+ quote="'"
607
+ quoted=
608
+ while :; do
609
+ case $value in
610
+ *"$quote"*)
611
+ quoted=$quoted\${value%%"$quote"*}"'\\\\''"
612
+ value=\${value#*"$quote"}
613
+ ;;
614
+ *)
615
+ quoted=$quoted$value
616
+ break
617
+ ;;
618
+ esac
619
+ done
620
+ printf "export '%s=%s'; " "$name" "$quoted"
621
+ done
278
622
  `;
279
623
  /** sudoedit edits through sudo's own temporary-file flow; polkit has none. */
280
624
  const SUDOEDIT_SHIM = `#!/bin/sh
281
- printf 'opencode-polkit: sudoedit is not supported; write a temp file and run pkexec install or pkexec cp on it.\\n' >&2
282
- exit ${SHIM_USAGE_EXIT}
625
+ ${SHIM_PRELUDE}
626
+ fail 'sudoedit is not supported; write a temp file and run pkexec install or pkexec cp on it.'
283
627
  `;
284
628
  /** visudo needs an interactive editor on a terminal the shell tool does not have. */
285
629
  const VISUDO_SHIM = `#!/bin/sh
286
- printf 'opencode-polkit: visudo needs an interactive terminal; validate a candidate file with pkexec visudo -c -f FILE instead.\\n' >&2
287
- exit ${SHIM_USAGE_EXIT}
630
+ ${SHIM_PRELUDE}
631
+ fail 'visudo needs an interactive terminal; validate a candidate file with pkexec visudo -c -f FILE instead.'
288
632
  `;
289
633
  /** File name → script body. Names must match the commands being redirected. */
290
634
  const POLKIT_SHIM_SCRIPTS = {
@@ -293,12 +637,15 @@ const POLKIT_SHIM_SCRIPTS = {
293
637
  su: SU_SHIM,
294
638
  sudoedit: SUDOEDIT_SHIM,
295
639
  visudo: VISUDO_SHIM,
640
+ "opencode-polkit-env": POLKIT_ENV_HELPER,
296
641
  };
297
642
  /**
298
643
  * Writes the shim scripts into a fresh private directory and returns it.
299
644
  * The caller prepends this directory to PATH; the shell then resolves
300
645
  * `sudo`/`doas`/`su` to these files instead of the system ones. The directory
301
- * is 0700 so no other local user can rewrite the scripts.
646
+ * is 0700 so no other local user can rewrite the scripts, and it holds
647
+ * `opencode-polkit-env`, the helper `sudo -E` uses to re-export an environment
648
+ * pkexec stripped.
302
649
  */
303
650
  export function installPolkitShims() {
304
651
  const directory = mkdtempSync(join(shimBaseDirectory(), "opencode-polkit-"));
package/dist/shims.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"shims.js","sourceRoot":"","sources":["../src/shims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AAEjC;;;;;;;;GAQG;AACH,MAAM,SAAS,GAAG;;;;;;QAMV,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFtB,CAAA;AAED,yDAAyD;AACzD,MAAM,SAAS,GAAG;;;;;;QAMV,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDtB,CAAA;AAED;;;;;GAKG;AACH,MAAM,OAAO,GAAG;;;;;;QAMR,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6FtB,CAAA;AAED,8EAA8E;AAC9E,MAAM,aAAa,GAAG;;OAEf,eAAe;CACrB,CAAA;AAED,qFAAqF;AACrF,MAAM,WAAW,GAAG;;OAEb,eAAe;CACrB,CAAA;AAED,+EAA+E;AAC/E,MAAM,mBAAmB,GAA2B;IAClD,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,SAAS;IACf,EAAE,EAAE,OAAO;IACX,QAAQ,EAAE,aAAa;IACvB,MAAM,EAAE,WAAW;CACpB,CAAA;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAA;IAC5E,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;QAClC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC3B,mEAAmE;QACnE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACxB,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IAC3C,OAAO,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;AACnF,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AACrD,CAAC"}
1
+ {"version":3,"file":"shims.js","sourceRoot":"","sources":["../src/shims.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-polkit",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Redirect sudo, doas and su to pkexec in OpenCode via PATH shims, so the system polkit agent handles authentication",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",