@landstrip/landstrip 0.16.22 → 0.17.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.
Files changed (3) hide show
  1. package/README.md +66 -22
  2. package/lib/index.d.ts +151 -0
  3. package/package.json +12 -6
package/README.md CHANGED
@@ -42,8 +42,9 @@ statically, e.g. for many filesystem mutator syscalls, or when denials must be
42
42
  reported back to the launcher. The broker intercepts `openat`/`openat2` via
43
43
  seccomp user-notifications, resolves the real path, and validates it
44
44
 
45
- Finally there's some logic to deduce that Landlock and Seccomp (mostly for
46
- mutator syscall, which take fd) are fairly disjoint entities.
45
+ Landlock and seccomp cover mostly disjoint filesystem operations: Landlock
46
+ handles kernel-enforced path access, while seccomp mediates unsupported mutators
47
+ and reports broker decisions.
47
48
 
48
49
  ### Windows
49
50
 
@@ -135,18 +136,20 @@ For a filesystem-only sandbox with unrestricted direct network access, set:
135
136
  policy enforcement in place. On Windows this grants the AppContainer its network
136
137
  capabilities; without it the container denies all network access.
137
138
 
138
- ## Denial Traps
139
+ ## Traps
139
140
 
140
- Sandbox denials are reported as JSON objects, one per line, each with a fixed
141
- `kind` discriminant and a stable `code`. Consumers route on `kind` for coarse
142
- grouping and on `code` for the policy denial class. Traps go to standard error
143
- by default; `--trap-fd FD` writes them to an already-open descriptor instead.
141
+ Every landstrip event a sandbox denial, and every failure that keeps the tool
142
+ from running is reported as a JSON object, one per line, with a fixed `kind`
143
+ discriminant and a stable `code`. Consumers route on `kind` for the shape of the
144
+ record and on `code` for what happened. Failure traps and completed denial traps
145
+ go to standard error by default. On Linux, pending query traps go only to
146
+ `--trap-fd FD`, which writes them to an already-open descriptor.
144
147
 
145
148
  ```sh
146
149
  landstrip --trap-fd 3 -p policy.json cargo test 3>landstrip-traps.txt
147
150
  ```
148
151
 
149
- The two trap kinds are:
152
+ The trap kinds are:
150
153
 
151
154
  - `filesystem` (`code` `FILESYSTEM_DENIED`): `operation` is `read` or `write`,
152
155
  `path` is the resolved path, `requested_path` is the tool's original path when
@@ -154,21 +157,43 @@ The two trap kinds are:
154
157
  `process` carry routing context.
155
158
  - `network` (`code` `NETWORK_DENIED`): `operation` is `connect` or `bind` and
156
159
  `target` is `address:port`, with `syscall`, `errno`, and `process` context.
157
-
158
- `reason` is a platform-independent classification of the decision, derived from
159
- the policy and the requested path:
160
+ - `launch` (`code` `LAUNCH_FAILED`): the sandbox was installed but the tool did
161
+ not start. `program` is the tool, `errno` its symbolic errno where the platform
162
+ has one, and `message` the system's text.
163
+ - `usage` (`code` `USAGE_ERROR`): the command line was rejected. Exits with
164
+ status 2, and reaches standard error only — the trap descriptor is part of the
165
+ arguments that failed to parse.
166
+ - `internal`: everything that fails before the tool runs. `code` names the stage:
167
+ `POLICY_PARSE_FAILED`, `POLICY_IO_FAILED`, `SANDBOX_SETUP_FAILED`,
168
+ `SUPERVISE_FAILED`, `PLATFORM_UNSUPPORTED`, `INTEGER_TOO_LARGE`, a
169
+ `POLICY_*` validation rejection (`POLICY_UNRESTRICTED_READ`,
170
+ `POLICY_TCP_BIND_UNSUPPORTED`, `POLICY_UNIX_SOCKET_UNSUPPORTED`,
171
+ `POLICY_UNIX_SOCKET_PATH`, `POLICY_DENY_WRITE_SYMLINK_ANCESTOR`,
172
+ `POLICY_INVALID_PORT`, `POLICY_EMPTY_PATH`, `POLICY_HOME_UNAVAILABLE`,
173
+ `POLICY_TRAVERSAL_DEPTH`), or `INTERNAL_ERROR` for a failure the code space
174
+ does not name.
175
+
176
+ A code names the stage that failed, not the operating system that reported it:
177
+ the same `LAUNCH_FAILED` or `SANDBOX_SETUP_FAILED` is raised by every backend
178
+ that has that stage. The platform detail rides along in the record instead.
179
+
180
+ `mechanism` records the kernel layer an event is attributed to: `landlock`,
181
+ `seccomp`, `seatbelt`, or `appcontainer`. Per-denial traps are always `seccomp`,
182
+ the only layer with a per-denial callback; Landlock enforces in-kernel without
183
+ one. `SANDBOX_SETUP_FAILED` carries the mechanism that could not be installed.
184
+
185
+ `reason` is a platform-independent classification of a filesystem decision,
186
+ derived from the policy and the requested path:
160
187
 
