Skip to content

Security Model

How navi-bootstrap protects against malicious input, supply-chain attacks, and unintended side effects.


Threat landscape

A template engine that generates project infrastructure is a high-value target. It produces CI workflows (which run with repository secrets), pre-commit hooks (which execute on every commit), and release pipelines (which publish to package registries). A compromised template or spec could inject arbitrary code into any of these surfaces.

navi-bootstrap addresses this with four layers of defense.


Layer 1 --- Input sanitization

All user-supplied strings (project name, description, author, etc.) pass through sanitization before entering the Jinja2 render context.

Homoglyph detection. Unicode characters that visually resemble ASCII characters (e.g., Cyrillic а vs Latin a) are flagged and rejected. This prevents visual spoofing attacks where a project name appears legitimate but contains hidden Unicode.

Path traversal prevention. Spec values that would be used in file paths are checked for traversal sequences (../, ..\\, absolute paths). The engine rejects any spec value that attempts to escape the output directory.

Jinja2 injection prevention. Spec values containing Jinja2 control characters ({{, {%, {#) are escaped before rendering. This prevents a malicious spec from injecting template logic into the render pipeline.


Layer 2 --- Destination sandboxing

The engine enforces a strict output boundary. Every rendered file's destination path is resolved to an absolute path and checked against the output directory. If the resolved path falls outside the output directory --- by any mechanism --- the render is rejected.

This is enforced in engine.py at render time, not just at plan time, so even a template that dynamically constructs a path cannot escape the sandbox.


Layer 3 --- Trust model

Stages 0--3 (resolve, validate, plan, render) are pure functions with no side effects. They read specs and templates, produce rendered files in memory, and write them to disk. Nothing else.

Stages 4--5 (validation and hooks) can have side effects --- they run shell commands like uv lock or validate generated YAML. These stages are opt-in:

  • nboot render and nboot new do not run hooks by default
  • The --trust flag explicitly enables hook execution
  • nboot diff never runs hooks (it only renders to memory for comparison)

This means a user can inspect what nboot would generate (nboot diff) before committing to any side effects (nboot apply --trust).


Layer 4 --- Supply-chain hardening

Pinned action SHAs

GitHub Action references in generated workflows use commit SHAs, not tags:

# Generated by navi-bootstrap
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2

Tags are mutable --- a compromised upstream repository can force-push a tag to point to malicious code. SHAs are immutable. Stage 0 resolves tags to SHAs via the GitHub API, and the results are cached in the pack manifest for reproducibility.

Step Security Harden Runner

Generated CI workflows include Step Security Harden Runner, which restricts network and filesystem access during workflow execution.

SLSA L3 build provenance

The release-pipeline pack generates workflows that produce SLSA Level 3 build provenance --- cryptographic attestation that the published artifact was built from the claimed source commit.


Adversarial test suite

The tests/adversarial/ directory contains dedicated tests for each attack vector:

Test file Attack vector
test_path_traversal.py ../ escapes in spec values and template destinations
test_template_injection.py Jinja2 control characters in spec values
test_unicode_hostile.py Homoglyphs, RTL override, zero-width characters
test_full_pipeline.py End-to-end adversarial inputs through the full pipeline

These tests run in CI alongside the standard test suite. Any regression in security behavior fails the build.


What's not covered

  • Template authenticity. navi-bootstrap trusts its bundled packs. If a user provides a custom pack, the engine applies the same sandboxing and sanitization, but does not verify the pack's provenance. Custom packs are treated as user code.
  • Spec authenticity. The engine does not sign or verify specs. If a spec file is tampered with, the engine will render from the tampered spec. This is by design --- the spec is user-controlled configuration, not a trust boundary.
  • Runtime behavior of generated code. navi-bootstrap generates infrastructure (CI, config, templates). It does not analyze or guarantee the security of the project code that runs within that infrastructure.