Skip to content

feat: warn when file accessors resolve outside their search paths - #1566

Open
Moep90 wants to merge 3 commits into
kapicorp:masterfrom
Moep90:harden/path-traversal-warning
Open

feat: warn when file accessors resolve outside their search paths#1566
Moep90 wants to merge 3 commits into
kapicorp:masterfrom
Moep90:harden/path-traversal-warning

Conversation

@Moep90

@Moep90 Moep90 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

What

Log a warning when a file accessor resolves a path outside its search paths,
for example through a .. segment or a symlink. The warning is informational
only; nothing is blocked.

Why

Several accessors resolve a caller-supplied name against search paths with
os.path.join and then open whatever that produces:

  • jsonnet import resolution (search_imports)
  • the file_read, yaml_load, yaml_load_stream, file_exists,
    dir_files_list and dir_files_read jsonnet natives
  • the jinja2_render_file callback
  • input path / glob expansion in the base input type

A name like ../../../../home/user/.ssh/id_rsa resolves cleanly and the file is
read, with nothing recording that the access left the inventory. This is the
first, non-breaking step toward confining those reads: make the escape visible
before any release starts rejecting it.

Approach

  • New warn_on_path_traversal(roots, resolved_path, accessor) in utils.py. It
    compares os.path.realpath(resolved_path) against the realpath of each
    allowed root and logs a warning only when the path is contained in none of
    them. realpath means symlink escapes are caught too, not just ...
  • Wired into each resource native callback (checked against the matched search
    path) and into search_imports (checked against cwd, the kapitan install
    path, and the search paths).
  • Wired into the input path loop in base.py, checked against all search paths.

Verification

  • New tests/test_path_traversal_warning.py (5 tests): warns on escape, silent
    in-tree, silent when contained in any of several roots, and confirms
    read_file still returns the content while logging the escape (warn-only).
  • tests/test_jsonnet.py, tests/test_inputs_base.py,
    tests/test_input_compile_obj.py pass unchanged.
  • Ran the reclass compile suite with warnings surfaced: no spurious traversal
    warnings on legitimate compiles.
  • pre-commit (ruff, ruff-format) clean.

Note

Warn-only by design. A follow-up can add an enforce mode once the warning has
been out long enough to surface any legitimate out-of-tree access.

@github-actions github-actions Bot added python Pull requests that update Python code size/m labels Jun 19, 2026
@Moep90 Moep90 self-assigned this Jun 19, 2026
@github-actions github-actions Bot added the documentation Anything related to documentation label Jun 19, 2026
@Moep90
Moep90 requested review from ademariag and ramaro June 19, 2026 14:35
Moep90 added 3 commits June 19, 2026 20:54
Jsonnet's import and file_read natives, the jinja2 render callback, the yaml and
directory natives, and input glob expansion all resolve a caller-supplied name
against search paths with os.path.join. A name containing ".." or a symlink can
resolve outside the inventory and read an unrelated file such as an SSH key, and
nothing flagged it.

Add warn_on_path_traversal in utils: it compares the resolved realpath against
the allowed roots and logs a warning when the path escapes all of them. Wire it
into the resource native callbacks and the input path loop. The check is
warn-only, so the read still succeeds and no existing setup breaks; the escape
is now visible in the logs.
Add --path-traversal-mode, also settable in the .kapitan compile section, so a
resolved path escaping its search paths can warn (default), fail the compile, or
be ignored. check_path_traversal now honours the mode; workers re-apply it in
compile_target so it survives the spawn start method. Memoise realpath of the
roots since they repeat across a compile.

Document the option on the compile command reference, and cover the error, off
and invalid modes plus the search_imports multi-root wiring in tests.
MagicMock args returned a Mock for path_traversal_mode, which failed
validation in set_path_traversal_mode (expected warn/error/off). Set it
explicitly so the worker re-apply step gets a real value.
@Moep90
Moep90 force-pushed the harden/path-traversal-warning branch from edf9bfa to 705c650 Compare June 19, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Anything related to documentation python Pull requests that update Python code size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant