@landstrip/landstrip-api 0.18.43 → 0.19.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 +30 -45
- package/lib/index.d.ts +3 -2
- package/lib/proxy.js +9 -2
- package/lib/shared.js +8 -8
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,64 +1,49 @@
|
|
|
1
|
-
# landstrip
|
|
1
|
+
# @landstrip/landstrip-api
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Node.js wrapper, native binaries, and TypeScript types for the Landstrip sandbox.
|
|
4
|
+
Requires Node.js 18 or newer; binaries are provided for Linux, macOS, and Windows
|
|
5
|
+
on x64 and arm64.
|
|
5
6
|
|
|
6
|
-
## Install
|
|
7
|
+
## Install and run
|
|
7
8
|
|
|
8
9
|
```sh
|
|
9
10
|
npm install --save-dev @landstrip/landstrip-api
|
|
10
11
|
npx landstrip run -p policy.json -- cargo test
|
|
11
12
|
```
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
See the [quick start](../../README.md#quick-start) for an example policy and
|
|
15
|
+
[landstrip(1)](../landstrip/man/man1/landstrip.1) for CLI options, policy rules,
|
|
16
|
+
and platform limits.
|
|
15
17
|
|
|
16
|
-
##
|
|
18
|
+
## Node.js API
|
|
17
19
|
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
landstrip policy resolve -p policy.json
|
|
22
|
-
landstrip doctor
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Run `landstrip --help` or see [landstrip(1)](../landstrip/man/man1/landstrip.1) for the full
|
|
26
|
-
command reference.
|
|
27
|
-
|
|
28
|
-
## Agent extensions
|
|
20
|
+
```js
|
|
21
|
+
const { execFileSync } = require('node:child_process');
|
|
22
|
+
const { binaryPath } = require('@landstrip/landstrip-api');
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
execFileSync(binaryPath(), ['run', '-p', 'policy.json', '--', 'cargo', 'test'], {
|
|
25
|
+
stdio: 'inherit',
|
|
26
|
+
});
|
|
27
|
+
```
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
`binaryPath()` returns the installed native binary's path and throws if the
|
|
30
|
+
platform is unsupported or its binary package is missing. `packageName()` returns
|
|
31
|
+
the platform's binary package name. Both accept optional `platform` and `arch`
|
|
32
|
+
arguments, defaulting to the current process.
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"denyWrite": ["**/.env", "**/*.pem"],
|
|
42
|
-
"denyRead": ["~/.ssh"],
|
|
43
|
-
"allowRead": ["~/.ssh/config"]
|
|
44
|
-
},
|
|
45
|
-
"network": {
|
|
46
|
-
"allowNetwork": false,
|
|
47
|
-
"allowLocalBinding": false
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
```
|
|
34
|
+
The package exports [`LandstripTrap` and `LandstripControlResponse`](lib/index.d.ts)
|
|
35
|
+
for structured events and Linux broker replies. Runtime traps and approval
|
|
36
|
+
queries are Linux-only; macOS's `--trap` reports setup and launch failures.
|
|
37
|
+
Descriptor lifetimes and the reply protocol are covered in the manual.
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
Integration helpers are exported through [`/shared`](lib/shared.d.ts) and
|
|
40
|
+
[`/proxy`](lib/proxy.d.ts).
|
|
54
41
|
|
|
55
42
|
## Development
|
|
56
43
|
|
|
57
|
-
|
|
58
|
-
make ci
|
|
59
|
-
```
|
|
44
|
+
Run `make ci` from the repository root.
|
|
60
45
|
|
|
61
|
-
##
|
|
46
|
+
## License
|
|
62
47
|
|
|
63
|
-
|
|
64
|
-
[
|
|
48
|
+
[Apache-2.0](LICENSE). The bundled native binary is
|
|
49
|
+
[LGPL-3.0-or-later](../landstrip/LICENSE).
|
package/lib/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type LandstripMechanism =
|
|
|
10
10
|
/**
|
|
11
11
|
* `query` holds the syscall until the launcher answers with a
|
|
12
12
|
* {@link LandstripControlResponse}; `info` is terminal. Queries need a socket on
|
|
13
|
-
* `--trap
|
|
13
|
+
* `--trap`, and only the Linux broker raises them.
|
|
14
14
|
*/
|
|
15
15
|
export type LandstripTrapState = 'query' | 'info';
|
|
16
16
|
|
|
@@ -83,6 +83,7 @@ export type LandstripPolicyErrorCode =
|
|
|
83
83
|
| 'POLICY_PARSE_FAILED'
|
|
84
84
|
| 'POLICY_IO_FAILED'
|
|
85
85
|
| 'POLICY_UNRESTRICTED_READ'
|
|
86
|
+
| 'POLICY_DENY_READ_ALWAYS_UNSUPPORTED'
|
|
86
87
|
| 'POLICY_TCP_BIND_UNSUPPORTED'
|
|
87
88
|
| 'POLICY_UNIX_SOCKET_UNSUPPORTED'
|
|
88
89
|
| 'POLICY_UNIX_SOCKET_PATH'
|
|
@@ -118,7 +119,7 @@ export type LandstripInternalTrap =
|
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
121
|
* One landstrip event. Failure and completed-denial events are written to
|
|
121
|
-
* stderr; pending Linux query events are written only to `--trap
|
|
122
|
+
* stderr; pending Linux query events are written only to `--trap`.
|
|
122
123
|
*/
|
|
123
124
|
export type LandstripTrap =
|
|
124
125
|
| LandstripFilesystemTrap
|
package/lib/proxy.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const { lookup } = require('node:dns/promises');
|
|
5
5
|
const { Agent, createServer: createHttpServer, request: requestHttp } = require('node:http');
|
|
6
6
|
const { connect, isIP } = require('node:net');
|
|
7
|
+
const { pipeline } = require('node:stream');
|
|
7
8
|
|
|
8
9
|
const { canonicalizeHost } = require('./shared');
|
|
9
10
|
|
|
@@ -262,7 +263,9 @@ function startFilterProxy(options) {
|
|
|
262
263
|
upstreamResponse.statusMessage,
|
|
263
264
|
stripHopByHopHeaders(upstreamResponse.headers),
|
|
264
265
|
);
|
|
265
|
-
upstreamResponse
|
|
266
|
+
pipeline(upstreamResponse, clientResponse, (error) => {
|
|
267
|
+
if (error) upstreamRequest.destroy();
|
|
268
|
+
});
|
|
266
269
|
});
|
|
267
270
|
upstreamRequest.once('error', () => {
|
|
268
271
|
if (!clientResponse.headersSent) {
|
|
@@ -272,8 +275,12 @@ function startFilterProxy(options) {
|
|
|
272
275
|
}
|
|
273
276
|
});
|
|
274
277
|
clientRequest.once('aborted', () => upstreamRequest.destroy());
|
|
278
|
+
clientRequest.once('error', () => upstreamRequest.destroy());
|
|
279
|
+
clientResponse.once('error', () => upstreamRequest.destroy());
|
|
275
280
|
clientResponse.once('close', () => {
|
|
276
|
-
|
|
281
|
+
clientRequest.unpipe(upstreamRequest);
|
|
282
|
+
upstreamRequest.destroy();
|
|
283
|
+
agent.destroy();
|
|
277
284
|
});
|
|
278
285
|
clientRequest.pipe(upstreamRequest);
|
|
279
286
|
}
|
package/lib/shared.js
CHANGED
|
@@ -68,22 +68,22 @@ function sessionScopeFor(filePath, baseDirectory) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function canonicalizeHost(host) {
|
|
71
|
+
if (/[\s\p{Cc}/\\@?#]/u.test(host)) return null;
|
|
72
|
+
|
|
71
73
|
const bracketed = host.startsWith('[');
|
|
72
74
|
if (bracketed !== host.endsWith(']')) return null;
|
|
73
|
-
|
|
74
|
-
// A trailing dot ("pastebin.com.") is the same host to DNS but would slip
|
|
75
|
-
// past a literal deny entry; strip a single trailing dot and reject the rest.
|
|
76
|
-
const value = bracketed ? host.slice(1, -1) : host.replace(/\.$/, '');
|
|
77
|
-
if (!value || value.endsWith('.')) return null;
|
|
75
|
+
const value = bracketed ? host.slice(1, -1) : host;
|
|
78
76
|
|
|
79
77
|
if (ipaddr.isValid(value)) {
|
|
80
78
|
if (bracketed && ipaddr.parse(value).kind() !== 'ipv6') return null;
|
|
81
79
|
return ipaddr.process(value).toString();
|
|
82
80
|
}
|
|
83
|
-
if (bracketed) return null;
|
|
81
|
+
if (bracketed || /[:%]/.test(value)) return null;
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
// A trailing dot ("pastebin.com.") is the same host to DNS but would slip
|
|
84
|
+
// past a literal deny entry; strip a single trailing dot and reject the rest.
|
|
85
|
+
const ascii = domainToASCII(value).replace(/\.$/, '');
|
|
86
|
+
if (!ascii || ascii.endsWith('.')) return null;
|
|
87
87
|
|
|
88
88
|
try {
|
|
89
89
|
const parsed = new URL(`http://${ascii}/`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@landstrip/landstrip-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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.19.0",
|
|
44
|
+
"@landstrip/landstrip-darwin-x64": "0.19.0",
|
|
45
|
+
"@landstrip/landstrip-linux-x64": "0.19.0",
|
|
46
|
+
"@landstrip/landstrip-linux-arm64": "0.19.0",
|
|
47
|
+
"@landstrip/landstrip-win32-x64": "0.19.0",
|
|
48
|
+
"@landstrip/landstrip-win32-arm64": "0.19.0"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|