161
188
  - `allow_miss`: the path matched no allow root and was denied by default.
162
189
  - `deny_match`: the path matched an explicit deny root that overrides an allow.
163
190
 
164
- `mechanism` records the kernel layer that detected the denial. Per-denial traps
165
- are always `seccomp`, the only layer with a per-denial callback; Landlock
166
- enforces in-kernel without one.
167
-
168
191
  ```json
169
192
  {
170
193
  "kind": "filesystem",
171
194
  "code": "FILESYSTEM_DENIED",
195
+ "state": "info",
196
+ "query_id": "0",
172
197
  "operation": "write",
173
198
  "path": "/repo/out",
174
199
  "requested_path": "out",
@@ -183,6 +208,8 @@ enforces in-kernel without one.
183
208
  {
184
209
  "kind": "network",
185
210
  "code": "NETWORK_DENIED",
211
+ "state": "info",
212
+ "query_id": "0",
186
213
  "operation": "connect",
187
214
  "target": "127.0.0.1:9999",
188
215
  "syscall": "connect",
@@ -190,15 +217,32 @@ enforces in-kernel without one.
190
217
  "mechanism": "seccomp",
191
218
  "process": { "pid": 1234, "exe": "/usr/bin/nc", "cwd": "/repo" }
192
219
  }
220
+ {
221
+ "kind": "launch",
222
+ "code": "LAUNCH_FAILED",
223
+ "program": "/usr/bin/cargo",
224
+ "errno": "ENOENT",
225
+ "message": "No such file or directory (os error 2)"
226
+ }
227
+ {
228
+ "kind": "internal",
229
+ "code": "SANDBOX_SETUP_FAILED",
230
+ "mechanism": "landlock",
231
+ "message": "not enforced by the kernel (Linux 5.13+ with CONFIG_SECURITY_LANDLOCK required, and not disabled via the lsm= boot parameter)"
232
+ }
193
233
  ```
194
234
 
195
- Traps are informational; the configured policy always applies. landstrip is
196
- otherwise quiet on success — standard error belongs to landstrip, standard
197
- output to the sandboxed tool. Usage, policy, launch, and platform errors are
198
- printed as plain text; usage errors exit with status 2. Writing to `--trap-fd`
199
- is best-effort: it needs an already-open descriptor (3 or greater; 0-2 are
200
- reserved), and if the write fails the denial is dropped while the policy stays
201
- in effect.
235
+ Denial traps are informational; the configured policy always applies. landstrip
236
+ is otherwise quiet on success — standard error belongs to landstrip, standard
237
+ output to the sandboxed tool. Failure traps are accompanied by a human-readable
238
+ log line; the JSON is what machines should read. Usage errors exit with status 2,
239
+ every other landstrip failure with 1; the tool's own status is passed through
240
+ otherwise.
241
+
242
+ Writing to `--trap-fd` is best-effort: it needs an already-open descriptor (3 or
243
+ greater; 0-2 are reserved), and if the write fails the trap is dropped while the
244
+ policy stays in effect. On Linux, a broker launch failure also reaches
245
+ `--trap-fd` while the descriptor remains open.
202
246
 
