Skip to content

fix(operations.files): shell-quote files.block content#10

Closed
wowi42 wants to merge 1 commit into
3.xfrom
fix/block-shell-quoting
Closed

fix(operations.files): shell-quote files.block content#10
wowi42 wants to merge 1 commit into
3.xfrom
fix/block-shell-quoting

Conversation

@wowi42
Copy link
Copy Markdown

@wowi42 wowi42 commented May 18, 2026

Summary

files.block passed the block content to awk as a positional argument wrapped in raw quote characters via string concatenation instead of proper shell quoting. Any content containing double quotes, single quotes, $(...) or backticks broke the generated command.

Reproduction (from upstream issue):

files.block(path="/root/.bashrc", content='eval "$(zoxide init --cmd cd bash)"')

The create path used a heredoc, so the first run succeeded; the second run (update existing block) failed with sh: Syntax error: "(" unexpected. The line-match path was affected the same way. Unquoted content was also a shell-injection vector.

Fix

  • Update and line-match paths now pass content through QuoteString (shlex.quote).
  • The create-path heredoc delimiter is quoted (<<'PYINFRAHERE') so the body is literal too.
  • Content is always written verbatim and is never expanded on the remote host. This also removes the previous limitation where content containing raw single quotes could break.
  • try_prevent_shell_expansion no longer affects quoting (content is always literal); it is kept and accepted for backwards compatibility and documented as deprecated.

Tests

  • 3 new YAML fixtures reproducing the issue across the update, line-match and no-file code paths with ", ' and $(...) in the content (written test-first, confirmed failing before the fix).
  • 7 pre-existing fixtures that encoded the old unsafe quoting updated to the corrected, shell-safe output.
  • Full suite passes (1737 passed); scripts/dev-lint.sh clean (ruff, format, mypy, arguments-sync).

Fixes the bug reported upstream: pyinfra-dev#1758

files.block passed the block content to awk as a positional argument
wrapped in raw quote characters via string concatenation instead of
proper shell quoting. Content containing double quotes, single quotes,
$(...) or backticks broke the generated command, e.g.

    eval "$(zoxide init --cmd cd bash)"

failed on the second run with `sh: Syntax error: "(" unexpected`. The
create path used a heredoc so the first run succeeded, masking the bug
until the block needed updating.

Content is now passed through QuoteString (shlex.quote) on the update
and line-match paths, and the create-path heredoc delimiter is quoted
(<<'PYINFRAHERE'), so content is always written verbatim and is never
expanded on the remote host. This also removes the previous limitation
where content containing raw single quotes could break.

try_prevent_shell_expansion no longer affects quoting (content is always
literal); it is kept and accepted for backwards compatibility and
documented as deprecated.

Upstream issue: pyinfra-dev#1758
@wowi42
Copy link
Copy Markdown
Author

wowi42 commented May 18, 2026

Superseded by upstream PR pyinfra-dev#1759.

@wowi42 wowi42 closed this May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant