opencode-polkit 0.1.2 → 0.1.4
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/README.md +43 -10
- package/dist/index.js +126 -69
- package/package.json +3 -3
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,8 +4,14 @@ Redirect `sudo`/`doas` to `pkexec` in [OpenCode](https://opencode.ai).
|
|
|
4
4
|
Triggers the system's native polkit authentication dialog (KDE, GNOME, etc.)
|
|
5
5
|
instead of requiring a terminal for password input.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
Like CachyOS Hello, the plugin does not check for a polkit agent up front:
|
|
8
|
+
`sudo` is always redirected to `pkexec` at execution time, and polkitd routes
|
|
9
|
+
the request to whatever agent is currently registered. Without an agent,
|
|
10
|
+
`pkexec` fails fast with `Error creating textual authentication agent`
|
|
11
|
+
(no TTY); the plugin reports that as a clear denial. A hang (dialog never
|
|
12
|
+
appears) is bounded by the bash tool's own timeout (default 2 min,
|
|
13
|
+
configurable up to 10 min) and left untranslated — it cannot be told
|
|
14
|
+
apart from a long-running command.
|
|
9
15
|
|
|
10
16
|
## Install
|
|
11
17
|
|
|
@@ -21,17 +27,44 @@ Or add to `opencode.json` / `~/.config/opencode/opencode.jsonc`:
|
|
|
21
27
|
}
|
|
22
28
|
```
|
|
23
29
|
|
|
30
|
+
For local development, point opencode at the project directory instead:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"plugin": ["/path/to/opencode-polkit"]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
24
38
|
## Behavior
|
|
25
39
|
|
|
26
|
-
| Command
|
|
27
|
-
|
|
28
|
-
| `sudo xxx`
|
|
29
|
-
| `doas xxx`
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `visudo`
|
|
40
|
+
| Command | result |
|
|
41
|
+
|-----------------------|-------------------------------------------------------|
|
|
42
|
+
| `sudo xxx` | redirects to `pkexec xxx` |
|
|
43
|
+
| `doas xxx` | redirects to `pkexec xxx` |
|
|
44
|
+
| `cat x \| sudo tee y` | redirects to `cat x \| pkexec tee y` (mid-command) |
|
|
45
|
+
| `pkexec xxx` | passes through |
|
|
46
|
+
| `sudoedit` / `visudo` | blocked |
|
|
47
|
+
|
|
48
|
+
### Minimal intervention
|
|
49
|
+
|
|
50
|
+
The command is rewritten **only** by replacing `sudo`/`doas` with
|
|
51
|
+
`pkexec` — its shape is otherwise untouched, so the agent always sees a
|
|
52
|
+
command that behaves like the one it wrote. Privilege keywords are found
|
|
53
|
+
by a lexical scanner (quote/escape/heredoc aware): `sudo` inside string
|
|
54
|
+
literals, comments or heredocs is ignored; `sudo` in any executable
|
|
55
|
+
position (leading, after `&&`/`||`/`|`, in `$(...)` or subshells) is
|
|
56
|
+
rewritten. `sudo a && sudo b` rewrites both.
|
|
57
|
+
|
|
58
|
+
### Failures
|
|
33
59
|
|
|
34
|
-
|
|
60
|
+
Unambiguous authentication failures are reported as clear errors and the
|
|
61
|
+
command is remembered so a retry is rejected without prompting again
|
|
62
|
+
(cleared when opencode restarts): `Not authorized`, `Error executing
|
|
63
|
+
command as another user`, `Error creating textual authentication agent`
|
|
64
|
+
(no polkit agent + no TTY). A hang (dialog never appears) is bounded by
|
|
65
|
+
the bash tool's own timeout (default 2 min, configurable up to 10 min)
|
|
66
|
+
and is left untranslated — it cannot be told apart from a long-running
|
|
67
|
+
command.
|
|
35
68
|
|
|
36
69
|
## i18n
|
|
37
70
|
|
package/dist/index.js
CHANGED
|
@@ -1,77 +1,138 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Lexical scan for privilege-escalation keywords OUTSIDE quotes.
|
|
3
|
+
*
|
|
4
|
+
* Shell commands may contain `sudo` anywhere a command starts (pipes,
|
|
5
|
+
* `&&`/`||`, subshells, command substitution), and string literals /
|
|
6
|
+
* comments may contain the same words spuriously. A plain regex cannot
|
|
7
|
+
* tell those apart; this scanner tracks quote state (' " and backslash
|
|
8
|
+
* escapes) and only reports keywords in executable position.
|
|
9
|
+
*
|
|
10
|
+
* - `node -e "…sudo…"` → no hit (inside quotes)
|
|
11
|
+
* - `cat x | sudo tee y` → hit at index 8 (command boundary)
|
|
12
|
+
* - `cd /tmp && sudo ./x` → hit (after `&&`)
|
|
13
|
+
*/
|
|
14
|
+
function scanPrivileged(command) {
|
|
15
|
+
const hits = [];
|
|
16
|
+
let inSingle = false;
|
|
17
|
+
let inDouble = false;
|
|
18
|
+
let escaped = false;
|
|
19
|
+
// Heredoc delimiters waiting to close (stack; multiple << are legal).
|
|
20
|
+
const heredocs = [];
|
|
21
|
+
for (let i = 0; i < command.length; i++) {
|
|
22
|
+
const ch = command[i];
|
|
23
|
+
// Inside a heredoc body: only a line consisting of the delimiter ends
|
|
24
|
+
// it. Content there is a string literal, never a command.
|
|
25
|
+
if (heredocs.length > 0) {
|
|
26
|
+
const lineEnd = command.indexOf("\n", i);
|
|
27
|
+
const line = lineEnd === -1 ? command.slice(i) : command.slice(i, lineEnd);
|
|
28
|
+
if (heredocs.some((d) => line === d)) {
|
|
29
|
+
heredocs.pop();
|
|
30
|
+
}
|
|
31
|
+
i = lineEnd === -1 ? command.length : lineEnd;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (escaped) {
|
|
35
|
+
escaped = false;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (ch === "\\" && !inSingle) {
|
|
39
|
+
escaped = true;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (ch === "'" && !inDouble) {
|
|
43
|
+
inSingle = !inSingle;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (ch === '"' && !inSingle) {
|
|
47
|
+
inDouble = !inDouble;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (inSingle || inDouble)
|
|
51
|
+
continue;
|
|
52
|
+
// Heredoc start: `<<[-]DELIM` (delim may be quoted or unquoted).
|
|
53
|
+
if (ch === "<" && command[i + 1] === "<") {
|
|
54
|
+
let j = i + 2;
|
|
55
|
+
if (command[j] === "-")
|
|
56
|
+
j++;
|
|
57
|
+
while (j < command.length && /\s/.test(command[j]))
|
|
58
|
+
j++;
|
|
59
|
+
if (j < command.length && (command[j] === "'" || command[j] === '"')) {
|
|
60
|
+
const q = command[j];
|
|
61
|
+
const end = command.indexOf(q, j + 1);
|
|
62
|
+
if (end !== -1) {
|
|
63
|
+
heredocs.push(command.slice(j + 1, end));
|
|
64
|
+
i = end;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const m = /^[^\s|;&<>()]+/.exec(command.slice(j));
|
|
70
|
+
if (m) {
|
|
71
|
+
heredocs.push(m[0]);
|
|
72
|
+
i = j + m[0].length - 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const prev = i > 0 ? command[i - 1] : "";
|
|
78
|
+
if (/[-\/.\w]/.test(prev))
|
|
79
|
+
continue;
|
|
80
|
+
for (const kw of ["sudoedit", "visudo", "sudo", "doas", "pkexec"]) {
|
|
81
|
+
if (command.startsWith(kw, i)) {
|
|
82
|
+
const next = command[i + kw.length] ?? "";
|
|
83
|
+
if (!/[-\/.\w]/.test(next)) {
|
|
84
|
+
hits.push({ index: i, kw });
|
|
85
|
+
i += kw.length - 1;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return hits;
|
|
92
|
+
}
|
|
10
93
|
const translations = {
|
|
11
94
|
en: {
|
|
12
95
|
blocked: "Privilege escalation command blocked by plugin",
|
|
13
|
-
noPolkit: "sudo/doas blocked: no polkit agent detected, cannot escalate safely",
|
|
14
|
-
noPolkitPkexec: "pkexec unavailable: no polkit agent detected",
|
|
15
96
|
polkitDenied: "Polkit authentication denied by user",
|
|
16
97
|
},
|
|
17
98
|
zh: {
|
|
18
99
|
blocked: "权限提升命令已被插件拦截",
|
|
19
|
-
noPolkit: "sudo/doas 已被拦截:未检测到 polkit agent,无法安全提权",
|
|
20
|
-
noPolkitPkexec: "pkexec 不可用:未检测到 polkit agent",
|
|
21
100
|
polkitDenied: "Polkit 认证已被用户拒绝",
|
|
22
101
|
},
|
|
23
102
|
ja: {
|
|
24
103
|
blocked: "特権昇格コマンドはプラグインによりブロックされました",
|
|
25
|
-
noPolkit: "sudo/doas がブロックされました:polkit エージェントが見つからないため、安全に昇格できません",
|
|
26
|
-
noPolkitPkexec: "pkexec は利用できません:polkit エージェントが見つかりません",
|
|
27
104
|
polkitDenied: "Polkit 認証がユーザーによって拒否されました",
|
|
28
105
|
},
|
|
29
106
|
ko: {
|
|
30
107
|
blocked: "권한 상승 명령이 플러그인에 의해 차단되었습니다",
|
|
31
|
-
noPolkit: "sudo/doas 차단됨: polkit 에이전트가 감지되지 않아 안전하게 권한 상승할 수 없습니다",
|
|
32
|
-
noPolkitPkexec: "pkexec 사용 불가: polkit 에이전트가 감지되지 않았습니다",
|
|
33
108
|
polkitDenied: "Polkit 인증이 사용자에 의해 거부되었습니다",
|
|
34
109
|
},
|
|
35
110
|
de: {
|
|
36
111
|
blocked: "Berechtigungseskalation vom Plugin blockiert",
|
|
37
|
-
noPolkit: "sudo/doas blockiert: kein polkit-Agent erkannt, sichere Eskalation nicht möglich",
|
|
38
|
-
noPolkitPkexec: "pkexec nicht verfügbar: kein polkit-Agent erkannt",
|
|
39
112
|
polkitDenied: "Polkit-Authentifizierung vom Benutzer abgelehnt",
|
|
40
113
|
},
|
|
41
114
|
fr: {
|
|
42
115
|
blocked: "Commande d'élévation de privilèges bloquée par le plugin",
|
|
43
|
-
noPolkit: "sudo/doas bloqué : aucun agent polkit détecté, élévation impossible",
|
|
44
|
-
noPolkitPkexec: "pkexec indisponible : aucun agent polkit détecté",
|
|
45
116
|
polkitDenied: "Authentification polkit refusée par l'utilisateur",
|
|
46
117
|
},
|
|
47
118
|
es: {
|
|
48
119
|
blocked: "Comando de elevación de privilegios bloqueado por el plugin",
|
|
49
|
-
noPolkit: "sudo/doas bloqueado: no se detectó agente polkit, no se puede elevar de forma segura",
|
|
50
|
-
noPolkitPkexec: "pkexec no disponible: no se detectó agente polkit",
|
|
51
120
|
polkitDenied: "Autenticación polkit denegada por el usuario",
|
|
52
121
|
},
|
|
53
122
|
pt: {
|
|
54
123
|
blocked: "Comando de elevação de privilégios bloqueado pelo plugin",
|
|
55
|
-
noPolkit: "sudo/doas bloqueado: nenhum agente polkit detectado, não é possível elevar com segurança",
|
|
56
|
-
noPolkitPkexec: "pkexec indisponível: nenhum agente polkit detectado",
|
|
57
124
|
polkitDenied: "Autenticação polkit negada pelo usuário",
|
|
58
125
|
},
|
|
59
126
|
ru: {
|
|
60
127
|
blocked: "Команда повышения привилегий заблокирована плагином",
|
|
61
|
-
noPolkit: "sudo/doas заблокирован: не обнаружен polkit-агент, невозможно безопасно повысить привилегии",
|
|
62
|
-
noPolkitPkexec: "pkexec недоступен: не обнаружен polkit-агент",
|
|
63
128
|
polkitDenied: "Аутентификация polkit отклонена пользователем",
|
|
64
129
|
},
|
|
65
130
|
tr: {
|
|
66
131
|
blocked: "Yetki yükseltme komutu eklenti tarafından engellendi",
|
|
67
|
-
noPolkit: "sudo/doas engellendi: polkit aracısı algılanmadı, güvenli şekilde yetki yükseltilemez",
|
|
68
|
-
noPolkitPkexec: "pkexec kullanılamıyor: polkit aracısı algılanmadı",
|
|
69
132
|
polkitDenied: "Polkit kimlik doğrulaması kullanıcı tarafından reddedildi",
|
|
70
133
|
},
|
|
71
134
|
uk: {
|
|
72
135
|
blocked: "Команду підвищення привілеїв заблоковано плагіном",
|
|
73
|
-
noPolkit: "sudo/doas заблоковано: не виявлено агента polkit, неможливо безпечно підвищити привілеї",
|
|
74
|
-
noPolkitPkexec: "pkexec недоступний: не виявлено агента polkit",
|
|
75
136
|
polkitDenied: "Автентифікацію polkit відхилено користувачем",
|
|
76
137
|
},
|
|
77
138
|
};
|
|
@@ -85,29 +146,6 @@ function detectLocale() {
|
|
|
85
146
|
return "en";
|
|
86
147
|
}
|
|
87
148
|
const MSG = translations[detectLocale()] ?? translations.en;
|
|
88
|
-
function polkitAvailable() {
|
|
89
|
-
try {
|
|
90
|
-
if (process.env.DBUS_SESSION_BUS_ADDRESS) {
|
|
91
|
-
const proc = Bun.spawnSync(["busctl", "--user", "list"], {
|
|
92
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
93
|
-
});
|
|
94
|
-
if (proc.exitCode === 0 && /\bpolkit\b/i.test(new TextDecoder().decode(proc.stdout))) {
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch { /* fall through */ }
|
|
100
|
-
try {
|
|
101
|
-
const proc = Bun.spawnSync(["pgrep", "-x", ...POLKIT_AGENTS], {
|
|
102
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
103
|
-
});
|
|
104
|
-
return proc.exitCode === 0;
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const hasPolkit = polkitAvailable();
|
|
111
149
|
export const PolkitPlugin = async (_input) => {
|
|
112
150
|
const deniedCommands = new Set();
|
|
113
151
|
return {
|
|
@@ -115,37 +153,56 @@ export const PolkitPlugin = async (_input) => {
|
|
|
115
153
|
if (hookInput.tool !== "bash")
|
|
116
154
|
return;
|
|
117
155
|
const command = hookOutput?.args?.command ?? "";
|
|
118
|
-
|
|
156
|
+
const hits = scanPrivileged(command);
|
|
157
|
+
if (hits.length === 0)
|
|
119
158
|
return;
|
|
120
159
|
if (deniedCommands.has(command)) {
|
|
121
160
|
throw new Error(MSG.polkitDenied);
|
|
122
161
|
}
|
|
123
|
-
if (
|
|
124
|
-
if (!hasPolkit)
|
|
125
|
-
throw new Error(MSG.noPolkitPkexec);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (/(?<![-\/.\w])(sudoedit|visudo)(?![-\/.\w])/.test(command)) {
|
|
162
|
+
if (hits.some((h) => h.kw === "sudoedit" || h.kw === "visudo")) {
|
|
129
163
|
throw new Error(MSG.blocked);
|
|
130
164
|
}
|
|
131
|
-
|
|
132
|
-
|
|
165
|
+
// Already pkexec (leading or mid-command): pass through untouched.
|
|
166
|
+
if (hits.every((h) => h.kw === "pkexec"))
|
|
167
|
+
return;
|
|
168
|
+
// Rewrite every in-command sudo/doas to pkexec (back to front so
|
|
169
|
+
// indices stay valid): `cat x | sudo tee y` -> `cat x | pkexec tee y`.
|
|
170
|
+
// No other rewriting: the command keeps its exact shape so the agent
|
|
171
|
+
// sees only the minimal change (sudo -> pkexec).
|
|
172
|
+
let rewritten = command;
|
|
173
|
+
for (const h of [...hits].reverse()) {
|
|
174
|
+
if (h.kw === "sudo" || h.kw === "doas") {
|
|
175
|
+
rewritten =
|
|
176
|
+
rewritten.slice(0, h.index) + "pkexec" + rewritten.slice(h.index + h.kw.length);
|
|
177
|
+
}
|
|
133
178
|
}
|
|
134
|
-
hookOutput.args.command =
|
|
179
|
+
hookOutput.args.command = rewritten;
|
|
135
180
|
},
|
|
136
181
|
"tool.execute.after": async (hookInput, hookOutput) => {
|
|
137
182
|
if (hookInput.tool !== "bash")
|
|
138
183
|
return;
|
|
139
184
|
const command = hookInput.args?.command ?? "";
|
|
140
|
-
|
|
185
|
+
const hits = scanPrivileged(command);
|
|
186
|
+
if (hits.length === 0)
|
|
141
187
|
return;
|
|
142
|
-
|
|
188
|
+
// Reconstruct the user's original command for the deny list: map
|
|
189
|
+
// pkexec back to sudo.
|
|
190
|
+
let original = command;
|
|
191
|
+
for (const h of [...scanPrivileged(original)].reverse()) {
|
|
192
|
+
if (h.kw === "pkexec") {
|
|
193
|
+
original = original.slice(0, h.index) + "sudo" + original.slice(h.index + 6);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Only unambiguous authentication failures are reported and deny
|
|
197
|
+
// listed. A hang (dialog never appears) is bounded by the bash tool's
|
|
198
|
+
// own timeout (default 2 min) and is NOT translated here: it cannot
|
|
199
|
+
// be told apart from a long-running command.
|
|
143
200
|
if (/\bNot authorized\b/.test(hookOutput.output) ||
|
|
144
|
-
/\bError executing command as another user\b/.test(hookOutput.output)
|
|
145
|
-
|
|
201
|
+
/\bError executing command as another user\b/.test(hookOutput.output) ||
|
|
202
|
+
/\bError creating textual authentication agent\b/.test(hookOutput.output)) {
|
|
203
|
+
deniedCommands.add(original);
|
|
146
204
|
throw new Error(MSG.polkitDenied);
|
|
147
205
|
}
|
|
148
206
|
},
|
|
149
207
|
};
|
|
150
208
|
};
|
|
151
|
-
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-polkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Redirect sudo/doas to pkexec in OpenCode — uses system polkit agent for native auth dialogs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"LICENSE"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "
|
|
21
|
-
"
|
|
20
|
+
"build": "deno task build",
|
|
21
|
+
"check": "deno task check"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"opencode": ">=1.0.0"
|
package/dist/index.d.ts
DELETED
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAA;AA2H9D,eAAO,MAAM,YAAY,EAAE,MAgD1B,CAAA"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|