@landstrip/landstrip-api 0.19.20 → 0.20.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 +32 -1
- package/lib/index.d.ts +57 -8
- package/lib/shared.d.ts +33 -18
- package/lib/shared.js +137 -10
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ See the [quick start](../../README.md#quick-start) for a policy and CLI example;
|
|
|
18
18
|
const { execFileSync } = require("node:child_process");
|
|
19
19
|
const { binaryPath } = require("@landstrip/landstrip-api");
|
|
20
20
|
|
|
21
|
-
execFileSync(binaryPath(), ["-p", "policy.json", "--", "cargo", "test"], {
|
|
21
|
+
execFileSync(binaryPath(), ["run", "-p", "policy.json", "--", "cargo", "test"], {
|
|
22
22
|
stdio: "inherit",
|
|
23
23
|
});
|
|
24
24
|
```
|
|
@@ -39,6 +39,37 @@ Proxy helpers: [`/proxy`](lib/proxy.d.ts). [`/shared`](lib/shared.d.ts) provides
|
|
|
39
39
|
Failed writes clean up; failed cleanup reports both errors and the directory
|
|
40
40
|
path in an `AggregateError`.
|
|
41
41
|
|
|
42
|
+
## macOS log correlation
|
|
43
|
+
|
|
44
|
+
Both `LandstripDarwinPolicy` and `SandboxDarwinConfig` accept optional
|
|
45
|
+
`logTag?: string`, under the JSON key `macos` (not `darwin`):
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
const { buildLandstripPolicy } = require("@landstrip/landstrip-api/shared");
|
|
49
|
+
const policy = buildLandstripPolicy({ macos: { logTag: "agent-session-42" } });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`macos.logTag` tags Seatbelt denial logs for correlation. It must be nonempty
|
|
53
|
+
and contain no double quote, backslash, ASCII control character (0x00–0x1f),
|
|
54
|
+
or DEL (0x7f). `null` is invalid. `buildLandstripPolicy` and `parseSandboxConfig`
|
|
55
|
+
validate and forward it; `deepMergeSandboxConfig` uses the override tag when
|
|
56
|
+
provided, otherwise preserving the base tag.
|
|
57
|
+
|
|
58
|
+
## Trap transport and reporting
|
|
59
|
+
|
|
60
|
+
On Unix, `--trap FD|PATH` accepts either an inherited open FD ≥3 or an absolute
|
|
61
|
+
Unix domain stream socket path. Start the socket listener before launching
|
|
62
|
+
Landstrip: the host Landstrip process connects before sandbox setup, and the
|
|
63
|
+
sandboxed child never inherits the trap transport.
|
|
64
|
+
|
|
65
|
+
On Linux, the stream is bidirectional and uses the existing broker query
|
|
66
|
+
protocol, not merely passive monitoring. For `state: "query"` events, reply on
|
|
67
|
+
the same socket with `controlResponseLine(trap.query_id, "allow")` (or `"deny"`);
|
|
68
|
+
the syscall waits for an answer. Completed events have `state: "info"`.
|
|
69
|
+
Explicit read denials (`DenyMatch`, JSON `reason: "deny_match"`) are reported
|
|
70
|
+
even when the command exits 0. macOS reports setup/launch failures on the trap
|
|
71
|
+
transport and closes it on successful exec; use Seatbelt logs for runtime denials.
|
|
72
|
+
|
|
42
73
|
## Development
|
|
43
74
|
|
|
44
75
|
Run `make ci` from the repository root.
|
package/lib/index.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ export type LandstripPolicyErrorCode =
|
|
|
93
93
|
| 'POLICY_DENY_WRITE_SYMLINK_ANCESTOR'
|
|
94
94
|
| 'POLICY_INVALID_PORT'
|
|
95
95
|
| 'POLICY_EMPTY_PATH'
|
|
96
|
+
| 'POLICY_INVALID_LOG_TAG'
|
|
96
97
|
| 'POLICY_HOME_UNAVAILABLE'
|
|
97
98
|
| 'POLICY_TRAVERSAL_DEPTH';
|
|
98
99
|
|
|
@@ -145,24 +146,38 @@ export interface LandstripControlResponse {
|
|
|
145
146
|
action: 'allow' | 'deny';
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
export type LandstripPathSelector =
|
|
150
|
+
| string
|
|
151
|
+
| { literal: string }
|
|
152
|
+
| { glob: string };
|
|
153
|
+
|
|
148
154
|
export interface LandstripFilesystemPolicy {
|
|
149
|
-
denyRead?:
|
|
150
|
-
denyReadAlways?:
|
|
151
|
-
allowRead?:
|
|
152
|
-
allowWrite?:
|
|
153
|
-
denyWrite?:
|
|
154
|
-
denyWriteAlways?:
|
|
155
|
+
denyRead?: LandstripPathSelector[];
|
|
156
|
+
denyReadAlways?: LandstripPathSelector[];
|
|
157
|
+
allowRead?: LandstripPathSelector[];
|
|
158
|
+
allowWrite?: LandstripPathSelector[];
|
|
159
|
+
denyWrite?: LandstripPathSelector[];
|
|
160
|
+
denyWriteAlways?: LandstripPathSelector[];
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
export interface LandstripNetworkPolicy {
|
|
158
164
|
allowNetwork?: boolean;
|
|
159
165
|
allowLocalBinding?: boolean;
|
|
160
166
|
allowAllUnixSockets?: boolean;
|
|
161
|
-
allowUnixSockets?:
|
|
167
|
+
allowUnixSockets?: LandstripPathSelector[];
|
|
162
168
|
httpProxyPort?: number;
|
|
163
169
|
socksProxyPort?: number;
|
|
164
170
|
}
|
|
165
171
|
|
|
172
|
+
export interface LandstripDarwinPolicy {
|
|
173
|
+
/** Seatbelt log correlation tag: nonempty, without double quotes, backslashes, ASCII controls or DEL; not null. */
|
|
174
|
+
logTag?: string;
|
|
175
|
+
allowTrustd?: boolean;
|
|
176
|
+
allowMachLookup?: string[];
|
|
177
|
+
allowAppleEvents?: boolean;
|
|
178
|
+
allowPty?: boolean;
|
|
179
|
+
}
|
|
180
|
+
|
|
166
181
|
export type LandstripWindowsAppContainerMode = 'lpac' | 'standard';
|
|
167
182
|
|
|
168
183
|
export interface LandstripWindowsPolicy {
|
|
@@ -173,9 +188,32 @@ export interface LandstripWindowsPolicy {
|
|
|
173
188
|
export interface LandstripPolicy {
|
|
174
189
|
filesystem?: LandstripFilesystemPolicy;
|
|
175
190
|
network?: LandstripNetworkPolicy;
|
|
191
|
+
macos?: LandstripDarwinPolicy;
|
|
176
192
|
windows?: LandstripWindowsPolicy;
|
|
177
193
|
}
|
|
178
194
|
|
|
195
|
+
export type SandboxImplementation =
|
|
196
|
+
| 'landlock+seccomp'
|
|
197
|
+
| 'seatbelt'
|
|
198
|
+
| 'appContainer'
|
|
199
|
+
| 'restrictedUser';
|
|
200
|
+
|
|
201
|
+
export interface FeatureCapabilities {
|
|
202
|
+
filesystemTiers: string[];
|
|
203
|
+
networkMediation: boolean;
|
|
204
|
+
unixSocketFiltering: boolean;
|
|
205
|
+
processIsolation: boolean;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface DoctorReport {
|
|
209
|
+
ok: boolean;
|
|
210
|
+
platform: string;
|
|
211
|
+
implementation: SandboxImplementation;
|
|
212
|
+
supported: FeatureCapabilities;
|
|
213
|
+
available: FeatureCapabilities;
|
|
214
|
+
error?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
179
217
|
export interface SandboxFilesystemConfig {
|
|
180
218
|
denyRead: string[];
|
|
181
219
|
denyReadAlways: string[];
|
|
@@ -200,6 +238,15 @@ export interface SandboxNetworkConfig {
|
|
|
200
238
|
deniedDomains: string[];
|
|
201
239
|
}
|
|
202
240
|
|
|
241
|
+
export interface SandboxDarwinConfig {
|
|
242
|
+
/** Optional Seatbelt log correlation tag; same restrictions as LandstripDarwinPolicy.logTag. */
|
|
243
|
+
logTag?: string;
|
|
244
|
+
allowTrustd: boolean;
|
|
245
|
+
allowMachLookup: string[];
|
|
246
|
+
allowAppleEvents: boolean;
|
|
247
|
+
allowPty: boolean;
|
|
248
|
+
}
|
|
249
|
+
|
|
203
250
|
export interface SandboxWindowsConfig {
|
|
204
251
|
appContainerMode: LandstripWindowsAppContainerMode;
|
|
205
252
|
allowLoopback: boolean;
|
|
@@ -210,12 +257,14 @@ export interface SandboxConfig {
|
|
|
210
257
|
shell?: SandboxShellConfig;
|
|
211
258
|
network: SandboxNetworkConfig;
|
|
212
259
|
filesystem: SandboxFilesystemConfig;
|
|
260
|
+
macos?: SandboxDarwinConfig;
|
|
213
261
|
windows?: SandboxWindowsConfig;
|
|
214
262
|
}
|
|
215
263
|
|
|
216
264
|
export type SandboxFilesystemConfigFile = Partial<SandboxFilesystemConfig>;
|
|
217
265
|
export type SandboxShellConfigFile = Partial<SandboxShellConfig>;
|
|
218
266
|
export type SandboxNetworkConfigFile = Partial<SandboxNetworkConfig>;
|
|
267
|
+
export type SandboxDarwinConfigFile = Partial<SandboxDarwinConfig>;
|
|
219
268
|
export type SandboxWindowsConfigFile = Partial<SandboxWindowsConfig>;
|
|
220
269
|
|
|
221
270
|
export interface SandboxConfigFile {
|
|
@@ -223,12 +272,12 @@ export interface SandboxConfigFile {
|
|
|
223
272
|
shell?: SandboxShellConfigFile;
|
|
224
273
|
network?: SandboxNetworkConfigFile;
|
|
225
274
|
filesystem?: SandboxFilesystemConfigFile;
|
|
275
|
+
macos?: SandboxDarwinConfigFile;
|
|
226
276
|
windows?: SandboxWindowsConfigFile;
|
|
227
277
|
}
|
|
228
278
|
|
|
229
279
|
export type SandboxConfigOverrides = SandboxConfigFile;
|
|
230
280
|
|
|
231
|
-
|
|
232
281
|
export type PathAccessDecision = 'allow' | 'ask' | 'deny' | 'denyAlways';
|
|
233
282
|
|
|
234
283
|
export type DomainAccessDecision = 'allow' | 'ask' | 'deny';
|
package/lib/shared.d.ts
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
+
DoctorReport,
|
|
5
|
+
FeatureCapabilities,
|
|
4
6
|
LandstripControlResponse,
|
|
7
|
+
LandstripDarwinPolicy,
|
|
5
8
|
LandstripFilesystemPolicy,
|
|
6
9
|
LandstripFilesystemTrap,
|
|
7
10
|
LandstripNetworkPolicy,
|
|
8
11
|
LandstripNetworkTrap,
|
|
12
|
+
LandstripPathSelector,
|
|
9
13
|
LandstripPolicy,
|
|
10
14
|
LandstripTrap,
|
|
11
15
|
LandstripWindowsPolicy,
|
|
12
16
|
SandboxConfig,
|
|
13
17
|
SandboxConfigFile,
|
|
14
18
|
SandboxConfigOverrides,
|
|
19
|
+
SandboxDarwinConfig,
|
|
20
|
+
SandboxDarwinConfigFile,
|
|
15
21
|
SandboxFilesystemConfig,
|
|
16
22
|
SandboxFilesystemConfigFile,
|
|
23
|
+
SandboxImplementation,
|
|
17
24
|
SandboxNetworkConfig,
|
|
18
25
|
SandboxNetworkConfigFile,
|
|
19
26
|
SandboxShellConfig,
|
|
@@ -27,11 +34,18 @@ export type LandstripDenialTrap = LandstripFilesystemTrap | LandstripNetworkTrap
|
|
|
27
34
|
export type LandstripQueryTrap = LandstripDenialTrap & { state: 'query' };
|
|
28
35
|
|
|
29
36
|
export type {
|
|
37
|
+
DoctorReport,
|
|
38
|
+
FeatureCapabilities,
|
|
39
|
+
LandstripDarwinPolicy,
|
|
40
|
+
LandstripPathSelector,
|
|
30
41
|
SandboxConfig,
|
|
31
42
|
SandboxConfigFile,
|
|
32
43
|
SandboxConfigOverrides,
|
|
44
|
+
SandboxDarwinConfig,
|
|
45
|
+
SandboxDarwinConfigFile,
|
|
33
46
|
SandboxFilesystemConfig,
|
|
34
47
|
SandboxFilesystemConfigFile,
|
|
48
|
+
SandboxImplementation,
|
|
35
49
|
SandboxNetworkConfig,
|
|
36
50
|
SandboxNetworkConfigFile,
|
|
37
51
|
SandboxShellConfig,
|
|
@@ -92,24 +106,25 @@ export function controlResponseLine(
|
|
|
92
106
|
|
|
93
107
|
|
|
94
108
|
export interface BuildLandstripPolicyOptions {
|
|
95
|
-
filesystem?: LandstripFilesystemPolicy;
|
|
96
|
-
network?: LandstripNetworkPolicy;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
filesystem?: LandstripFilesystemPolicy | undefined;
|
|
110
|
+
network?: LandstripNetworkPolicy | undefined;
|
|
111
|
+
macos?: LandstripDarwinPolicy | undefined;
|
|
112
|
+
windows?: LandstripWindowsPolicy | undefined;
|
|
113
|
+
baseDirectory?: string | undefined;
|
|
114
|
+
httpProxyPort?: number | null | undefined;
|
|
115
|
+
socksProxyPort?: number | null | undefined;
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
export function resolveFilesystemPatterns(
|
|
104
|
-
patterns:
|
|
119
|
+
patterns: LandstripPathSelector[] | undefined,
|
|
105
120
|
baseDirectory?: string,
|
|
106
|
-
):
|
|
107
|
-
|
|
121
|
+
): LandstripPathSelector[];
|
|
108
122
|
export function resolveFilesystemPolicy(
|
|
109
123
|
filesystem?: LandstripFilesystemPolicy,
|
|
110
124
|
baseDirectory?: string,
|
|
111
125
|
): LandstripFilesystemPolicy;
|
|
112
126
|
|
|
127
|
+
/** Forwards macos.logTag; throws if it is empty, non-string, or contains a double quote, backslash, ASCII control or DEL. */
|
|
113
128
|
export function buildLandstripPolicy(
|
|
114
129
|
options: BuildLandstripPolicyOptions,
|
|
115
130
|
): LandstripPolicy;
|
|
@@ -122,10 +137,11 @@ export function writeLandstripPolicyFile(
|
|
|
122
137
|
): { dir: string; path: string };
|
|
123
138
|
|
|
124
139
|
|
|
125
|
-
export function mergeArray(base:
|
|
126
|
-
|
|
140
|
+
export function mergeArray<T = string>(base: T[], override?: T[]): T[];
|
|
141
|
+
/** Parses sandbox settings, validating macos.logTag with the same rules as buildLandstripPolicy (null is invalid). */
|
|
127
142
|
export function parseSandboxConfig(value: unknown): SandboxConfigFile;
|
|
128
143
|
|
|
144
|
+
/** Merges arrays and applies override scalar precedence, including macos.logTag. */
|
|
129
145
|
export function deepMergeSandboxConfig<T extends SandboxConfig = SandboxConfig>(
|
|
130
146
|
base: T,
|
|
131
147
|
overrides?: SandboxConfigFile,
|
|
@@ -153,36 +169,35 @@ export interface EvaluateDomainOptions {
|
|
|
153
169
|
|
|
154
170
|
export function matchesPathPattern(
|
|
155
171
|
filePath: string,
|
|
156
|
-
patterns:
|
|
172
|
+
patterns: LandstripPathSelector[],
|
|
157
173
|
baseDirectory?: string,
|
|
158
174
|
): boolean;
|
|
159
175
|
|
|
160
176
|
export function matchPathSpecificity(
|
|
161
177
|
filePath: string,
|
|
162
|
-
patterns:
|
|
178
|
+
patterns: LandstripPathSelector[],
|
|
163
179
|
baseDirectory?: string,
|
|
164
180
|
): number;
|
|
165
181
|
|
|
166
182
|
export function isPathReadAllowed(
|
|
167
183
|
filePath: string,
|
|
168
|
-
allowRead:
|
|
169
|
-
denyRead:
|
|
184
|
+
allowRead: LandstripPathSelector[],
|
|
185
|
+
denyRead: LandstripPathSelector[],
|
|
170
186
|
baseDirectory?: string,
|
|
171
187
|
options?: { requireAllowMatch?: boolean },
|
|
172
188
|
): boolean;
|
|
173
189
|
|
|
174
190
|
export function isPathWriteAllowed(
|
|
175
191
|
filePath: string,
|
|
176
|
-
allowWrite:
|
|
192
|
+
allowWrite: LandstripPathSelector[],
|
|
177
193
|
baseDirectory?: string,
|
|
178
194
|
): boolean;
|
|
179
195
|
|
|
180
196
|
export function shouldPromptForWrite(
|
|
181
197
|
filePath: string,
|
|
182
|
-
allowWrite:
|
|
198
|
+
allowWrite: LandstripPathSelector[],
|
|
183
199
|
baseDirectory?: string,
|
|
184
200
|
): boolean;
|
|
185
|
-
|
|
186
201
|
export function evaluateReadAccess(
|
|
187
202
|
filePath: string,
|
|
188
203
|
filesystem: SandboxFilesystemConfig | SandboxFilesystemConfigFile | SandboxConfig | SandboxConfigFile,
|
package/lib/shared.js
CHANGED
|
@@ -142,6 +142,7 @@ const LANDSTRIP_INTERNAL_CODES = new Set([
|
|
|
142
142
|
'POLICY_DENY_WRITE_SYMLINK_ANCESTOR',
|
|
143
143
|
'POLICY_INVALID_PORT',
|
|
144
144
|
'POLICY_EMPTY_PATH',
|
|
145
|
+
'POLICY_INVALID_LOG_TAG',
|
|
145
146
|
'POLICY_HOME_UNAVAILABLE',
|
|
146
147
|
'POLICY_TRAVERSAL_DEPTH',
|
|
147
148
|
'SUPERVISE_FAILED',
|
|
@@ -418,14 +419,27 @@ function controlResponseLine(queryId, action) {
|
|
|
418
419
|
return JSON.stringify({ query_id: queryId, action }) + '\n';
|
|
419
420
|
}
|
|
420
421
|
|
|
422
|
+
function resolvePathSelector(entry, baseDirectory) {
|
|
423
|
+
if (typeof entry === 'string') {
|
|
424
|
+
return /[*?[\]]/.test(entry)
|
|
425
|
+
? canonicalizeGlobPattern(entry, baseDirectory)
|
|
426
|
+
: canonicalizePath(entry, baseDirectory);
|
|
427
|
+
}
|
|
428
|
+
if (isRecord(entry)) {
|
|
429
|
+
if (typeof entry.literal === 'string') {
|
|
430
|
+
return { literal: canonicalizePath(entry.literal, baseDirectory) };
|
|
431
|
+
}
|
|
432
|
+
if (typeof entry.glob === 'string') {
|
|
433
|
+
return { glob: canonicalizeGlobPattern(entry.glob, baseDirectory) };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return entry;
|
|
437
|
+
}
|
|
438
|
+
|
|
421
439
|
function resolveFilesystemPatterns(patterns, baseDirectory) {
|
|
422
440
|
if (!patterns || !Array.isArray(patterns)) return [];
|
|
423
441
|
if (!baseDirectory) return [...patterns];
|
|
424
|
-
return patterns.map((pattern) =>
|
|
425
|
-
/[*?[\]]/.test(pattern)
|
|
426
|
-
? canonicalizeGlobPattern(pattern, baseDirectory)
|
|
427
|
-
: canonicalizePath(pattern, baseDirectory),
|
|
428
|
-
);
|
|
442
|
+
return patterns.map((pattern) => resolvePathSelector(pattern, baseDirectory));
|
|
429
443
|
}
|
|
430
444
|
|
|
431
445
|
function resolveFilesystemPolicy(filesystem, baseDirectory) {
|
|
@@ -452,6 +466,14 @@ function resolveFilesystemPolicy(filesystem, baseDirectory) {
|
|
|
452
466
|
return resolved;
|
|
453
467
|
}
|
|
454
468
|
|
|
469
|
+
function validateDarwinLogTag(value) {
|
|
470
|
+
if (typeof value !== 'string' || value.length === 0 || /["\\\x00-\x1f\x7f]/.test(value)) {
|
|
471
|
+
throw new Error(
|
|
472
|
+
'macos.logTag must be a nonempty string without double quotes, backslashes, ASCII control characters or DEL',
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
455
477
|
function buildLandstripPolicy(options) {
|
|
456
478
|
const policy = {};
|
|
457
479
|
if (options.network) {
|
|
@@ -485,6 +507,20 @@ function buildLandstripPolicy(options) {
|
|
|
485
507
|
options.baseDirectory,
|
|
486
508
|
);
|
|
487
509
|
}
|
|
510
|
+
if (options.macos) {
|
|
511
|
+
policy.macos = {
|
|
512
|
+
allowTrustd: Boolean(options.macos.allowTrustd),
|
|
513
|
+
allowMachLookup: Array.isArray(options.macos.allowMachLookup)
|
|
514
|
+
? [...options.macos.allowMachLookup]
|
|
515
|
+
: [],
|
|
516
|
+
allowAppleEvents: Boolean(options.macos.allowAppleEvents),
|
|
517
|
+
allowPty: Boolean(options.macos.allowPty),
|
|
518
|
+
};
|
|
519
|
+
if (options.macos.logTag !== undefined) {
|
|
520
|
+
validateDarwinLogTag(options.macos.logTag);
|
|
521
|
+
policy.macos.logTag = options.macos.logTag;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
488
524
|
if (options.windows) {
|
|
489
525
|
policy.windows = {
|
|
490
526
|
appContainerMode: options.windows.appContainerMode ?? 'lpac',
|
|
@@ -552,12 +588,28 @@ function validateStringArrayFields(value, fields, prefix = '') {
|
|
|
552
588
|
|
|
553
589
|
function mergeArray(base, override) {
|
|
554
590
|
if (!override) return base;
|
|
555
|
-
|
|
591
|
+
const result = [];
|
|
592
|
+
const seen = new Set();
|
|
593
|
+
for (const item of [...(base ?? []), ...override]) {
|
|
594
|
+
const key = typeof item === 'string' ? `s:${item}` : `o:${JSON.stringify(item)}`;
|
|
595
|
+
if (!seen.has(key)) {
|
|
596
|
+
seen.add(key);
|
|
597
|
+
result.push(item);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return result;
|
|
556
601
|
}
|
|
557
602
|
|
|
558
603
|
function parseSandboxConfig(value) {
|
|
559
604
|
requireSandboxObject(value, 'sandbox config');
|
|
560
|
-
rejectUnknownSandboxFields(value, [
|
|
605
|
+
rejectUnknownSandboxFields(value, [
|
|
606
|
+
'enabled',
|
|
607
|
+
'shell',
|
|
608
|
+
'network',
|
|
609
|
+
'filesystem',
|
|
610
|
+
'macos',
|
|
611
|
+
'windows',
|
|
612
|
+
]);
|
|
561
613
|
validateBooleanFields(value, ['enabled']);
|
|
562
614
|
|
|
563
615
|
const config = {};
|
|
@@ -656,6 +708,38 @@ function parseSandboxConfig(value) {
|
|
|
656
708
|
}
|
|
657
709
|
}
|
|
658
710
|
|
|
711
|
+
if (value.macos !== undefined) {
|
|
712
|
+
requireSandboxObject(value.macos, 'macos');
|
|
713
|
+
rejectUnknownSandboxFields(
|
|
714
|
+
value.macos,
|
|
715
|
+
['allowTrustd', 'allowMachLookup', 'allowAppleEvents', 'allowPty', 'logTag'],
|
|
716
|
+
'macos.',
|
|
717
|
+
);
|
|
718
|
+
validateBooleanFields(
|
|
719
|
+
value.macos,
|
|
720
|
+
['allowTrustd', 'allowAppleEvents', 'allowPty'],
|
|
721
|
+
'macos.',
|
|
722
|
+
);
|
|
723
|
+
validateStringArrayFields(value.macos, ['allowMachLookup'], 'macos.');
|
|
724
|
+
config.macos = {};
|
|
725
|
+
if (value.macos.allowTrustd !== undefined) {
|
|
726
|
+
config.macos.allowTrustd = value.macos.allowTrustd;
|
|
727
|
+
}
|
|
728
|
+
if (value.macos.allowMachLookup !== undefined) {
|
|
729
|
+
config.macos.allowMachLookup = [...value.macos.allowMachLookup];
|
|
730
|
+
}
|
|
731
|
+
if (value.macos.allowAppleEvents !== undefined) {
|
|
732
|
+
config.macos.allowAppleEvents = value.macos.allowAppleEvents;
|
|
733
|
+
}
|
|
734
|
+
if (value.macos.allowPty !== undefined) {
|
|
735
|
+
config.macos.allowPty = value.macos.allowPty;
|
|
736
|
+
}
|
|
737
|
+
if (value.macos.logTag !== undefined) {
|
|
738
|
+
validateDarwinLogTag(value.macos.logTag);
|
|
739
|
+
config.macos.logTag = value.macos.logTag;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
659
743
|
if (value.windows !== undefined) {
|
|
660
744
|
requireSandboxObject(value.windows, 'windows');
|
|
661
745
|
rejectUnknownSandboxFields(value.windows, ['appContainerMode', 'allowLoopback'], 'windows.');
|
|
@@ -685,6 +769,7 @@ function deepMergeSandboxConfig(base, overrides) {
|
|
|
685
769
|
const shell = overrides.shell;
|
|
686
770
|
const network = overrides.network;
|
|
687
771
|
const filesystem = overrides.filesystem;
|
|
772
|
+
const macos = overrides.macos;
|
|
688
773
|
const windows = overrides.windows;
|
|
689
774
|
|
|
690
775
|
const result = {
|
|
@@ -713,6 +798,19 @@ function deepMergeSandboxConfig(base, overrides) {
|
|
|
713
798
|
};
|
|
714
799
|
}
|
|
715
800
|
|
|
801
|
+
if (base.macos !== undefined || macos !== undefined) {
|
|
802
|
+
result.macos = {
|
|
803
|
+
allowTrustd: macos?.allowTrustd ?? base.macos?.allowTrustd ?? false,
|
|
804
|
+
allowMachLookup: mergeArray(base.macos?.allowMachLookup ?? [], macos?.allowMachLookup),
|
|
805
|
+
allowAppleEvents: macos?.allowAppleEvents ?? base.macos?.allowAppleEvents ?? false,
|
|
806
|
+
allowPty: macos?.allowPty ?? base.macos?.allowPty ?? false,
|
|
807
|
+
};
|
|
808
|
+
const logTag = macos?.logTag ?? base.macos?.logTag;
|
|
809
|
+
if (logTag !== undefined) {
|
|
810
|
+
result.macos.logTag = logTag;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
716
814
|
if (base.windows !== undefined || windows !== undefined) {
|
|
717
815
|
result.windows = {
|
|
718
816
|
appContainerMode: windows?.appContainerMode ?? base.windows?.appContainerMode ?? 'standard',
|
|
@@ -729,14 +827,43 @@ function normalizePathForComparison(filePath) {
|
|
|
729
827
|
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
|
|
730
828
|
}
|
|
731
829
|
|
|
830
|
+
function resolveSelectorForMatch(pattern, baseDirectory) {
|
|
831
|
+
if (isRecord(pattern)) {
|
|
832
|
+
if (typeof pattern.literal === 'string') {
|
|
833
|
+
return {
|
|
834
|
+
canonical: canonicalizePath(pattern.literal, baseDirectory),
|
|
835
|
+
isGlob: false,
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
if (typeof pattern.glob === 'string') {
|
|
839
|
+
const isGlob = /[*?[\]]/.test(pattern.glob);
|
|
840
|
+
return {
|
|
841
|
+
canonical: isGlob
|
|
842
|
+
? canonicalizeGlobPattern(pattern.glob, baseDirectory)
|
|
843
|
+
: canonicalizePath(pattern.glob, baseDirectory),
|
|
844
|
+
isGlob,
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
const raw = String(pattern);
|
|
849
|
+
const isGlob = /[*?[\]]/.test(raw);
|
|
850
|
+
return {
|
|
851
|
+
canonical: isGlob
|
|
852
|
+
? canonicalizeGlobPattern(raw, baseDirectory)
|
|
853
|
+
: canonicalizePath(raw, baseDirectory),
|
|
854
|
+
isGlob,
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
|
|
732
858
|
function matchesPathPattern(filePath, patterns, baseDirectory = process.cwd()) {
|
|
733
859
|
if (!Array.isArray(patterns) || patterns.length === 0) return false;
|
|
734
860
|
const abs = normalizePathForComparison(canonicalizePath(filePath, baseDirectory));
|
|
735
861
|
|
|
736
862
|
return patterns.some((pattern) => {
|
|
737
|
-
const
|
|
863
|
+
const { canonical, isGlob } = resolveSelectorForMatch(pattern, baseDirectory);
|
|
864
|
+
const absPattern = normalizePathForComparison(canonical);
|
|
738
865
|
|
|
739
|
-
if (
|
|
866
|
+
if (isGlob) {
|
|
740
867
|
const matcher = globToRegExp(absPattern);
|
|
741
868
|
for (let candidate = abs; ;) {
|
|
742
869
|
if (matcher.test(candidate)) return true;
|
|
@@ -757,7 +884,7 @@ function matchPathSpecificity(filePath, patterns, baseDirectory = process.cwd())
|
|
|
757
884
|
let best = -1;
|
|
758
885
|
for (const pattern of patterns) {
|
|
759
886
|
if (!matchesPathPattern(filePath, [pattern], baseDirectory)) continue;
|
|
760
|
-
const canonical =
|
|
887
|
+
const { canonical } = resolveSelectorForMatch(pattern, baseDirectory);
|
|
761
888
|
if (canonical.length > best) {
|
|
762
889
|
best = canonical.length;
|
|
763
890
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@landstrip/landstrip-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Sandbox runner using Landlock, Seatbelt, and AppContainer",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/landstrip/landstrip#readme",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"ipaddr.js": "^2.5.0"
|
|
41
41
|
},
|
|
42
42
|
"optionalDependencies": {
|
|
43
|
-
"@landstrip/landstrip-darwin-arm64": "0.
|
|
44
|
-
"@landstrip/landstrip-darwin-x64": "0.
|
|
45
|
-
"@landstrip/landstrip-linux-x64": "0.
|
|
46
|
-
"@landstrip/landstrip-linux-arm64": "0.
|
|
47
|
-
"@landstrip/landstrip-win32-x64": "0.
|
|
48
|
-
"@landstrip/landstrip-win32-arm64": "0.
|
|
43
|
+
"@landstrip/landstrip-darwin-arm64": "0.20.0",
|
|
44
|
+
"@landstrip/landstrip-darwin-x64": "0.20.0",
|
|
45
|
+
"@landstrip/landstrip-linux-x64": "0.20.0",
|
|
46
|
+
"@landstrip/landstrip-linux-arm64": "0.20.0",
|
|
47
|
+
"@landstrip/landstrip-win32-x64": "0.20.0",
|
|
48
|
+
"@landstrip/landstrip-win32-arm64": "0.20.0"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|