@argszero/cordis-plugin-sandbox-grant-advisor 0.2.0 → 0.3.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/README.md +22 -3
- package/lib/advice.js +48 -1
- package/lib/index.js +12 -4
- package/lib/types/advice.d.ts +13 -1
- package/lib/types/index.d.ts +12 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -30,9 +30,20 @@ stuck.
|
|
|
30
30
|
|
|
31
31
|
### 1. Workspace provisioning — the Windows ACL failure (`acl-provisioning`)
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
[discussion #7646]. In each one every sandboxed command fails
|
|
35
|
-
before it runs, and the error names neither the missing right nor
|
|
33
|
+
Four reports describe this exact line: [discussion #7538], [discussion #7622],
|
|
34
|
+
[discussion #7646], [discussion #7720]. In each one every sandboxed command fails
|
|
35
|
+
the same way, before it runs, and the error names neither the missing right nor
|
|
36
|
+
a remedy.
|
|
37
|
+
|
|
38
|
+
`#7720` is worth reading for where the failure lands: the grant is materialized
|
|
39
|
+
at sandbox **initialization**, so this is not one refused operation but *every*
|
|
40
|
+
shell tool at once — the reporter could not run `netstat` or even `icacls` to
|
|
41
|
+
diagnose the error they were staring at (on `0.1.5-rc.3` the same directory
|
|
42
|
+
worked, because confinement was skipped silently rather than failing closed).
|
|
43
|
+
They also report the two remedies that look right and are not
|
|
44
|
+
(`takeown /F <dir> /R /D Y`, `icacls <dir> /reset /T /C`), which is why the
|
|
45
|
+
advisory names them with the reason each fails instead of leaving the reader to
|
|
46
|
+
discover it.
|
|
36
47
|
|
|
37
48
|
The Windows backend provisions a workspace by writing the directory's DACL and
|
|
38
49
|
its mandatory-integrity label in **one** `SetNamedSecurityInfoW` call
|
|
@@ -79,6 +90,13 @@ Confirm the cause (unelevated) — `icacls` is a normal user command:
|
|
|
79
90
|
Fix it (unelevated, one line) and then run the command again:
|
|
80
91
|
PowerShell: icacls "D:\ws" /grant "$env:USERNAME:(OI)(CI)F"
|
|
81
92
|
cmd: icacls "D:\ws" /grant "%USERNAME%:(OI)(CI)F"
|
|
93
|
+
|
|
94
|
+
What will NOT fix it — both look like the right move, and both were tried and reported:
|
|
95
|
+
takeown /F "D:\ws" /R /D Y
|
|
96
|
+
makes you the owner, but ownership's implicit rights are READ_CONTROL and WRITE_DAC only.
|
|
97
|
+
The owner does not implicitly hold WRITE_OWNER, which is the right this call needs.
|
|
98
|
+
icacls "D:\ws" /reset /T /C
|
|
99
|
+
restores inheritance, and inheritance is what supplied the Modify-only ACE above.
|
|
82
100
|
```
|
|
83
101
|
|
|
84
102
|
### 2. Persistent shell startup (`pty-startup`)
|
|
@@ -338,4 +356,5 @@ the current runtime cannot distinguish rather than counting it as a pass.
|
|
|
338
356
|
[discussion #7538]: https://github.com/deepseek-ai/deepseek-harness/discussions/7538
|
|
339
357
|
[discussion #7622]: https://github.com/deepseek-ai/deepseek-harness/discussions/7622
|
|
340
358
|
[discussion #7646]: https://github.com/deepseek-ai/deepseek-harness/discussions/7646
|
|
359
|
+
[discussion #7720]: https://github.com/deepseek-ai/deepseek-harness/discussions/7720
|
|
341
360
|
[discussion #7638]: https://github.com/deepseek-ai/deepseek-harness/discussions/7638
|
package/lib/advice.js
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { failureLine } from './signature.js';
|
|
35
35
|
/** The upstream threads the ACL advisory is a stopgap for. */
|
|
36
|
-
export const ACL_DISCUSSIONS = '#7538 / #7622 / #7646';
|
|
36
|
+
export const ACL_DISCUSSIONS = '#7538 / #7622 / #7646 / #7720';
|
|
37
37
|
/** The upstream thread the persistent-shell advisory is a stopgap for. */
|
|
38
38
|
export const PTY_DISCUSSIONS = '#7638';
|
|
39
39
|
/** The documented prerequisite, quoted from the backend's README. */
|
|
@@ -57,6 +57,21 @@ export const GLOBAL_PATCH = '$DSH_HOME/cordis.patch.yml';
|
|
|
57
57
|
export const MINIMAL_PRESET_ROW = 'preset-minimal';
|
|
58
58
|
/** The one-shot shell tool the `standard` preset mounts on Windows. */
|
|
59
59
|
export const ONE_SHOT_SHELL = '@deepseek-ai/dsh-tool-pwsh';
|
|
60
|
+
/**
|
|
61
|
+
* The two remedies that look like the fix and are not.
|
|
62
|
+
*
|
|
63
|
+
* Both were applied by the reporter of `#7720` before finding the one that
|
|
64
|
+
* works, and both are the *natural* reach: making yourself the owner and
|
|
65
|
+
* resetting the directory's ACL are how one normally repairs a Windows
|
|
66
|
+
* permission problem. They fail here for two different reasons, and naming the
|
|
67
|
+
* reason is what makes this section worth its lines — a reader who already
|
|
68
|
+
* tried them learns why, and a reader who has not is spared the attempt. See
|
|
69
|
+
* {@link nonFixes} for when this is emitted.
|
|
70
|
+
*/
|
|
71
|
+
export const NOT_FIXES = [
|
|
72
|
+
'takeown /F "<dir>" /R /D Y',
|
|
73
|
+
'icacls "<dir>" /reset /T /C',
|
|
74
|
+
];
|
|
60
75
|
/** Placeholder the user replaces with the directory the error named. */
|
|
61
76
|
const PLACEHOLDER = '<the directory from the error line above>';
|
|
62
77
|
/**
|
|
@@ -91,6 +106,37 @@ function diagnosis(failure) {
|
|
|
91
106
|
].join('\n');
|
|
92
107
|
}
|
|
93
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The "this is not the fix" lines for one class of failure.
|
|
111
|
+
*
|
|
112
|
+
* Emitted only for `apply-denied`, the class whose whole diagnosis is the
|
|
113
|
+
* missing `WRITE_OWNER` right — because only there is the claim true:
|
|
114
|
+
*
|
|
115
|
+
* - `read-denied` wants `READ_CONTROL`, and taking ownership *does* carry it,
|
|
116
|
+
* so calling `takeown` a non-fix there would be false.
|
|
117
|
+
* - `apply-other` already says the missing-rights story does not apply
|
|
118
|
+
* verbatim, so a section that presupposes it would contradict its own
|
|
119
|
+
* diagnosis.
|
|
120
|
+
*
|
|
121
|
+
* A negative claim still has to be earned: the failure to avoid is advice that
|
|
122
|
+
* is confidently wrong in the other direction.
|
|
123
|
+
* @param failure - the recognized provisioning failure.
|
|
124
|
+
* @param path - the directory the error named, or the placeholder.
|
|
125
|
+
* @returns the section's lines, or an empty array for a class it does not fit.
|
|
126
|
+
*/
|
|
127
|
+
function nonFixes(failure, path) {
|
|
128
|
+
if (failure.klass !== 'apply-denied')
|
|
129
|
+
return [];
|
|
130
|
+
return [
|
|
131
|
+
'What will NOT fix it — both look like the right move, and both were tried and reported:',
|
|
132
|
+
` takeown /F "${path}" /R /D Y`,
|
|
133
|
+
" makes you the owner, but ownership's implicit rights are READ_CONTROL and WRITE_DAC only.",
|
|
134
|
+
' The owner does not implicitly hold WRITE_OWNER, which is the right this call needs.',
|
|
135
|
+
` icacls "${path}" /reset /T /C`,
|
|
136
|
+
' restores inheritance, and inheritance is what supplied the Modify-only ACE above.',
|
|
137
|
+
'',
|
|
138
|
+
];
|
|
139
|
+
}
|
|
94
140
|
/**
|
|
95
141
|
* Build the advisory attached to the failing tool result.
|
|
96
142
|
*
|
|
@@ -137,6 +183,7 @@ function aclAdvisory(failure, href) {
|
|
|
137
183
|
` PowerShell: icacls "${path}" /grant "$env:USERNAME:(OI)(CI)F"`,
|
|
138
184
|
` cmd: icacls "${path}" /grant "%USERNAME%:(OI)(CI)F"`,
|
|
139
185
|
'',
|
|
186
|
+
...nonFixes(failure, path),
|
|
140
187
|
'How to read this: the harness documents the prerequisite (' + PREREQUISITE + ') and this',
|
|
141
188
|
'error does not name it yet, so the advice is delivered here instead. This is a stopgap, ' + where + '.',
|
|
142
189
|
'What it is NOT: this plugin neither edits ACLs nor elevates — the command above is yours to run.',
|
package/lib/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ## The two failures it recognizes
|
|
6
6
|
*
|
|
7
|
-
* **Workspace provisioning (Windows ACL).**
|
|
8
|
-
* (`#7538`, `#7622`, `#7646`) describe the same shape: the host-side write grant
|
|
7
|
+
* **Workspace provisioning (Windows ACL).** Four reports of one signature
|
|
8
|
+
* (`#7538`, `#7622`, `#7646`, `#7720`) describe the same shape: the host-side write grant
|
|
9
9
|
* for a sandboxed workspace cannot be applied, every sandboxed command then
|
|
10
10
|
* fails identically **before it runs**, and the error text is a bare Win32 line:
|
|
11
11
|
*
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
* caller `WRITE_OWNER` — never reaches the user, so sessions escape into
|
|
19
19
|
* `danger-full-access` or die on the model's output cap.
|
|
20
20
|
*
|
|
21
|
+
* `#7720` sharpens where this lands: because the grant is materialized at
|
|
22
|
+
* sandbox *initialization*, that failure takes **every** shell tool with it, not
|
|
23
|
+
* one operation — the reporter could not run `netstat` or `icacls` to diagnose
|
|
24
|
+
* the failure they were looking at. It also contributes the two remedies that
|
|
25
|
+
* look right and are not (`takeown /R /D Y`, `icacls /reset /T /C`), which the
|
|
26
|
+
* ACL advisory now names along with the reason each fails.
|
|
27
|
+
*
|
|
21
28
|
* **Persistent shell startup (#7638).** With the `minimal` preset on Windows the
|
|
22
29
|
* only shell tool is a persistent PTY (`dsh-terminal-bash` +
|
|
23
30
|
* `dsh-tool-pwsh-persistent`), and under a *confining* sandbox mode every call
|
|
@@ -59,8 +66,9 @@
|
|
|
59
66
|
* failure of a family, the failing tool result is enriched with a user-role
|
|
60
67
|
* notice. For the ACL family it names the missing right (`WRITE_OWNER` on the
|
|
61
68
|
* directory, not `SeSecurityPrivilege`), gives the unelevated one-line
|
|
62
|
-
* `icacls` remedy,
|
|
63
|
-
* directory from a wrong prerequisite
|
|
69
|
+
* `icacls` remedy, gives the discriminator that separates a Modify-only
|
|
70
|
+
* directory from a wrong prerequisite, and names the two remedies that look
|
|
71
|
+
* right and are not (`takeown`, `icacls /reset`), each with its reason. For the PTY family it names the
|
|
64
72
|
* combination that fails (persistent PTY × a confining mode), states the
|
|
65
73
|
* resolved mode, says plainly that no command can fix it, and hands the
|
|
66
74
|
* user-side preset choice over. Both ride `additionalContexts`, so the model
|
package/lib/types/advice.d.ts
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
import type { ProvisioningFailure, RecognizedFailure } from './signature.js';
|
|
35
35
|
import type { SandboxModeName } from './mode.js';
|
|
36
36
|
/** The upstream threads the ACL advisory is a stopgap for. */
|
|
37
|
-
export declare const ACL_DISCUSSIONS = "#7538 / #7622 / #7646";
|
|
37
|
+
export declare const ACL_DISCUSSIONS = "#7538 / #7622 / #7646 / #7720";
|
|
38
38
|
/** The upstream thread the persistent-shell advisory is a stopgap for. */
|
|
39
39
|
export declare const PTY_DISCUSSIONS = "#7638";
|
|
40
40
|
/** The documented prerequisite, quoted from the backend's README. */
|
|
@@ -58,6 +58,18 @@ export declare const GLOBAL_PATCH = "$DSH_HOME/cordis.patch.yml";
|
|
|
58
58
|
export declare const MINIMAL_PRESET_ROW = "preset-minimal";
|
|
59
59
|
/** The one-shot shell tool the `standard` preset mounts on Windows. */
|
|
60
60
|
export declare const ONE_SHOT_SHELL = "@deepseek-ai/dsh-tool-pwsh";
|
|
61
|
+
/**
|
|
62
|
+
* The two remedies that look like the fix and are not.
|
|
63
|
+
*
|
|
64
|
+
* Both were applied by the reporter of `#7720` before finding the one that
|
|
65
|
+
* works, and both are the *natural* reach: making yourself the owner and
|
|
66
|
+
* resetting the directory's ACL are how one normally repairs a Windows
|
|
67
|
+
* permission problem. They fail here for two different reasons, and naming the
|
|
68
|
+
* reason is what makes this section worth its lines — a reader who already
|
|
69
|
+
* tried them learns why, and a reader who has not is spared the attempt. See
|
|
70
|
+
* {@link nonFixes} for when this is emitted.
|
|
71
|
+
*/
|
|
72
|
+
export declare const NOT_FIXES: readonly ["takeown /F \"<dir>\" /R /D Y", "icacls \"<dir>\" /reset /T /C"];
|
|
61
73
|
/** What the caller knows about the failing call, beyond the failure text. */
|
|
62
74
|
export interface AdvisoryContext {
|
|
63
75
|
/** URL quoted in place of the discussions list; optional. */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ## The two failures it recognizes
|
|
6
6
|
*
|
|
7
|
-
* **Workspace provisioning (Windows ACL).**
|
|
8
|
-
* (`#7538`, `#7622`, `#7646`) describe the same shape: the host-side write grant
|
|
7
|
+
* **Workspace provisioning (Windows ACL).** Four reports of one signature
|
|
8
|
+
* (`#7538`, `#7622`, `#7646`, `#7720`) describe the same shape: the host-side write grant
|
|
9
9
|
* for a sandboxed workspace cannot be applied, every sandboxed command then
|
|
10
10
|
* fails identically **before it runs**, and the error text is a bare Win32 line:
|
|
11
11
|
*
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
* caller `WRITE_OWNER` — never reaches the user, so sessions escape into
|
|
19
19
|
* `danger-full-access` or die on the model's output cap.
|
|
20
20
|
*
|
|
21
|
+
* `#7720` sharpens where this lands: because the grant is materialized at
|
|
22
|
+
* sandbox *initialization*, that failure takes **every** shell tool with it, not
|
|
23
|
+
* one operation — the reporter could not run `netstat` or `icacls` to diagnose
|
|
24
|
+
* the failure they were looking at. It also contributes the two remedies that
|
|
25
|
+
* look right and are not (`takeown /R /D Y`, `icacls /reset /T /C`), which the
|
|
26
|
+
* ACL advisory now names along with the reason each fails.
|
|
27
|
+
*
|
|
21
28
|
* **Persistent shell startup (#7638).** With the `minimal` preset on Windows the
|
|
22
29
|
* only shell tool is a persistent PTY (`dsh-terminal-bash` +
|
|
23
30
|
* `dsh-tool-pwsh-persistent`), and under a *confining* sandbox mode every call
|
|
@@ -59,8 +66,9 @@
|
|
|
59
66
|
* failure of a family, the failing tool result is enriched with a user-role
|
|
60
67
|
* notice. For the ACL family it names the missing right (`WRITE_OWNER` on the
|
|
61
68
|
* directory, not `SeSecurityPrivilege`), gives the unelevated one-line
|
|
62
|
-
* `icacls` remedy,
|
|
63
|
-
* directory from a wrong prerequisite
|
|
69
|
+
* `icacls` remedy, gives the discriminator that separates a Modify-only
|
|
70
|
+
* directory from a wrong prerequisite, and names the two remedies that look
|
|
71
|
+
* right and are not (`takeown`, `icacls /reset`), each with its reason. For the PTY family it names the
|
|
64
72
|
* combination that fails (persistent PTY × a confining mode), states the
|
|
65
73
|
* resolved mode, says plainly that no command can fix it, and hands the
|
|
66
74
|
* user-side preset choice over. Both ride `additionalContexts`, so the model
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@argszero/cordis-plugin-sandbox-grant-advisor",
|
|
3
|
-
"description": "Turns two sandbox environment failures that name neither their cause nor a remedy into a diagnosis with a path forward. Family 1, the Windows workspace ACL:
|
|
4
|
-
"version": "0.
|
|
3
|
+
"description": "Turns two sandbox environment failures that name neither their cause nor a remedy into a diagnosis with a path forward. Family 1, the Windows workspace ACL: four reports (#7538, #7622, #7646, #7720) of one signature — every sandboxed command fails before it runs with `SetNamedSecurityInfoW failed (Win32 5): grantWrite(<workspace>)`, because the merged DACL + mandatory-label write needs WRITE_OWNER on the directory (an object right the caller can self-grant), not SeSecurityPrivilege and not elevation; the host grant is materialized lazily and caches nothing on the failure path, so the same failure repeats per command. Family 2, the persistent shell (#7638): with the `minimal` preset under a confining sandbox mode every shell call dies instantly with `PTY shell exited during startup` because the terminal backend cannot create the pseudo-console inside the sandbox, retrying never helps, and `minimal` mounts no fallback shell tool. The plugin observes the public `tools/post-execute` waterfall, classifies both signatures narrowly (only the two `...NamedSecurityInfoW` operations; the PTY message matched on a whole line, never as a substring, and only under a mode the policy resolver reports as confining), and attaches ONE durable user-role advisory per agent per family through `additionalContexts`. The ACL advisory names the missing right, the discriminator, the unelevated `icacls` fix, and the two remedies that look right and are not (`takeown`, `icacls /reset`), each with the reason it fails; the PTY advisory names the failing combination, states the resolved mode, tells the model to stop rather than retry, and hands the user-side preset choice over — it never names a shell tool the failing composition does not mount. An optional, off-by-default `enforceAfter` refuses an identical ACL call this plugin has watched fail, bounded by `maxDenials`; the blocking half is ACL-only by design. It never edits an ACL, never elevates, and never changes a preset or a mode, and it complements repeat-guard-escalation, which keys on call identity rather than on the environment signature.",
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|