π¦ Nika 0.66.0 β Security Stabilization
π¦ Nika 0.66.0 β Security Stabilization
Inference as Code Β· April 4, 2026 Β· 23 commits
| π§ͺ Tests | π§ Builtins | π¦ Transforms | π Providers |
|---|---|---|---|
| 9,819 | 61 | 50 | 9 |
β§ infer Β· β exec Β· β fetch Β· β invoke Β· β agent
β¨ Four security vulnerabilities patched. A 2,122-line monolith broken into six modules. And 19 transforms that were always there but nobody documented. This release is about making what exists stronger, cleaner, and more trustworthy. The kind of work that doesn't make headlines but makes the difference between a toy and a tool you can put in production.
π Security Hardening: Four Fixes, Four Stories
Each of these was found during an internal security audit. None were exploited in the wild, but each represents a class of vulnerability that matters when workflows run untrusted input.
SEC-1: The 4KB Blind Spot
The bug: The exec: verb's command blocklist only scanned the first 4,096 bytes of each command string. An attacker could bypass the blocklist by padding a command with 4KB of harmless characters before the dangerous payload.
# Bypassed the blocklist in v0.65.x:
exec:
command: "echo AAAA...(4KB of padding)...AAAA && rm -rf /"
shell: trueThe fix: Full command string scan. No length limit. Every byte is checked against the blocklist, regardless of command length.
Important
If you run nika serve with untrusted workflow submissions, upgrade immediately. This is the most impactful fix in this release.
SEC-2: Unescaped Bindings in Shell Mode
The bug: When shell: true was set, template bindings like {{with.user_input}} were interpolated directly into the shell command without escaping. A warning was emitted, but execution continued. This meant user-controlled data could inject arbitrary shell commands.
# v0.65.x: warning only, still executed
exec:
command: "echo {{with.user_input}}" # user_input = "; rm -rf /"
shell: trueThe fix: Hard NIKA-053 error. If shell: true is set and a binding isn't escaped with | shell, the task fails at analysis time -- before any shell is spawned. The error message tells you exactly what to do:
NIKA-053: Unescaped binding '{{with.user_input}}' in shell command.
Use '{{with.user_input | shell}}' to safely escape the value.
SEC-3: Secrets in Error Messages
The bug: When a command was blocked by the security checklist, the BlockedCommand error included the full command text -- including any interpolated API keys or secrets that happened to be in the command string.
The fix: All known secret patterns (API keys, tokens, passwords) are now redacted from BlockedCommand error messages before they reach logs, traces, or user-facing output. The error still tells you which command was blocked and why, but sensitive values appear as [REDACTED].
SEC-4: Unbounded File Reads
The bug: nika:read loaded files into memory without checking their size first. A workflow could reference a multi-gigabyte file and OOM the process.
The fix: Pre-read size check. Files larger than 50MB are rejected before any bytes are loaded into memory. The limit is intentionally generous -- most workflow data files are under 1MB -- but it prevents accidental (or malicious) memory exhaustion.
π Security Summary Table
| ID | Vulnerability | Severity | Fix |
|---|---|---|---|
| SEC-1 | exec blocklist limited to first 4KB | π΄ High | Full command string scan |
| SEC-2 | shell: true + unescaped bindings = injection |
π΄ High | Hard NIKA-053 error, require | shell |
| SEC-3 | API keys leaked in BlockedCommand errors | π‘ Medium | Secret pattern redaction |
| SEC-4 | nika:read loaded arbitrarily large files |
π‘ Medium | 50MB pre-read size gate |
π¦ data_tools.rs Refactor: 2,122 Lines to 6 Modules
The data_tools.rs file had become a dumping ground. Every new data builtin -- nika:map, nika:filter, nika:aggregate, nika:jq, nika:json_flatten -- landed in the same file. At 2,122 lines, it was the largest source file in the engine and growing fast.
Now it's split into six focused modules:
| Module | Responsibility | Key tools |
|---|---|---|
data/merge.rs |
Deep merge, zip, set_diff | nika:json_merge, nika:zip, nika:set_diff |
data/transform.rs |
Map, filter, group, enrich | nika:map, nika:filter, nika:group_by, nika:enrich |
data/aggregate.rs |
Statistics, counting | nika:aggregate |
data/jq.rs |
jq evaluation engine | nika:jq, nika:json_query |
data/text.rs |
Chunking, token counting | nika:chunk, nika:token_count |
data/io.rs |
JSON flatten/unflatten, validation | nika:json_flatten, nika:yaml_validate |
Each module has a single responsibility. Finding the implementation of any tool is now a 2-second grep instead of scrolling through 2,000 lines.
Tip
nika:json_query is now deprecated in favor of nika:jq. Same jaq-core engine, cleaner name. json_query still works but will be removed in a future version.
π Documentation Overhaul: The Missing 19 Transforms
Here's something embarrassing: Nika had 50 pipe transforms, but only 31 were documented. Nineteen transforms -- including starts_with, ends_with, contains, content_hash, unique_urls, all 5 URL transforms, slice, and jq() -- existed and worked perfectly but weren't in any docs, README, or CLAUDE.md rule file.
Now fixed. All 50 transforms are documented with examples. All 61 builtin tools are categorized into their proper tiers (5 always-on + 6 media-core + 13 opt-in + data + file + introspection).
The README was also rewritten from scratch: 1,292 lines down to 640. Sharper, more scannable, focused on what matters for the May launch.
β‘ jq LRU Cache: 1000x in for_each
When nika:jq runs inside a for_each loop, the same jq expression gets compiled once per iteration. For a loop with 500 items, that's 500 identical compilations.
Now the jq engine caches compiled filters in an LRU cache. The first iteration compiles the expression; the remaining 499 get a cache hit. In benchmarks, this is a 1000x speedup for for_each + nika:jq patterns.
π Fixes (5 items)
- π₯οΈ CLI
-iinput error -- Improved error message with usage examples when--inputflag is malformed - π
nika switch-- Better version hints, automatic rebuild detection, and cleanup of stale channel files - π§
nika:injectpath validation -- Directory traversal (../) now blocked in inject tool file paths - π§Ή
jq_stdlib.rsdeleted -- Deduplicateddeep_mergeandextract_fieldfunctions that were copy-pasted between modules - πΊ CI: Homebrew multi-arch + Windows -- Fixed version naming in Homebrew formula for multi-architecture builds and Windows packages
π Documentation Changes
- π 50 pipe transforms documented (was 31 -- 19 were undocumented)
- π§ 61 builtin tools categorized with proper tier assignments
- π README rewrite -- 1,292 to 640 lines, focused and launch-ready
- π GitHub templates -- Issue + PR templates added for community contributions
- π€ AGENTS.md -- Updated with v0.66 crate structure and complete tool inventory
- π― Showcase fix -- Added
| shellto serve-api bindings in showcase workflows
β¬οΈ Upgrade Notes
Warning
SEC-2 may break workflows that use shell: true with unescaped bindings. If you see NIKA-053 errors after upgrading, add | shell to the template bindings in your exec: commands:
# Before (now fails):
exec:
command: "echo {{with.data}}"
shell: true
# After (safe):
exec:
command: "echo {{with.data | shell}}"
shell: trueEverything else is backward compatible. nika:json_query still works but logs a deprecation warning -- migrate to nika:jq at your convenience.
π¦ Install
curl -fsSL https://raw.githubusercontent.com/supernovae-st/nika/main/install.sh | sh| Method | Command |
|---|---|
| πΊ Homebrew | brew install supernovae-st/tap/nika |
| π¦ npm | npx @supernovae-st/nika |
| π¦ Cargo | cargo install nika |
| π³ Docker | docker run --rm ghcr.io/supernovae-st/nika:0.66.0 |
| π» VS Code | ext install supernovae.nika-lang |
| πͺ Scoop | scoop install nika via supernovae-st/scoop-nika |
| π§ AUR | yay -S nika-bin |
π All binaries: SHA256 checksums Β· SLSA provenance Β· macOS notarization
Made with π by SuperNovae Studio Β· Open Source, AGPL-3.0
Full Changelog: v0.65.1...v0.66.0