@cocalc/openat2 0.1.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/LICENSE +21 -0
- package/README.md +135 -0
- package/index.d.ts +38 -0
- package/index.js +315 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SageMath, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# @cocalc/openat2
|
|
2
|
+
|
|
3
|
+
Linux-only `napi-rs` addon that exposes `openat2`-anchored filesystem operations for race-safe sandbox mutation.
|
|
4
|
+
|
|
5
|
+
## Why this exists
|
|
6
|
+
|
|
7
|
+
In CoCalc safe mode, we need a strong guarantee:
|
|
8
|
+
|
|
9
|
+
- filesystem operations for a project stay inside that project root
|
|
10
|
+
- even if the project owner (or another process) is changing paths concurrently
|
|
11
|
+
|
|
12
|
+
The subtle failure mode is a classic race:
|
|
13
|
+
|
|
14
|
+
1. We validate a string path (e.g. `a/b/file.txt`) and it looks safe.
|
|
15
|
+
2. Before the actual mutation syscall runs, an attacker swaps an intermediate path component (or leaf) to a symlink.
|
|
16
|
+
3. The mutation then lands outside the sandbox.
|
|
17
|
+
|
|
18
|
+
That `validate(path) -> mutate(path)` pattern is fundamentally fragile under concurrency, because validation and mutation happen at different times on a mutable namespace.
|
|
19
|
+
|
|
20
|
+
`openat2` changes the model from string-based trust to descriptor-based trust:
|
|
21
|
+
|
|
22
|
+
- we first open a **root directory handle** (`dirfd`) for the sandbox root
|
|
23
|
+
- each operation resolves relative paths under that root via `openat2`
|
|
24
|
+
- kernel-enforced resolve rules (`RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS | RESOLVE_NO_MAGICLINKS`) prevent escaping during resolution
|
|
25
|
+
- we then mutate via `*at` syscalls (`mkdirat`, `renameat`, `unlinkat`, etc.) anchored to validated dirfds
|
|
26
|
+
|
|
27
|
+
This is closer to a capability model: possession of the root `dirfd` defines the authority boundary, and every derived operation stays constrained to that boundary. In practice, this removes dependence on ad-hoc deny/allow path filtering as the primary safety mechanism.
|
|
28
|
+
|
|
29
|
+
Why not just use Node `fs` + file descriptors?
|
|
30
|
+
|
|
31
|
+
- File descriptors help for **existing-file content I/O** (`read`/`write` on an already opened inode), and we do use that pattern.
|
|
32
|
+
- But many dangerous operations are **path mutators** (`mkdir`, `rename`, `unlink`, `rmdir`, `chmod`, `utimes`, create paths) that still require pathname resolution at operation time.
|
|
33
|
+
- In plain Node, those mutators are path-based. You can pre-check with `realpath`/`lstat`, but that is still a user-space check followed by a later path syscall, so there is still a race window.
|
|
34
|
+
- For create flows, there may be no target inode yet to pin with an fd. The critical security question is whether parent-chain resolution stayed inside the sandbox at the exact syscall boundary.
|
|
35
|
+
- Node does not currently expose a complete `openat2`/`*at` capability API that lets us anchor all resolution to a sandbox dirfd with kernel-enforced constraints.
|
|
36
|
+
|
|
37
|
+
So fd-only hardening in Node is necessary but not sufficient: it meaningfully improves read/write safety, but it cannot fully eliminate TOCTOU escape classes for path-mutating operations. `openat2` + `*at` is the piece that closes that remaining gap.
|
|
38
|
+
|
|
39
|
+
Tradeoffs:
|
|
40
|
+
|
|
41
|
+
- implementation is more tedious than plain Node `fs` path calls
|
|
42
|
+
- Linux-specific (`openat2` is a Linux syscall)
|
|
43
|
+
- existing path-oriented code needs adapter layers for migration
|
|
44
|
+
|
|
45
|
+
For our situation, that tradeoff is worth it: mutators become fail-closed under symlink/path-swap races, which is exactly the remaining hardening gap in backend sandbox safe mode.
|
|
46
|
+
|
|
47
|
+
## Current API
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { SandboxRoot } from '@cocalc/openat2'
|
|
51
|
+
|
|
52
|
+
const root = new SandboxRoot('/srv/project')
|
|
53
|
+
root.mkdir('a/b', true)
|
|
54
|
+
root.rename('a/b/file.txt', 'a/b/file2.txt')
|
|
55
|
+
root.unlink('a/b/file2.txt')
|
|
56
|
+
const st = root.stat('a/b')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Methods implemented now:
|
|
60
|
+
|
|
61
|
+
- `mkdir(path, recursive?, mode?)`
|
|
62
|
+
- `unlink(path)`
|
|
63
|
+
- `rmdir(path)`
|
|
64
|
+
- `rename(oldPath, newPath)`
|
|
65
|
+
- `renameNoReplace(oldPath, newPath)`
|
|
66
|
+
- `link(oldPath, newPath)`
|
|
67
|
+
- `symlink(target, newPath)`
|
|
68
|
+
- `chmod(path, mode)`
|
|
69
|
+
- `truncate(path, len)`
|
|
70
|
+
- `copyFile(src, dest, mode?)`
|
|
71
|
+
- `rm(path, recursive?, force?)`
|
|
72
|
+
- `utimes(path, atimeNs, mtimeNs)`
|
|
73
|
+
- `stat(path)`
|
|
74
|
+
- `openRead(path) -> fd`
|
|
75
|
+
- `openWrite(path, create?, truncate?, append?, mode?) -> fd`
|
|
76
|
+
|
|
77
|
+
`openRead`/`openWrite` return numeric file descriptors intended for high-frequency
|
|
78
|
+
I/O paths in Node. The caller owns the descriptor and must close it.
|
|
79
|
+
|
|
80
|
+
## Security model
|
|
81
|
+
|
|
82
|
+
- Absolute paths are rejected.
|
|
83
|
+
- `..` traversal is rejected.
|
|
84
|
+
- Symlink traversal is blocked by `openat2` resolve flags.
|
|
85
|
+
- Operations are anchored to a root dirfd opened once in constructor.
|
|
86
|
+
|
|
87
|
+
## Build
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pnpm install
|
|
91
|
+
pnpm build
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Requirements:
|
|
95
|
+
|
|
96
|
+
- Linux kernel with `openat2` support (>=5.6)
|
|
97
|
+
- Rust toolchain
|
|
98
|
+
- Node 18+
|
|
99
|
+
|
|
100
|
+
## Test
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pnpm run test:rust
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Packaging notes
|
|
107
|
+
|
|
108
|
+
This repository is configured to publish Linux prebuilt binaries for:
|
|
109
|
+
|
|
110
|
+
- `x86_64-unknown-linux-gnu`
|
|
111
|
+
- `aarch64-unknown-linux-gnu`
|
|
112
|
+
|
|
113
|
+
Runtime loading prefers:
|
|
114
|
+
|
|
115
|
+
1. local `cocalc_openat2.linux-*.node` files (for CI/dev artifacts)
|
|
116
|
+
2. optional npm packages (`@cocalc/openat2-linux-x64-gnu` / `@cocalc/openat2-linux-arm64-gnu`)
|
|
117
|
+
3. local `cocalc_openat2.node` fallback for local development
|
|
118
|
+
|
|
119
|
+
### Release automation
|
|
120
|
+
|
|
121
|
+
The workflow in [.github/workflows/release.yml](./.github/workflows/release.yml):
|
|
122
|
+
|
|
123
|
+
1. builds both linux targets
|
|
124
|
+
2. uploads `.node` artifacts
|
|
125
|
+
3. on `v*` tags, downloads artifacts and publishes:
|
|
126
|
+
- `@cocalc/openat2-linux-x64-gnu`
|
|
127
|
+
- `@cocalc/openat2-linux-arm64-gnu`
|
|
128
|
+
- `@cocalc/openat2` (root package)
|
|
129
|
+
|
|
130
|
+
Publishing relies on the root `prepublishOnly` script:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pnpm run create-npm-dirs
|
|
134
|
+
napi prepublish -t npm --tagstyle npm --skip-gh-release
|
|
135
|
+
```
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/* auto-generated by NAPI-RS */
|
|
5
|
+
|
|
6
|
+
export interface StatResult {
|
|
7
|
+
dev: number
|
|
8
|
+
ino: number
|
|
9
|
+
mode: number
|
|
10
|
+
nlink: number
|
|
11
|
+
uid: number
|
|
12
|
+
gid: number
|
|
13
|
+
rdev: number
|
|
14
|
+
size: number
|
|
15
|
+
blksize: number
|
|
16
|
+
blocks: number
|
|
17
|
+
atimeNs: number
|
|
18
|
+
mtimeNs: number
|
|
19
|
+
ctimeNs: number
|
|
20
|
+
}
|
|
21
|
+
export declare class SandboxRoot {
|
|
22
|
+
constructor(root: string)
|
|
23
|
+
mkdir(path: string, recursive?: boolean | undefined | null, mode?: number | undefined | null): void
|
|
24
|
+
unlink(path: string): void
|
|
25
|
+
rmdir(path: string): void
|
|
26
|
+
rename(oldPath: string, newPath: string): void
|
|
27
|
+
renameNoReplace(oldPath: string, newPath: string): void
|
|
28
|
+
link(oldPath: string, newPath: string): void
|
|
29
|
+
symlink(target: string, newPath: string): void
|
|
30
|
+
chmod(path: string, mode: number): void
|
|
31
|
+
truncate(path: string, len: number): void
|
|
32
|
+
copyFile(src: string, dest: string, mode?: number | undefined | null): void
|
|
33
|
+
openRead(path: string): number
|
|
34
|
+
openWrite(path: string, create?: boolean | undefined | null, truncate?: boolean | undefined | null, append?: boolean | undefined | null, mode?: number | undefined | null): number
|
|
35
|
+
rm(path: string, recursive?: boolean | undefined | null, force?: boolean | undefined | null): void
|
|
36
|
+
utimes(path: string, atimeNs: number, mtimeNs: number): void
|
|
37
|
+
stat(path: string): StatResult
|
|
38
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/* prettier-ignore */
|
|
4
|
+
|
|
5
|
+
/* auto-generated by NAPI-RS */
|
|
6
|
+
|
|
7
|
+
const { existsSync, readFileSync } = require('fs')
|
|
8
|
+
const { join } = require('path')
|
|
9
|
+
|
|
10
|
+
const { platform, arch } = process
|
|
11
|
+
|
|
12
|
+
let nativeBinding = null
|
|
13
|
+
let localFileExisted = false
|
|
14
|
+
let loadError = null
|
|
15
|
+
|
|
16
|
+
function isMusl() {
|
|
17
|
+
// For Node 10
|
|
18
|
+
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
19
|
+
try {
|
|
20
|
+
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
|
21
|
+
return readFileSync(lddPath, 'utf8').includes('musl')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
const { glibcVersionRuntime } = process.report.getReport().header
|
|
27
|
+
return !glibcVersionRuntime
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
switch (platform) {
|
|
32
|
+
case 'android':
|
|
33
|
+
switch (arch) {
|
|
34
|
+
case 'arm64':
|
|
35
|
+
localFileExisted = existsSync(join(__dirname, 'cocalc_openat2.android-arm64.node'))
|
|
36
|
+
try {
|
|
37
|
+
if (localFileExisted) {
|
|
38
|
+
nativeBinding = require('./cocalc_openat2.android-arm64.node')
|
|
39
|
+
} else {
|
|
40
|
+
nativeBinding = require('@cocalc/openat2-android-arm64')
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
loadError = e
|
|
44
|
+
}
|
|
45
|
+
break
|
|
46
|
+
case 'arm':
|
|
47
|
+
localFileExisted = existsSync(join(__dirname, 'cocalc_openat2.android-arm-eabi.node'))
|
|
48
|
+
try {
|
|
49
|
+
if (localFileExisted) {
|
|
50
|
+
nativeBinding = require('./cocalc_openat2.android-arm-eabi.node')
|
|
51
|
+
} else {
|
|
52
|
+
nativeBinding = require('@cocalc/openat2-android-arm-eabi')
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
loadError = e
|
|
56
|
+
}
|
|
57
|
+
break
|
|
58
|
+
default:
|
|
59
|
+
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
60
|
+
}
|
|
61
|
+
break
|
|
62
|
+
case 'win32':
|
|
63
|
+
switch (arch) {
|
|
64
|
+
case 'x64':
|
|
65
|
+
localFileExisted = existsSync(
|
|
66
|
+
join(__dirname, 'cocalc_openat2.win32-x64-msvc.node')
|
|
67
|
+
)
|
|
68
|
+
try {
|
|
69
|
+
if (localFileExisted) {
|
|
70
|
+
nativeBinding = require('./cocalc_openat2.win32-x64-msvc.node')
|
|
71
|
+
} else {
|
|
72
|
+
nativeBinding = require('@cocalc/openat2-win32-x64-msvc')
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
loadError = e
|
|
76
|
+
}
|
|
77
|
+
break
|
|
78
|
+
case 'ia32':
|
|
79
|
+
localFileExisted = existsSync(
|
|
80
|
+
join(__dirname, 'cocalc_openat2.win32-ia32-msvc.node')
|
|
81
|
+
)
|
|
82
|
+
try {
|
|
83
|
+
if (localFileExisted) {
|
|
84
|
+
nativeBinding = require('./cocalc_openat2.win32-ia32-msvc.node')
|
|
85
|
+
} else {
|
|
86
|
+
nativeBinding = require('@cocalc/openat2-win32-ia32-msvc')
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
loadError = e
|
|
90
|
+
}
|
|
91
|
+
break
|
|
92
|
+
case 'arm64':
|
|
93
|
+
localFileExisted = existsSync(
|
|
94
|
+
join(__dirname, 'cocalc_openat2.win32-arm64-msvc.node')
|
|
95
|
+
)
|
|
96
|
+
try {
|
|
97
|
+
if (localFileExisted) {
|
|
98
|
+
nativeBinding = require('./cocalc_openat2.win32-arm64-msvc.node')
|
|
99
|
+
} else {
|
|
100
|
+
nativeBinding = require('@cocalc/openat2-win32-arm64-msvc')
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
loadError = e
|
|
104
|
+
}
|
|
105
|
+
break
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
case 'darwin':
|
|
111
|
+
localFileExisted = existsSync(join(__dirname, 'cocalc_openat2.darwin-universal.node'))
|
|
112
|
+
try {
|
|
113
|
+
if (localFileExisted) {
|
|
114
|
+
nativeBinding = require('./cocalc_openat2.darwin-universal.node')
|
|
115
|
+
} else {
|
|
116
|
+
nativeBinding = require('@cocalc/openat2-darwin-universal')
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
} catch {}
|
|
120
|
+
switch (arch) {
|
|
121
|
+
case 'x64':
|
|
122
|
+
localFileExisted = existsSync(join(__dirname, 'cocalc_openat2.darwin-x64.node'))
|
|
123
|
+
try {
|
|
124
|
+
if (localFileExisted) {
|
|
125
|
+
nativeBinding = require('./cocalc_openat2.darwin-x64.node')
|
|
126
|
+
} else {
|
|
127
|
+
nativeBinding = require('@cocalc/openat2-darwin-x64')
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
loadError = e
|
|
131
|
+
}
|
|
132
|
+
break
|
|
133
|
+
case 'arm64':
|
|
134
|
+
localFileExisted = existsSync(
|
|
135
|
+
join(__dirname, 'cocalc_openat2.darwin-arm64.node')
|
|
136
|
+
)
|
|
137
|
+
try {
|
|
138
|
+
if (localFileExisted) {
|
|
139
|
+
nativeBinding = require('./cocalc_openat2.darwin-arm64.node')
|
|
140
|
+
} else {
|
|
141
|
+
nativeBinding = require('@cocalc/openat2-darwin-arm64')
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
loadError = e
|
|
145
|
+
}
|
|
146
|
+
break
|
|
147
|
+
default:
|
|
148
|
+
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
|
149
|
+
}
|
|
150
|
+
break
|
|
151
|
+
case 'freebsd':
|
|
152
|
+
if (arch !== 'x64') {
|
|
153
|
+
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
|
154
|
+
}
|
|
155
|
+
localFileExisted = existsSync(join(__dirname, 'cocalc_openat2.freebsd-x64.node'))
|
|
156
|
+
try {
|
|
157
|
+
if (localFileExisted) {
|
|
158
|
+
nativeBinding = require('./cocalc_openat2.freebsd-x64.node')
|
|
159
|
+
} else {
|
|
160
|
+
nativeBinding = require('@cocalc/openat2-freebsd-x64')
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
loadError = e
|
|
164
|
+
}
|
|
165
|
+
break
|
|
166
|
+
case 'linux':
|
|
167
|
+
switch (arch) {
|
|
168
|
+
case 'x64':
|
|
169
|
+
if (isMusl()) {
|
|
170
|
+
localFileExisted = existsSync(
|
|
171
|
+
join(__dirname, 'cocalc_openat2.linux-x64-musl.node')
|
|
172
|
+
)
|
|
173
|
+
try {
|
|
174
|
+
if (localFileExisted) {
|
|
175
|
+
nativeBinding = require('./cocalc_openat2.linux-x64-musl.node')
|
|
176
|
+
} else {
|
|
177
|
+
nativeBinding = require('@cocalc/openat2-linux-x64-musl')
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
loadError = e
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
localFileExisted = existsSync(
|
|
184
|
+
join(__dirname, 'cocalc_openat2.linux-x64-gnu.node')
|
|
185
|
+
)
|
|
186
|
+
try {
|
|
187
|
+
if (localFileExisted) {
|
|
188
|
+
nativeBinding = require('./cocalc_openat2.linux-x64-gnu.node')
|
|
189
|
+
} else {
|
|
190
|
+
nativeBinding = require('@cocalc/openat2-linux-x64-gnu')
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {
|
|
193
|
+
loadError = e
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break
|
|
197
|
+
case 'arm64':
|
|
198
|
+
if (isMusl()) {
|
|
199
|
+
localFileExisted = existsSync(
|
|
200
|
+
join(__dirname, 'cocalc_openat2.linux-arm64-musl.node')
|
|
201
|
+
)
|
|
202
|
+
try {
|
|
203
|
+
if (localFileExisted) {
|
|
204
|
+
nativeBinding = require('./cocalc_openat2.linux-arm64-musl.node')
|
|
205
|
+
} else {
|
|
206
|
+
nativeBinding = require('@cocalc/openat2-linux-arm64-musl')
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
loadError = e
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
localFileExisted = existsSync(
|
|
213
|
+
join(__dirname, 'cocalc_openat2.linux-arm64-gnu.node')
|
|
214
|
+
)
|
|
215
|
+
try {
|
|
216
|
+
if (localFileExisted) {
|
|
217
|
+
nativeBinding = require('./cocalc_openat2.linux-arm64-gnu.node')
|
|
218
|
+
} else {
|
|
219
|
+
nativeBinding = require('@cocalc/openat2-linux-arm64-gnu')
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
loadError = e
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
break
|
|
226
|
+
case 'arm':
|
|
227
|
+
if (isMusl()) {
|
|
228
|
+
localFileExisted = existsSync(
|
|
229
|
+
join(__dirname, 'cocalc_openat2.linux-arm-musleabihf.node')
|
|
230
|
+
)
|
|
231
|
+
try {
|
|
232
|
+
if (localFileExisted) {
|
|
233
|
+
nativeBinding = require('./cocalc_openat2.linux-arm-musleabihf.node')
|
|
234
|
+
} else {
|
|
235
|
+
nativeBinding = require('@cocalc/openat2-linux-arm-musleabihf')
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
loadError = e
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
localFileExisted = existsSync(
|
|
242
|
+
join(__dirname, 'cocalc_openat2.linux-arm-gnueabihf.node')
|
|
243
|
+
)
|
|
244
|
+
try {
|
|
245
|
+
if (localFileExisted) {
|
|
246
|
+
nativeBinding = require('./cocalc_openat2.linux-arm-gnueabihf.node')
|
|
247
|
+
} else {
|
|
248
|
+
nativeBinding = require('@cocalc/openat2-linux-arm-gnueabihf')
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
loadError = e
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
break
|
|
255
|
+
case 'riscv64':
|
|
256
|
+
if (isMusl()) {
|
|
257
|
+
localFileExisted = existsSync(
|
|
258
|
+
join(__dirname, 'cocalc_openat2.linux-riscv64-musl.node')
|
|
259
|
+
)
|
|
260
|
+
try {
|
|
261
|
+
if (localFileExisted) {
|
|
262
|
+
nativeBinding = require('./cocalc_openat2.linux-riscv64-musl.node')
|
|
263
|
+
} else {
|
|
264
|
+
nativeBinding = require('@cocalc/openat2-linux-riscv64-musl')
|
|
265
|
+
}
|
|
266
|
+
} catch (e) {
|
|
267
|
+
loadError = e
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
localFileExisted = existsSync(
|
|
271
|
+
join(__dirname, 'cocalc_openat2.linux-riscv64-gnu.node')
|
|
272
|
+
)
|
|
273
|
+
try {
|
|
274
|
+
if (localFileExisted) {
|
|
275
|
+
nativeBinding = require('./cocalc_openat2.linux-riscv64-gnu.node')
|
|
276
|
+
} else {
|
|
277
|
+
nativeBinding = require('@cocalc/openat2-linux-riscv64-gnu')
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
loadError = e
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
break
|
|
284
|
+
case 's390x':
|
|
285
|
+
localFileExisted = existsSync(
|
|
286
|
+
join(__dirname, 'cocalc_openat2.linux-s390x-gnu.node')
|
|
287
|
+
)
|
|
288
|
+
try {
|
|
289
|
+
if (localFileExisted) {
|
|
290
|
+
nativeBinding = require('./cocalc_openat2.linux-s390x-gnu.node')
|
|
291
|
+
} else {
|
|
292
|
+
nativeBinding = require('@cocalc/openat2-linux-s390x-gnu')
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
loadError = e
|
|
296
|
+
}
|
|
297
|
+
break
|
|
298
|
+
default:
|
|
299
|
+
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
|
300
|
+
}
|
|
301
|
+
break
|
|
302
|
+
default:
|
|
303
|
+
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!nativeBinding) {
|
|
307
|
+
if (loadError) {
|
|
308
|
+
throw loadError
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`Failed to load native binding`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const { SandboxRoot } = nativeBinding
|
|
314
|
+
|
|
315
|
+
module.exports.SandboxRoot = SandboxRoot
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cocalc/openat2",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Linux openat2-based filesystem primitives for secure sandboxing",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"index.d.ts",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"artifacts": "napi artifacts -d artifacts",
|
|
17
|
+
"build": "napi build --platform --release",
|
|
18
|
+
"build:debug": "napi build --platform",
|
|
19
|
+
"build:platform": "napi build --platform --release",
|
|
20
|
+
"build:target": "napi build --platform --release --target",
|
|
21
|
+
"create-npm-dirs": "napi create-npm-dir -t .",
|
|
22
|
+
"prepublishOnly": "pnpm run create-npm-dirs && napi prepublish -t npm --tagstyle npm --skip-gh-release",
|
|
23
|
+
"test:node": "node --test test/*.test.cjs",
|
|
24
|
+
"test:rust": "cargo test",
|
|
25
|
+
"test": "pnpm run test:rust && pnpm run test:node",
|
|
26
|
+
"lint:rust": "cargo clippy --all-targets --all-features -- -D warnings",
|
|
27
|
+
"version": "napi version"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@napi-rs/cli": "^2.18.4"
|
|
34
|
+
},
|
|
35
|
+
"napi": {
|
|
36
|
+
"name": "cocalc_openat2",
|
|
37
|
+
"triples": {
|
|
38
|
+
"defaults": false,
|
|
39
|
+
"additional": [
|
|
40
|
+
"x86_64-unknown-linux-gnu",
|
|
41
|
+
"aarch64-unknown-linux-gnu"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"optionalDependencies": {
|
|
46
|
+
"@cocalc/openat2-linux-x64-gnu": "0.1.0",
|
|
47
|
+
"@cocalc/openat2-linux-arm64-gnu": "0.1.0"
|
|
48
|
+
}
|
|
49
|
+
}
|