Reference

Security Model

Validated structure, quoted path substitutions, a documented recipe-shell contract, and an auditable network policy for the fetch() helper. A Netsukefile deserves the same care as a Makefile: Netsuke reduces common quoting mistakes, but it is not a sandbox.

Network Policy

The fetch() standard-library function downloads content from URLs at template evaluation time. Because this happens before a build runs, Netsuke provides configurable network-policy controls so that teams can audit and restrict what remote resources a manifest is allowed to contact.

--fetch-allow-scheme <SCHEME>

Allow additional URL schemes beyond the defaults. Repeat the flag to allow multiple schemes.

--fetch-allow-host <HOST>

Allow the provided hostname when default-deny mode is active. Wildcards such as *.example.com are supported.

--fetch-block-host <HOST>

Always block the provided hostname, even if it appears on the allowlist. Wildcards are supported.

--fetch-default-deny

Deny all hosts by default. Only hosts explicitly allowed by --fetch-allow-host may be contacted.

Restricting fetch to a single host
$ netsuke \
  --fetch-default-deny \
  --fetch-allow-host releases.example.com \
  --fetch-block-host internal.example.com \
  build

Recipe shells

Recipes are shell strings in v0.1.x. Which interpreter runs them is a fixed contract per platform, not a property of the shell that launched netsuke. Structured executable arguments (RFC 0001) are planned for v0.2.0 and do not change the contract below.

Unix: /bin/sh -e

Each command list entry runs in its own brace group, joined with &&, and is passed to eval as a shell-quoted payload so a trailing # or & cannot break the chain. Groups run in the current shell, so a cd or variable assignment carries to later entries. An entry may start at most one background job; structured or nested exec forms are rejected during Ninja generation.

Windows: PowerShell by default

Every legacy recipe is handed to Windows PowerShell (powershell.exe, not pwsh) as an encoded, non-interactive, no-profile command. A command list shares one process and is checked with $LASTEXITCODE after every entry. Use PowerShell syntax: $name is a variable and $env:NAME reads the environment. {{ ins }} and {{ outs }} render as single-quoted PowerShell arguments. Recipes over Windows' command-line limit and up to 1 MiB are routed through a self-deleting .ps1 response file.

Windows: explicit Bash route

Set NETSUKE_WINDOWS_SHELL=bash to keep POSIX interpretation through Git Bash or MSYS2 (bash.exe on PATH). Netsuke verifies bash.exe --version before build or Ninja-tool execution and stops with install instructions otherwise. In CI, install Git explicitly, prepend its bin directory to PATH, and launch Netsuke from a pwsh step rather than relying on a workflow-wide shell: bash.

Keeping POSIX recipes on Windows
choco install git --yes --no-progress
$env:PATH = "C:\Program Files\Git\bin;$env:PATH"
$env:NETSUKE_WINDOWS_SHELL = "bash"
netsuke build

Markers and shell dollars

{{ ins }} and {{ outs }} are the only Netsuke markers for input and output paths, and the only values that are automatically shell-quoted. Everything else a template renders is ordinary command text. Since v0.1.0-beta3, shell dollar expressions are written normally: Netsuke lowers the markers first and only then escapes literal dollars as $$ in the generated Ninja file, so the shell receives them unchanged.

Write this

targets:
  - name: build/app
    sources: src/main.c
    command: >-
      ${CC:-cc} $CFLAGS {{ ins }} -o {{ outs }}
      && echo "built on $HOSTNAME"

$CC, $CFLAGS, and $HOSTNAME reach /bin/sh as literal shell variables. On the PowerShell route, write $env:CC instead; PowerShell does not perform POSIX ${VAR:-default} expansion.

Migrating a beta2 manifest

  • Replace the historical spelling $$PATH with $PATH. On POSIX and Bash routes $$ is the shell's process identifier; in PowerShell it is the last token of the previous command, so the extra dollar now changes the result.
  • Replace any $in or $out placeholder with {{ ins }} or {{ outs }}. Literal $ins and $outs remain ordinary shell variables.
  • On POSIX and Bash routes a marker may appear unquoted, single-quoted, or double-quoted, but not inside a command substitution or backticks. In PowerShell, use markers unquoted; quoted and command-substitution sites are rejected.
  • Build and default-target paths reject $, spaces, colons, |, and control characters; emitted metadata such as descriptions rejects newlines and NUL.

Security Model

Safe by Default

Netsuke validates every manifest against a schema before template expansion runs, quotes the path markers it owns, and rejects the characters Ninja cannot represent unambiguously. Diagnostics are typed, so validation failures point to the exact manifest location rather than surfacing as opaque errors.

YAML first

The manifest must be valid YAML before any template expansion happens. Structural mistakes fail early, before commands are generated.

Quoted path markers

{{ ins }} and {{ outs }} are quoted as path arguments for the selected recipe shell. Other rendered Jinja values are not automatically shell-quoted, and the shell_escape filter described in older drafts is not implemented in v0.1.0-beta3: handwritten shell fragments remain the author's responsibility.

Typed diagnostics

CLI errors are designed to carry context, hints, and localized output rather than dropping raw parser failures on the floor.

Scoped glob authority

Since v0.1.0-beta2, the filesystem capability used for glob() metadata checks is opened at the pattern's longest literal directory prefix — not at the filesystem root or the working directory — so glob expansion holds only the authority its pattern can reach. Since v0.1.0-beta3, the Jinja helper also rejects any matched path that is not a portable unquoted shell word, so an untrusted checkout filename cannot become shell syntax when item is interpolated into a recipe.

A pattern whose literal prefix is missing, or names something other than a directory, expands to no matches. A match reached through a symbolic link that resolves outside the prefix, or dangles, is skipped rather than failing the expansion; a cyclic symbolic link still fails it, and a literal prefix that is itself a symbolic link cannot establish the capability, so the expansion fails. Glob tracing redacts caller-controlled path fields as <redacted>.

Vulnerability Mitigation

Qualitative comparison based on documented safety features; no numeric mitigation claim is implied.

Manifest schema validation, quoted path markers with shell-safe glob results, and typed diagnostics give Netsuke stronger qualitative mitigation than ad hoc scripts on each documented boundary. Interactive charting requires JavaScript.