203
247
  ## Development
204
248
 
package/lib/index.d.ts ADDED
@@ -0,0 +1,151 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ /** The kernel layer an event is attributed to. */
4
+ export type LandstripMechanism =
5
+ | 'landlock'
6
+ | 'seccomp'
7
+ | 'seatbelt'
8
+ | 'appcontainer';
9
+
10
+ /**
11
+ * `query` holds the syscall until the launcher answers with a
12
+ * {@link LandstripControlResponse}; `info` is terminal. Queries need a socket on
13
+ * `--trap-fd`, and only the Linux broker raises them.
14
+ */
15
+ export type LandstripTrapState = 'query' | 'info';
16
+
17
+ /** Why a filesystem access was mediated. */
18
+ export type LandstripTrapReason = 'allow_miss' | 'deny_match';
19
+
20
+ export interface LandstripProcess {
21
+ pid: number;
22
+ exe: string | null;
23
+ cwd: string | null;
24
+ }
25
+
26
+ /** A filesystem access the policy denies. */
27
+ export interface LandstripFilesystemTrap {
28
+ kind: 'filesystem';
29
+ code: 'FILESYSTEM_DENIED';
30
+ state: LandstripTrapState;
31
+ /** Decimal `u64`; `"0"` marks a terminal `info` event. */
32
+ query_id: string;
33
+ operation: 'read' | 'write';
34
+ /** The resolved path. */
35
+ path: string;
36
+ /** The path the tool asked for, before resolution. */
37
+ requested_path: string;
38
+ syscall: string;
39
+ errno: string;
40
+ flags: string[];
41
+ reason: LandstripTrapReason;
42
+ /** The policy edit that would permit this access. */
43
+ suggested_grant: { allowRead?: string; allowWrite?: string };
44
+ process: LandstripProcess;
45
+ mechanism: 'seccomp';
46
+ }
47
+
48
+ /** A network access the policy denies. */
49
+ export interface LandstripNetworkTrap {
50
+ kind: 'network';
51
+ code: 'NETWORK_DENIED';
52
+ state: LandstripTrapState;
53
+ /** Decimal `u64`; `"0"` marks a terminal `info` event. */
54
+ query_id: string;
55
+ operation: 'connect' | 'bind';
56
+ /** `address:port`. */
57
+ target: string;
58
+ syscall: string;
59
+ errno: string;
60
+ mechanism: 'seccomp';
61
+ process: LandstripProcess;
62
+ }
63
+
64
+ /** The sandbox was installed but the tool did not start. */
65
+ export interface LandstripLaunchTrap {
66
+ kind: 'launch';
67
+ code: 'LAUNCH_FAILED';
68
+ program: string;
69
+ /** Absent where the platform has no POSIX errno for the failure. */
70
+ errno?: string;
71
+ message: string;
72
+ }
73
+
74
+ /** The command line was rejected. Reaches stderr only; landstrip exits 2. */
75
+ export interface LandstripUsageTrap {
76
+ kind: 'usage';
77
+ code: 'USAGE_ERROR';
78
+ message: string;
79
+ }
80
+
81
+ /** A policy the platform sandbox cannot enforce, rejected before launch. */
82
+ export type LandstripPolicyErrorCode =
83
+ | 'POLICY_PARSE_FAILED'
84
+ | 'POLICY_IO_FAILED'
85
+ | 'POLICY_UNRESTRICTED_READ'
86
+ | 'POLICY_TCP_BIND_UNSUPPORTED'
87
+ | 'POLICY_UNIX_SOCKET_UNSUPPORTED'
88
+ | 'POLICY_UNIX_SOCKET_PATH'
89
+ | 'POLICY_DENY_WRITE_SYMLINK_ANCESTOR'
90
+ | 'POLICY_INVALID_PORT'
91
+ | 'POLICY_EMPTY_PATH'
92
+ | 'POLICY_HOME_UNAVAILABLE'
93
+ | 'POLICY_TRAVERSAL_DEPTH';
94
+
95
+ /** The stage that failed before the tool ran. */
96
+ export type LandstripInternalCode =
97
+ | LandstripPolicyErrorCode
98
+ | 'SANDBOX_SETUP_FAILED'
99
+ | 'SUPERVISE_FAILED'
100
+ | 'PLATFORM_UNSUPPORTED'
101
+ | 'INTEGER_TOO_LARGE'
102
+ | 'INTERNAL_ERROR';
103
+
104
+ /** Everything that fails before the tool runs. */
105
+ export type LandstripInternalTrap =
106
+ | {
107
+ kind: 'internal';
108
+ code: 'SANDBOX_SETUP_FAILED';
109
+ mechanism: LandstripMechanism;
110
+ message: string;
111
+ }
112
+ | {
113
+ kind: 'internal';
114
+ code: Exclude<LandstripInternalCode, 'SANDBOX_SETUP_FAILED'>;
115
+ mechanism?: never;
116
+ message: string;
117
+ };
118
+
119
+ /**
120
+ * One landstrip event. Failure and completed-denial events are written to
121
+ * stderr; pending Linux query events are written only to `--trap-fd`.
122
+ */
123
+ export type LandstripTrap =
124
+ | LandstripFilesystemTrap
125
+ | LandstripNetworkTrap
126
+ | LandstripLaunchTrap
127
+ | LandstripUsageTrap
128
+ | LandstripInternalTrap;
129
+
130
+ /** Every code landstrip reports, across all trap kinds. */
131
+ export type LandstripCode = LandstripTrap['code'];
132
+
133
+ /**
134
+ * The answer to a `state: "query"` trap, written back to the trap socket as one
135
+ * JSON line. `query_id` is an opaque decimal `u64` copied verbatim from the
136
+ * trap. An unanswered query holds the sandboxed syscall.
137
+ */
138
+ export interface LandstripControlResponse {
139
+ query_id: string;
140
+ action: 'allow' | 'deny';
141
+ }
142
+
143
+ /**
144
+ * Path to the native landstrip binary for the running platform.
145
+ *
146
+ * @throws if the platform is unsupported, or the binary package is not installed.
147
+ */
148
+ export function binaryPath(platform?: string, arch?: string): string;
149
+
150
+ /** Name of the binary package for the given platform. */
151
+ export function packageName(platform?: string, arch?: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@landstrip/landstrip",
3
- "version": "0.16.22",
3
+ "version": "0.17.0",
4
4
  "description": "Sandbox runner using Landlock, Seatbelt, and AppContainer",
5
5
  "license": "Apache-2.0 AND LGPL-2.1-or-later",
6
6
  "homepage": "https://github.com/landstrip/landstrip#readme",
@@ -12,7 +12,13 @@
12
12
  "url": "https://github.com/landstrip/landstrip/issues"
13
13
  },
14
14
  "main": "lib/index.js",
15
- "exports": "./lib/index.js",
15
+ "types": "lib/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ }
21
+ },
16
22
  "bin": {
17
23
  "landstrip": "bin/landstrip.js"
18
24
  },
@@ -24,10 +30,10 @@
24
30
  "LICENSE-LGPL-2.1"
25
31
  ],
26
32
  "optionalDependencies": {
27
- "@landstrip/landstrip-darwin-arm64": "0.16.22",
28
- "@landstrip/landstrip-darwin-x64": "0.16.22",
29
- "@landstrip/landstrip-linux-x64": "0.16.22",
30
- "@landstrip/landstrip-win32-x64": "0.16.22"
33
+ "@landstrip/landstrip-darwin-arm64": "0.17.0",
34
+ "@landstrip/landstrip-darwin-x64": "0.17.0",
35
+ "@landstrip/landstrip-linux-x64": "0.17.0",
36
+ "@landstrip/landstrip-win32-x64": "0.17.0"
31
37
  },
32
38
  "publishConfig": {
33
39
  "access": "public"