Skip to content

πŸ¦‹ Nika 0.66.0 β€” Security Stabilization

Choose a tag to compare

@github-actions github-actions released this 04 Apr 12:52

πŸ¦‹ 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: true

The 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: true

The 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 -i input error -- Improved error message with usage examples when --input flag is malformed
  • πŸ”„ nika switch -- Better version hints, automatic rebuild detection, and cleanup of stale channel files
  • πŸ”§ nika:inject path validation -- Directory traversal (../) now blocked in inject tool file paths
  • 🧹 jq_stdlib.rs deleted -- Deduplicated deep_merge and extract_field functions 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 | shell to 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: true

Everything 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