dsh-permission-rules 0.7.0 → 0.7.2
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 +17 -0
- package/{README.es.md → README-es.md} +2 -2
- package/{README.hi.md → README-hi.md} +2 -2
- package/{README.pt.md → README-pt.md} +2 -2
- package/{README.zh.md → README-zh.md} +2 -2
- package/README.md +2 -2
- package/lib/client.js +19 -6668
- package/lib/client.js.map +1 -1
- package/lib/index.js +11 -4
- package/lib/types/allow-host-notice.d.ts +56 -0
- package/lib/types/allow-host-notice.d.ts.map +1 -0
- package/lib/types/allow-host.d.ts +13 -39
- package/lib/types/allow-host.d.ts.map +1 -1
- package/package.json +24 -23
- package/src/allow-host-notice.ts +76 -0
- package/src/allow-host.ts +16 -59
- package/src/client/section.ts +1 -1
package/lib/index.js
CHANGED
|
@@ -1826,11 +1826,18 @@ function assertBoolean(key, value) {
|
|
|
1826
1826
|
//#endregion
|
|
1827
1827
|
//#region src/allow-host.ts
|
|
1828
1828
|
/**
|
|
1829
|
-
* The settings-page "allow this host" action,
|
|
1829
|
+
* The settings-page "allow this host" action, host-only half: normalizing
|
|
1830
1830
|
* and validating a blocked host, inserting ONE minimal
|
|
1831
|
-
* `match.network.domains` allow rule at the HEAD of a rule document,
|
|
1832
|
-
* which rule file the rule belongs in
|
|
1833
|
-
*
|
|
1831
|
+
* `match.network.domains` allow rule at the HEAD of a rule document, and
|
|
1832
|
+
* choosing which rule file the rule belongs in.
|
|
1833
|
+
*
|
|
1834
|
+
* The workspace choices (`allowHostWorkspaces`) and the notice mapping
|
|
1835
|
+
* (`allowHostNotice`) live in `./allow-host-notice.ts`: the settings page
|
|
1836
|
+
* imports them from the BROWSER bundle, whose bundler inlines every value
|
|
1837
|
+
* import — keeping them next to the `node:path`/`node:net`/`yaml` code here
|
|
1838
|
+
* emitted `require("process")`/`require("buffer")` calls the shell's frozen
|
|
1839
|
+
* module table cannot answer, failing the whole plugin load. They are
|
|
1840
|
+
* re-exported below so existing host-side imports keep working.
|
|
1834
1841
|
*
|
|
1835
1842
|
* Three invariants hold here and are asserted by `test/allow-host.spec.ts`:
|
|
1836
1843
|
*
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client-safe half of the settings-page "allow this host" action: the
|
|
3
|
+
* workspace choices offered for a host-level block ({@link allowHostWorkspaces})
|
|
4
|
+
* and the notice mapping ({@link allowHostNotice}).
|
|
5
|
+
*
|
|
6
|
+
* These live in their own module — separate from the host-only
|
|
7
|
+
* `./allow-host.ts` — because the settings page (the browser bundle built
|
|
8
|
+
* from `src/client/index.ts`) imports them, and the client bundler inlines
|
|
9
|
+
* every value import it meets. A single `import { allowHostNotice } from
|
|
10
|
+
* '../allow-host.ts'` used to drag the whole host module — `node:path`,
|
|
11
|
+
* `node:net`, `yaml`, `process.platform` — into `lib/client.js` as external
|
|
12
|
+
* `require("process")`/`require("buffer")` calls the shell's frozen module
|
|
13
|
+
* table cannot answer, which made the entire plugin fail to load.
|
|
14
|
+
*
|
|
15
|
+
* The hard rule for this module: NO runtime imports beyond pure type-only
|
|
16
|
+
* references to `./wire.ts`. Anything that needs a node builtin, the yaml
|
|
17
|
+
* parser, or the rule compiler belongs in `./allow-host.ts` (or deeper),
|
|
18
|
+
* never here.
|
|
19
|
+
* @module dsh-permission-rules/allow-host-notice
|
|
20
|
+
*/
|
|
21
|
+
import type { AllowHostResult, RuleSourceView } from './wire.js';
|
|
22
|
+
/**
|
|
23
|
+
* The workspaces a host-level block may be allowed in: the distinct non-null
|
|
24
|
+
* `cwd`s of the loaded rule sources.
|
|
25
|
+
*
|
|
26
|
+
* A block with no cwd was judged by the proxy's session-less host chain, which
|
|
27
|
+
* any loaded workspace chain outranks the moment one exists — so writing the
|
|
28
|
+
* host file can leave the connection blocked while the rule sits in a file
|
|
29
|
+
* nothing consults. The settings page therefore offers these workspaces and
|
|
30
|
+
* sends the chosen one as the request's cwd; when there is exactly one (or
|
|
31
|
+
* none), there is nothing to choose between.
|
|
32
|
+
* @param sources - the snapshot's rule sources.
|
|
33
|
+
* @returns the distinct workspace roots, in source order.
|
|
34
|
+
*/
|
|
35
|
+
export declare function allowHostWorkspaces(sources: readonly RuleSourceView[]): string[];
|
|
36
|
+
/** The locale keys the allow action reports through. */
|
|
37
|
+
export type AllowHostNoticeKey = 'allowHostSaved' | 'allowHostAlready' | 'allowHostStillBlocked' | 'allowHostFailed';
|
|
38
|
+
/** The notice one action result maps onto: a locale key plus its interpolation values. */
|
|
39
|
+
export interface AllowHostNotice {
|
|
40
|
+
readonly ok: boolean;
|
|
41
|
+
readonly key: AllowHostNoticeKey;
|
|
42
|
+
readonly vars: Record<string, string | number>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Map an action result onto the page notice.
|
|
46
|
+
*
|
|
47
|
+
* A non-`allow` outcome outranks `alreadyAllowed`: the whole point of
|
|
48
|
+
* returning the RECOMPUTED decision is that the page can never say "allowed"
|
|
49
|
+
* while the connection is still blocked (a nearer chain with an earlier deny,
|
|
50
|
+
* or a mode default, still wins). `alreadyAllowed` therefore only ever reads
|
|
51
|
+
* as success when the decision really is `allow`.
|
|
52
|
+
* @param result - the `permissionRules/allowHost` result.
|
|
53
|
+
* @returns the notice to render.
|
|
54
|
+
*/
|
|
55
|
+
export declare function allowHostNotice(result: AllowHostResult): AllowHostNotice;
|
|
56
|
+
//# sourceMappingURL=allow-host-notice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allow-host-notice.d.ts","sourceRoot":"","sources":["../../src/allow-host-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,EAAE,CAShF;AAED,wDAAwD;AACxD,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,uBAAuB,GAAG,iBAAiB,CAAA;AAEpH,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAA;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;CAC/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAOxE"}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The settings-page "allow this host" action,
|
|
2
|
+
* The settings-page "allow this host" action, host-only half: normalizing
|
|
3
3
|
* and validating a blocked host, inserting ONE minimal
|
|
4
|
-
* `match.network.domains` allow rule at the HEAD of a rule document,
|
|
5
|
-
* which rule file the rule belongs in
|
|
6
|
-
*
|
|
4
|
+
* `match.network.domains` allow rule at the HEAD of a rule document, and
|
|
5
|
+
* choosing which rule file the rule belongs in.
|
|
6
|
+
*
|
|
7
|
+
* The workspace choices (`allowHostWorkspaces`) and the notice mapping
|
|
8
|
+
* (`allowHostNotice`) live in `./allow-host-notice.ts`: the settings page
|
|
9
|
+
* imports them from the BROWSER bundle, whose bundler inlines every value
|
|
10
|
+
* import — keeping them next to the `node:path`/`node:net`/`yaml` code here
|
|
11
|
+
* emitted `require("process")`/`require("buffer")` calls the shell's frozen
|
|
12
|
+
* module table cannot answer, failing the whole plugin load. They are
|
|
13
|
+
* re-exported below so existing host-side imports keep working.
|
|
7
14
|
*
|
|
8
15
|
* Three invariants hold here and are asserted by `test/allow-host.spec.ts`:
|
|
9
16
|
*
|
|
@@ -21,7 +28,8 @@
|
|
|
21
28
|
* what makes the target choice testable without touching a real host.
|
|
22
29
|
* @module dsh-permission-rules/allow-host
|
|
23
30
|
*/
|
|
24
|
-
|
|
31
|
+
export { allowHostNotice, allowHostWorkspaces } from './allow-host-notice.js';
|
|
32
|
+
export type { AllowHostNotice, AllowHostNoticeKey } from './allow-host-notice.js';
|
|
25
33
|
/**
|
|
26
34
|
* Canonical spelling of a host for the allow action, matching target parsing
|
|
27
35
|
* exactly (see `normalizeHost`): lowercase, IPv6 brackets stripped, one
|
|
@@ -114,38 +122,4 @@ export interface AllowHostTargetInput {
|
|
|
114
122
|
* a path outside the writable set.
|
|
115
123
|
*/
|
|
116
124
|
export declare function resolveAllowHostTarget(input: AllowHostTargetInput): string;
|
|
117
|
-
/**
|
|
118
|
-
* The workspaces a host-level block may be allowed in: the distinct non-null
|
|
119
|
-
* `cwd`s of the loaded rule sources.
|
|
120
|
-
*
|
|
121
|
-
* A block with no cwd was judged by the proxy's session-less host chain, which
|
|
122
|
-
* any loaded workspace chain outranks the moment one exists — so writing the
|
|
123
|
-
* host file can leave the connection blocked while the rule sits in a file
|
|
124
|
-
* nothing consults. The settings page therefore offers these workspaces and
|
|
125
|
-
* sends the chosen one as the request's cwd; when there is exactly one (or
|
|
126
|
-
* none), there is nothing to choose between.
|
|
127
|
-
* @param sources - the snapshot's rule sources.
|
|
128
|
-
* @returns the distinct workspace roots, in source order.
|
|
129
|
-
*/
|
|
130
|
-
export declare function allowHostWorkspaces(sources: readonly RuleSourceView[]): string[];
|
|
131
|
-
/** The locale keys the allow action reports through. */
|
|
132
|
-
export type AllowHostNoticeKey = 'allowHostSaved' | 'allowHostAlready' | 'allowHostStillBlocked' | 'allowHostFailed';
|
|
133
|
-
/** The notice one action result maps onto: a locale key plus its interpolation values. */
|
|
134
|
-
export interface AllowHostNotice {
|
|
135
|
-
readonly ok: boolean;
|
|
136
|
-
readonly key: AllowHostNoticeKey;
|
|
137
|
-
readonly vars: Record<string, string | number>;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Map an action result onto the page notice.
|
|
141
|
-
*
|
|
142
|
-
* A non-`allow` outcome outranks `alreadyAllowed`: the whole point of
|
|
143
|
-
* returning the RECOMPUTED decision is that the page can never say "allowed"
|
|
144
|
-
* while the connection is still blocked (a nearer chain with an earlier deny,
|
|
145
|
-
* or a mode default, still wins). `alreadyAllowed` therefore only ever reads
|
|
146
|
-
* as success when the decision really is `allow`.
|
|
147
|
-
* @param result - the `permissionRules/allowHost` result.
|
|
148
|
-
* @returns the notice to render.
|
|
149
|
-
*/
|
|
150
|
-
export declare function allowHostNotice(result: AllowHostResult): AllowHostNotice;
|
|
151
125
|
//# sourceMappingURL=allow-host.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"allow-host.d.ts","sourceRoot":"","sources":["../../src/allow-host.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"allow-host.d.ts","sourceRoot":"","sources":["../../src/allow-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAUH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC7E,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAqBjF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIpD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,CA0B9E;AAiCD,oGAAoG;AACpG,MAAM,WAAW,oBAAoB;IACnC,sFAAsF;IACtF,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,+FAA+F;IAC/F,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,2FAA2F;IAC3F,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,sFAAsF;IACtF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IACzC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAS1E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-permission-rules",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Declarative Claude Code-style permission rules plus a Codex-style process-level network policy for DeepSeek Harness: ordered allow/deny/ask rules with tool-name, argument (glob/regex), workspace-path, and network-target (domain/ip/port/scheme) matching on the tools/pre-execute waterfall, a built-in local HTTP/CONNECT proxy governing shell subprocess traffic, three network modes mapped onto the official sandbox presets, full session-log audit, HMR rule reload, and a settings page with a rule editor, block counters, and recent interception lists.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"docs",
|
|
30
30
|
"scripts/repair-session-logs.mjs",
|
|
31
31
|
"README.md",
|
|
32
|
-
"README
|
|
33
|
-
"README
|
|
34
|
-
"README
|
|
35
|
-
"README
|
|
32
|
+
"README-zh.md",
|
|
33
|
+
"README-es.md",
|
|
34
|
+
"README-pt.md",
|
|
35
|
+
"README-hi.md",
|
|
36
36
|
"CHANGELOG.md",
|
|
37
37
|
"LICENSE"
|
|
38
38
|
],
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"network:outbound"
|
|
77
77
|
],
|
|
78
78
|
"compatibility": {
|
|
79
|
-
"dshVersions": ["0.1.2-rc.1","0.1.5-rc.
|
|
79
|
+
"dshVersions": ["0.1.2-rc.1","0.1.5-rc.2"]
|
|
80
80
|
},
|
|
81
81
|
"capability": {
|
|
82
82
|
"id": "rules",
|
|
@@ -154,26 +154,26 @@
|
|
|
154
154
|
"zod": "^4.4.3"
|
|
155
155
|
},
|
|
156
156
|
"devDependencies": {
|
|
157
|
-
"@deepseek-ai/dsh-attachment": "0.1.5-rc.
|
|
157
|
+
"@deepseek-ai/dsh-attachment": "0.1.5-rc.2",
|
|
158
158
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
159
159
|
"@deepseek-ai/cordis-plugin-include": "^1.0.7",
|
|
160
160
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.3",
|
|
161
|
-
"@deepseek-ai/dsh-agent": "0.1.5-rc.
|
|
162
|
-
"@deepseek-ai/dsh-brand": "0.1.5-rc.
|
|
163
|
-
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.
|
|
164
|
-
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.
|
|
165
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.
|
|
166
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.
|
|
167
|
-
"@deepseek-ai/dsh-client-web": "0.1.5-rc.
|
|
168
|
-
"@deepseek-ai/dsh-commands": "0.1.5-rc.
|
|
169
|
-
"@deepseek-ai/dsh-invariants": "0.1.5-rc.
|
|
170
|
-
"@deepseek-ai/dsh-llm": "0.1.5-rc.
|
|
171
|
-
"@deepseek-ai/dsh-session": "0.1.5-rc.
|
|
172
|
-
"@deepseek-ai/dsh-session-projection": "0.1.5-rc.
|
|
173
|
-
"@deepseek-ai/dsh-subagent": "0.1.5-rc.
|
|
174
|
-
"@deepseek-ai/dsh-tools": "0.1.5-rc.
|
|
175
|
-
"@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.
|
|
176
|
-
"@deepseek-ai/dsh-user-approval": "0.1.5-rc.
|
|
161
|
+
"@deepseek-ai/dsh-agent": "0.1.5-rc.2",
|
|
162
|
+
"@deepseek-ai/dsh-brand": "0.1.5-rc.2",
|
|
163
|
+
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.2",
|
|
164
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2",
|
|
165
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2",
|
|
166
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
|
|
167
|
+
"@deepseek-ai/dsh-client-web": "0.1.5-rc.2",
|
|
168
|
+
"@deepseek-ai/dsh-commands": "0.1.5-rc.2",
|
|
169
|
+
"@deepseek-ai/dsh-invariants": "0.1.5-rc.2",
|
|
170
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
171
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
172
|
+
"@deepseek-ai/dsh-session-projection": "0.1.5-rc.2",
|
|
173
|
+
"@deepseek-ai/dsh-subagent": "0.1.5-rc.2",
|
|
174
|
+
"@deepseek-ai/dsh-tools": "0.1.5-rc.2",
|
|
175
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.2",
|
|
176
|
+
"@deepseek-ai/dsh-user-approval": "0.1.5-rc.2",
|
|
177
177
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
178
178
|
"@eslint/js": "^10.0.1",
|
|
179
179
|
"@types/node": "^22.19.0",
|
|
@@ -194,6 +194,7 @@
|
|
|
194
194
|
"test:coverage": "vitest run --coverage",
|
|
195
195
|
"check:readmes": "node scripts/check-readme-sync.mjs",
|
|
196
196
|
"verify:self-contained": "node scripts/verify-self-contained.mjs",
|
|
197
|
+
"verify:client-bundle": "node scripts/verify-client-bundle.mjs",
|
|
197
198
|
"pack:check": "node scripts/prepare.mjs && pnpm pack",
|
|
198
199
|
"prepare": "node scripts/prepare.mjs"
|
|
199
200
|
},
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client-safe half of the settings-page "allow this host" action: the
|
|
3
|
+
* workspace choices offered for a host-level block ({@link allowHostWorkspaces})
|
|
4
|
+
* and the notice mapping ({@link allowHostNotice}).
|
|
5
|
+
*
|
|
6
|
+
* These live in their own module — separate from the host-only
|
|
7
|
+
* `./allow-host.ts` — because the settings page (the browser bundle built
|
|
8
|
+
* from `src/client/index.ts`) imports them, and the client bundler inlines
|
|
9
|
+
* every value import it meets. A single `import { allowHostNotice } from
|
|
10
|
+
* '../allow-host.ts'` used to drag the whole host module — `node:path`,
|
|
11
|
+
* `node:net`, `yaml`, `process.platform` — into `lib/client.js` as external
|
|
12
|
+
* `require("process")`/`require("buffer")` calls the shell's frozen module
|
|
13
|
+
* table cannot answer, which made the entire plugin fail to load.
|
|
14
|
+
*
|
|
15
|
+
* The hard rule for this module: NO runtime imports beyond pure type-only
|
|
16
|
+
* references to `./wire.ts`. Anything that needs a node builtin, the yaml
|
|
17
|
+
* parser, or the rule compiler belongs in `./allow-host.ts` (or deeper),
|
|
18
|
+
* never here.
|
|
19
|
+
* @module dsh-permission-rules/allow-host-notice
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { AllowHostResult, RuleSourceView } from './wire.ts'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The workspaces a host-level block may be allowed in: the distinct non-null
|
|
26
|
+
* `cwd`s of the loaded rule sources.
|
|
27
|
+
*
|
|
28
|
+
* A block with no cwd was judged by the proxy's session-less host chain, which
|
|
29
|
+
* any loaded workspace chain outranks the moment one exists — so writing the
|
|
30
|
+
* host file can leave the connection blocked while the rule sits in a file
|
|
31
|
+
* nothing consults. The settings page therefore offers these workspaces and
|
|
32
|
+
* sends the chosen one as the request's cwd; when there is exactly one (or
|
|
33
|
+
* none), there is nothing to choose between.
|
|
34
|
+
* @param sources - the snapshot's rule sources.
|
|
35
|
+
* @returns the distinct workspace roots, in source order.
|
|
36
|
+
*/
|
|
37
|
+
export function allowHostWorkspaces(sources: readonly RuleSourceView[]): string[] {
|
|
38
|
+
const seen = new Set<string>()
|
|
39
|
+
const workspaces: string[] = []
|
|
40
|
+
for (const source of sources) {
|
|
41
|
+
if (source.cwd === null || seen.has(source.cwd)) continue
|
|
42
|
+
seen.add(source.cwd)
|
|
43
|
+
workspaces.push(source.cwd)
|
|
44
|
+
}
|
|
45
|
+
return workspaces
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The locale keys the allow action reports through. */
|
|
49
|
+
export type AllowHostNoticeKey = 'allowHostSaved' | 'allowHostAlready' | 'allowHostStillBlocked' | 'allowHostFailed'
|
|
50
|
+
|
|
51
|
+
/** The notice one action result maps onto: a locale key plus its interpolation values. */
|
|
52
|
+
export interface AllowHostNotice {
|
|
53
|
+
readonly ok: boolean
|
|
54
|
+
readonly key: AllowHostNoticeKey
|
|
55
|
+
readonly vars: Record<string, string | number>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Map an action result onto the page notice.
|
|
60
|
+
*
|
|
61
|
+
* A non-`allow` outcome outranks `alreadyAllowed`: the whole point of
|
|
62
|
+
* returning the RECOMPUTED decision is that the page can never say "allowed"
|
|
63
|
+
* while the connection is still blocked (a nearer chain with an earlier deny,
|
|
64
|
+
* or a mode default, still wins). `alreadyAllowed` therefore only ever reads
|
|
65
|
+
* as success when the decision really is `allow`.
|
|
66
|
+
* @param result - the `permissionRules/allowHost` result.
|
|
67
|
+
* @returns the notice to render.
|
|
68
|
+
*/
|
|
69
|
+
export function allowHostNotice(result: AllowHostResult): AllowHostNotice {
|
|
70
|
+
if (!result.ok) return { ok: false, key: 'allowHostFailed', vars: { error: result.error ?? 'unknown error' } }
|
|
71
|
+
if (result.outcome !== 'allow') {
|
|
72
|
+
return { ok: false, key: 'allowHostStillBlocked', vars: { outcome: result.outcome ?? 'unknown' } }
|
|
73
|
+
}
|
|
74
|
+
if (result.alreadyAllowed) return { ok: true, key: 'allowHostAlready', vars: {} }
|
|
75
|
+
return { ok: true, key: 'allowHostSaved', vars: { path: result.path ?? '', reloaded: result.reloaded } }
|
|
76
|
+
}
|
package/src/allow-host.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The settings-page "allow this host" action,
|
|
2
|
+
* The settings-page "allow this host" action, host-only half: normalizing
|
|
3
3
|
* and validating a blocked host, inserting ONE minimal
|
|
4
|
-
* `match.network.domains` allow rule at the HEAD of a rule document,
|
|
5
|
-
* which rule file the rule belongs in
|
|
6
|
-
*
|
|
4
|
+
* `match.network.domains` allow rule at the HEAD of a rule document, and
|
|
5
|
+
* choosing which rule file the rule belongs in.
|
|
6
|
+
*
|
|
7
|
+
* The workspace choices (`allowHostWorkspaces`) and the notice mapping
|
|
8
|
+
* (`allowHostNotice`) live in `./allow-host-notice.ts`: the settings page
|
|
9
|
+
* imports them from the BROWSER bundle, whose bundler inlines every value
|
|
10
|
+
* import — keeping them next to the `node:path`/`node:net`/`yaml` code here
|
|
11
|
+
* emitted `require("process")`/`require("buffer")` calls the shell's frozen
|
|
12
|
+
* module table cannot answer, failing the whole plugin load. They are
|
|
13
|
+
* re-exported below so existing host-side imports keep working.
|
|
7
14
|
*
|
|
8
15
|
* Three invariants hold here and are asserted by `test/allow-host.spec.ts`:
|
|
9
16
|
*
|
|
@@ -27,7 +34,11 @@ import { isIP } from 'node:net'
|
|
|
27
34
|
import { isMap, isSeq, parseDocument } from 'yaml'
|
|
28
35
|
import type { YAMLSeq } from 'yaml'
|
|
29
36
|
import { RuleError, normalizeHost } from './rules.ts'
|
|
30
|
-
|
|
37
|
+
|
|
38
|
+
// Client-safe pieces re-exported from their own module — see the module doc
|
|
39
|
+
// above for why they must not live in this host-only file.
|
|
40
|
+
export { allowHostNotice, allowHostWorkspaces } from './allow-host-notice.ts'
|
|
41
|
+
export type { AllowHostNotice, AllowHostNoticeKey } from './allow-host-notice.ts'
|
|
31
42
|
|
|
32
43
|
/**
|
|
33
44
|
* Why a generated rule may legitimately live in a rule file the parser would
|
|
@@ -251,57 +262,3 @@ function samePath(a: string, b: string): boolean {
|
|
|
251
262
|
const right = resolve(b)
|
|
252
263
|
return process.platform === 'win32' ? left.toLowerCase() === right.toLowerCase() : left === right
|
|
253
264
|
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* The workspaces a host-level block may be allowed in: the distinct non-null
|
|
257
|
-
* `cwd`s of the loaded rule sources.
|
|
258
|
-
*
|
|
259
|
-
* A block with no cwd was judged by the proxy's session-less host chain, which
|
|
260
|
-
* any loaded workspace chain outranks the moment one exists — so writing the
|
|
261
|
-
* host file can leave the connection blocked while the rule sits in a file
|
|
262
|
-
* nothing consults. The settings page therefore offers these workspaces and
|
|
263
|
-
* sends the chosen one as the request's cwd; when there is exactly one (or
|
|
264
|
-
* none), there is nothing to choose between.
|
|
265
|
-
* @param sources - the snapshot's rule sources.
|
|
266
|
-
* @returns the distinct workspace roots, in source order.
|
|
267
|
-
*/
|
|
268
|
-
export function allowHostWorkspaces(sources: readonly RuleSourceView[]): string[] {
|
|
269
|
-
const seen = new Set<string>()
|
|
270
|
-
const workspaces: string[] = []
|
|
271
|
-
for (const source of sources) {
|
|
272
|
-
if (source.cwd === null || seen.has(source.cwd)) continue
|
|
273
|
-
seen.add(source.cwd)
|
|
274
|
-
workspaces.push(source.cwd)
|
|
275
|
-
}
|
|
276
|
-
return workspaces
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** The locale keys the allow action reports through. */
|
|
280
|
-
export type AllowHostNoticeKey = 'allowHostSaved' | 'allowHostAlready' | 'allowHostStillBlocked' | 'allowHostFailed'
|
|
281
|
-
|
|
282
|
-
/** The notice one action result maps onto: a locale key plus its interpolation values. */
|
|
283
|
-
export interface AllowHostNotice {
|
|
284
|
-
readonly ok: boolean
|
|
285
|
-
readonly key: AllowHostNoticeKey
|
|
286
|
-
readonly vars: Record<string, string | number>
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Map an action result onto the page notice.
|
|
291
|
-
*
|
|
292
|
-
* A non-`allow` outcome outranks `alreadyAllowed`: the whole point of
|
|
293
|
-
* returning the RECOMPUTED decision is that the page can never say "allowed"
|
|
294
|
-
* while the connection is still blocked (a nearer chain with an earlier deny,
|
|
295
|
-
* or a mode default, still wins). `alreadyAllowed` therefore only ever reads
|
|
296
|
-
* as success when the decision really is `allow`.
|
|
297
|
-
* @param result - the `permissionRules/allowHost` result.
|
|
298
|
-
* @returns the notice to render.
|
|
299
|
-
*/
|
|
300
|
-
export function allowHostNotice(result: AllowHostResult): AllowHostNotice {
|
|
301
|
-
if (!result.ok) return { ok: false, key: 'allowHostFailed', vars: { error: result.error ?? 'unknown error' } }
|
|
302
|
-
if (result.outcome !== 'allow') {
|
|
303
|
-
return { ok: false, key: 'allowHostStillBlocked', vars: { outcome: result.outcome ?? 'unknown' } }
|
|
304
|
-
}
|
|
305
|
-
if (result.alreadyAllowed) return { ok: true, key: 'allowHostAlready', vars: {} }
|
|
306
|
-
return { ok: true, key: 'allowHostSaved', vars: { path: result.path ?? '', reloaded: result.reloaded } }
|
|
307
|
-
}
|
package/src/client/section.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import * as React from 'react'
|
|
13
13
|
import type { ReactElement } from 'react'
|
|
14
|
-
import { allowHostNotice, allowHostWorkspaces } from '../allow-host.ts'
|
|
14
|
+
import { allowHostNotice, allowHostWorkspaces } from '../allow-host-notice.ts'
|
|
15
15
|
import type { PermissionRulesLocaleKey } from './locales.ts'
|
|
16
16
|
import type { AllowHostRequest, AllowHostResult, NetworkBlockView, PermissionRulesSnapshot, RulesReadResult, RulesReloadResult, RulesSaveResult } from '../wire.ts'
|
|
17
17
|
|