Skip to content

Commit 363b92f

Browse files
koki-developclaude
andcommitted
feat: add package-lock.json to node-typescript restricted files and use npm ci
Add package-lock.json as a default embedded file and restrict user submission to ensure consistency with the pre-installed node_modules. Switch Dockerfile from npm install to npm ci for deterministic builds. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 604f656 commit 363b92f

6 files changed

Lines changed: 85 additions & 13 deletions

File tree

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ RUN apt-get update && \
3030
# Node.js
3131
ENV PATH="/mise/installs/node/24.14.0/bin:$PATH"
3232
RUN mise use -g node@24.14.0
33-
COPY internal/sandbox/defaults/node-typescript/package.json /mise/ts-node-modules/package.json
34-
RUN cd /mise/ts-node-modules && npm install
33+
COPY internal/sandbox/defaults/node-typescript/package.json internal/sandbox/defaults/node-typescript/package-lock.json /mise/ts-node-modules/
34+
RUN cd /mise/ts-node-modules && npm ci
3535

3636
# Ruby
3737
ENV PATH="/mise/installs/ruby/3.4.8/bin:$PATH"

e2e/tests/runtime/node-typescript.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,29 @@ tests:
263263
- path: ["files", 1, "name"]
264264
message: "not allowed for this runtime"
265265

266+
- name: "restricted file - package-lock.json"
267+
requests:
268+
- input:
269+
runtime: node-typescript
270+
files:
271+
- name: index.ts
272+
type: plain
273+
content: |
274+
console.log("hello");
275+
- name: package-lock.json
276+
type: plain
277+
content: |
278+
{}
279+
output:
280+
status: 400
281+
body:
282+
error:
283+
code: VALIDATION_ERROR
284+
message: "request validation failed"
285+
errors:
286+
- path: ["files", 1, "name"]
287+
message: "not allowed for this runtime"
288+
266289
- name: "interfaces and generics"
267290
requests:
268291
- input:

internal/sandbox/CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ Core sandbox execution engine, split across three files:
88
- **configs/nsjail.cfg** — Static protobuf-format nsjail configuration loaded via `-C /etc/nsjail/nsjail.cfg`. Defines execution mode (`ONCE`), Seccomp-BPF policy file reference, logging (`log_fd: 3`), working directory, static rlimits, and filesystem mounts (system libraries, device nodes, tmpfs, procfs, symlinks). Per-invocation settings (runtime bind mounts, resource limits, timeout, env vars) are passed as CLI flags.
99
- **configs/seccomp.kafel** — Seccomp-BPF syscall filtering policy written in Kafel. Uses a blacklist approach (DEFAULT ALLOW) blocking dangerous syscalls (io_uring, bpf, userfaultfd, mount, ptrace, etc.) as a defense-in-depth layer. Referenced from nsjail.cfg via `seccomp_policy_file` and copied to `/etc/nsjail/seccomp.kafel` in Docker.
1010
- **defaults/go/** — Embedded `go.mod.tmpl` and `go.sum.tmpl` templates applied as default files for Go runtime execution.
11+
- **defaults/node-typescript/** — Embedded `package.json`, `package-lock.json`, and `tsconfig.json` applied as default files for Node-TypeScript runtime execution.
1112

1213
Go runtime rejects user-submitted `go.mod`, `go.sum`, and `main` files (HTTP 400) to enforce use of defaults and prevent overwriting the compiled binary.
14+
15+
Node-TypeScript runtime rejects user-submitted `package.json` and `package-lock.json` files (HTTP 400) to ensure consistency with the pre-installed `node_modules` bind mount.

internal/sandbox/defaults/node-typescript/package-lock.json

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/sandbox/runtime.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -630,10 +630,11 @@ func (nodeTypeScriptRuntime) Limits() Limits {
630630
}
631631
}
632632

633-
// RestrictedFiles prevents users from overriding package.json, which must
634-
// match the pre-installed node_modules bind mount (contains typescript and @types/node).
633+
// RestrictedFiles prevents users from overriding package.json and
634+
// package-lock.json, which must match the pre-installed node_modules
635+
// bind mount (contains typescript and @types/node).
635636
func (nodeTypeScriptRuntime) RestrictedFiles() []string {
636-
return []string{"package.json"}
637+
return []string{"package.json", "package-lock.json"}
637638
}
638639

639640
// --- Bash ---

internal/sandbox/sandbox_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,16 +291,17 @@ func Test_readDefaultFiles(t *testing.T) {
291291
assert.Empty(t, files)
292292
})
293293

294-
t.Run("node-typescript has tsconfig.json and package.json", func(t *testing.T) {
294+
t.Run("node-typescript has package.json, package-lock.json, and tsconfig.json", func(t *testing.T) {
295295
t.Parallel()
296296
files, err := readDefaultFiles(RuntimeNodeTypeScript)
297297
require.NoError(t, err)
298-
require.Len(t, files, 2)
298+
require.Len(t, files, 3)
299299
// Files are returned in directory order (lexicographic by embedded FS)
300-
assert.Equal(t, "package.json", files[0].Name)
301-
assert.Contains(t, string(files[0].Content), `"@types/node"`)
302-
assert.Equal(t, "tsconfig.json", files[1].Name)
303-
assert.Contains(t, string(files[1].Content), `"skipLibCheck": true`)
300+
assert.Equal(t, "package-lock.json", files[0].Name)
301+
assert.Equal(t, "package.json", files[1].Name)
302+
assert.Contains(t, string(files[1].Content), `"@types/node"`)
303+
assert.Equal(t, "tsconfig.json", files[2].Name)
304+
assert.Contains(t, string(files[2].Content), `"skipLibCheck": true`)
304305
})
305306

306307
t.Run("unknown runtime has no defaults", func(t *testing.T) {
@@ -394,10 +395,10 @@ func TestRuntime_RestrictedFiles(t *testing.T) {
394395
assert.ElementsMatch(t, []string{"main"}, rt.RestrictedFiles())
395396
})
396397

397-
t.Run("node-typescript restricts package.json", func(t *testing.T) {
398+
t.Run("node-typescript restricts package.json and package-lock.json", func(t *testing.T) {
398399
t.Parallel()
399400
rt, err := LookupRuntime(RuntimeNodeTypeScript)
400401
require.NoError(t, err)
401-
assert.ElementsMatch(t, []string{"package.json"}, rt.RestrictedFiles())
402+
assert.ElementsMatch(t, []string{"package.json", "package-lock.json"}, rt.RestrictedFiles())
402403
})
403404
}

0 commit comments

Comments
 (0)