You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(filter,cli,server): sandbox CLI Lua execution and inline scripts on publish (#194)
## Summary
- **Extract `tokf-filter` crate** with the filter engine and sandboxed
Lua execution, shared between CLI and server
- **Sandbox all Lua execution** — remove unsandboxed `run_lua_script()`;
all paths now use `run_lua_script_sandboxed()` with instruction-count
(1M) and memory (16MB) limits
- **Unify `apply()`/`apply_sandboxed()`** into shared
`apply_internal()`, eliminating ~96% code duplication
- **Auto-inline external Lua scripts on publish** — `tokf publish` reads
`lua_script.file`, embeds as inline `source`, with path traversal
protection via `canonicalize()` + `starts_with()`
- **Server-side test verification** — filters are verified against their
test suites before persisting to storage (both publish and test update
endpoints)
- **Fail-fast server validation** — rejects `lua_script.file` before
hashing/upload with a helpful hint
- **Documentation updates** — new `publishing-filters.md`, updated Lua
escape hatch docs with sandbox limits, updated writing-filters with
`[lua_script]` reference
- **File size compliance** — split `publish.rs` and `update_tests.rs`
into directory modules (tests extracted to sibling files)
## Test plan
- [x] All 1152 workspace tests pass (`cargo test --workspace`)
- [x] Clippy clean (`cargo clippy --workspace --all-targets -- -D
warnings`)
- [x] File size limits pass (all files under 700 lines)
- [ ] DB integration tests (`just test-db` — requires CockroachDB)
- [ ] End-to-end tests (`just test-e2e` — requires CockroachDB)
- [ ] Manual test: `tokf publish` with `lua_script.file` auto-inlines
correctly
- [ ] Manual test: path traversal (`lua_script.file =
"../../etc/passwd"`) is rejected
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
reason = "TestHarness::blocking_gain/blocking_list_machines - spawn_blocking test boilerplate; test clarity > DRY"
120
+
121
+
[[ignore]]
122
+
fingerprint = "ccbacfce8575cd81"
123
+
reason = "publish_filter_rejects_missing_* tests - same post+assert+check-error pattern with different payloads and error messages; test clarity > DRY"
124
+
125
+
[[ignore]]
126
+
fingerprint = "91e815e8a2c7146b"
127
+
reason = "publish_filter_rejects_oversized/failing tests - same post+assert+check-error pattern with different inputs and assertions; test clarity > DRY"
Copy file name to clipboardExpand all lines: CONTRIBUTING.md
+9-2Lines changed: 9 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -108,9 +108,16 @@ Every filter in the stdlib **must** have a `_test/` suite — CI enforces this w
108
108
109
109
## Lua filters
110
110
111
-
For filters that need logic beyond what TOML can express, use the `[lua_script]` section with [Luau](https://luau.org/). The sandbox blocks `io`, `os`, and `package` — scripts cannot access the filesystem or network.
111
+
For filters that need logic beyond what TOML can express, use the `[lua_script]` section with [Luau](https://luau.org/).
112
112
113
-
See the [README](README.md#lua-escape-hatch) for the full API and the built-in filter library for examples.
113
+
All Lua execution is sandboxed:
114
+
115
+
-**Blocked libraries:**`io`, `os`, `package` — no filesystem or network access.
116
+
-**Resource limits:** 1 million VM instructions, 16 MB memory (prevents infinite loops and memory exhaustion).
117
+
118
+
For local development, you can reference external scripts with `lua_script.file = "script.luau"`. For published filters, use inline `source` — `tokf publish` automatically inlines file references before uploading.
119
+
120
+
See `docs/lua-escape-hatch.md` for the full API, globals, and examples.
Available globals: `output` (string), `exit_code` (integer — the underlying command's real exit code, unaffected by `--no-mask-exit-code`), `args` (table).
455
461
Return a string to replace output, or `nil` to fall through to the rest of the TOML pipeline.
456
-
The sandbox blocks `io`, `os`, and `package` — no filesystem or network access from scripts.
462
+
463
+
### Sandbox
464
+
465
+
All Lua execution is sandboxed — both in the CLI and on the server:
466
+
467
+
-**Blocked libraries:**`io`, `os`, `package` — no filesystem or network access.
468
+
-**Instruction limit:** 1 million VM instructions (prevents infinite loops).
Scripts that exceed these limits are terminated and treated as a passthrough (the TOML pipeline continues as if no Lua script was configured).
472
+
473
+
### External script files
474
+
475
+
For local development you can keep the script in a separate `.luau` file:
476
+
477
+
```toml
478
+
[lua_script]
479
+
lang = "luau"
480
+
file = "transform.luau"
481
+
```
482
+
483
+
Only one of `file` or `source` may be set — not both. When you run `tokf publish`, file references are automatically inlined (the file content is embedded as `source`) so the published filter is self-contained. The script file must reside within the filter's directory — path traversal (e.g. `../secret.txt`) is rejected.
See [Publishing Filters](./publishing-filters.md) for how to share your own filters.
922
+
```sh
923
+
tokf publish <filter-name>
924
+
```
925
+
926
+
Publishes a local filter to the community registry under the MIT license. Authentication is required — run `tokf auth login` first.
927
+
928
+
### Requirements
929
+
930
+
- The filter must be a **user-level or project-local** filter (not a built-in). Use `tokf eject` first if needed.
931
+
- At least one **test file** must exist in the adjacent `_test/` directory. The server runs these tests against your filter before accepting the upload.
932
+
- You must accept the **MIT license** (prompted on first publish, remembered afterwards).
933
+
934
+
### What happens on publish
935
+
936
+
1. The filter TOML is read and validated.
937
+
2. If the filter uses `lua_script.file`, the referenced script is **automatically inlined** — its content is embedded as `lua_script.source` so the published filter is self-contained. The script file must reside within the filter's directory (path traversal is rejected).
938
+
3. A content hash is computed from the parsed config. This hash is the filter's permanent identity.
939
+
4. The filter and test files are uploaded. The server verifies tests pass before accepting.
940
+
5. On success, the registry URL is printed.
941
+
942
+
### Options
943
+
944
+
| Flag | Description |
945
+
|------|-------------|
946
+
|`--dry-run`| Preview what would be published without uploading |
947
+
|`--update-tests`| Replace the test suite for an already-published filter |
948
+
949
+
### Examples
950
+
951
+
```sh
952
+
tokf publish git/push # publish a filter
953
+
tokf publish git/push --dry-run # preview only
954
+
tokf publish --update-tests git/push # replace test suite
955
+
```
956
+
957
+
### Size limits
958
+
959
+
- Filter TOML: 64 KB max
960
+
- Total upload (filter + tests): 1 MB max
961
+
962
+
### Lua scripts in published filters
963
+
964
+
Published filters must use **inline `source`** for Lua scripts — `lua_script.file` is not supported on the server. The `tokf publish` command handles this automatically by reading the file and embedding its content. You don't need to change your filter.
965
+
966
+
All Lua scripts in published filters are executed in a sandbox with resource limits (1 million instructions, 16 MB memory) during server-side test verification.
0 commit comments