Repository navigation
Hand catalog inputs to the team's playbook as data, never as templates (#181) - #189
Merged
Merged
Conversation
#181) An approved operation's inputs reached ansible-playbook as plain --extra-vars JSON, and Ansible templates extra vars: an input holding {{ lookup('pipe', ...) }} ran on the management host with the operator's rights, and an honest input holding {{ broke the run. run_command now marks every input string unsafe with as_ansible_data, as #179 did for the rendered inventory; integers and booleans keep their types. The replay reads the argv back with object_hook=unmarked, so its key matches the unmarked inputs a recording keeps, from before this change and after. The decision record's inputs are unchanged. Fixes #181 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
An approved catalog operation's inputs reached the team's own playbook as plain
--extra-varsJSON (run_commandinsdk/src/cloudfall/decision.py), and Ansible templates extra-var values: an input holding{{ lookup('pipe', 'touch /tmp/x') }}ran on the management host with the operator's rights when the playbook started, and an honest input holding{{broke the run.run_commandmarks every input string unsafe withcloudfall.ansible_data.as_ansible_data(the helper Hand declared config strings to Ansible as data, never as templates (#174) #179 added for the rendered inventory), so the playbook gets the text as written; integers and booleans keep their types. This is the one place that builds the inputs'--extra-vars, used byoperations propose(check mode),decisions approve(run and verify) andagent investigate, which proposes through itrecording._invocation) reads the argv back withobject_hook=unmarked, so its key matches the unmarked inputs a recording keeps. Recordings store the decision record'sspec.inputs, which stay unmarked, so a recording made before this change and one made after match the same live invocationinputs, its schema, and the D-1/D-7 proposal matching (which compares the stored inputs) are unchanged; only the argv form changesBehaviour change: a team playbook that relied on an input being evaluated as Jinja now gets the literal text.
Plan
sdk/src/cloudfall/decision.py,sdk/src/cloudfall/recording.py,sdk/tests/test_decision.py,sdk/tests/test_host_stats.py,changelog.d/181-catalog-inputs-as-data.mdansible-playbookrun of a proposal whose string input holds{{ lookup('pipe', 'touch <marker>') }}, plus integer and boolean inputs), then mark the inputs withas_ansible_datainrun_command, then read them back withobject_hook=unmarkedin the replay's_invocationtest_a_replay_leaves_what_the_callback_would_have_written) keeps matching a recording; a new replay test matches a recording against a live invocation with a{{inputTest plan
test_inputs_reach_the_playbook_as_data_never_as_templates(new,sdk/tests/test_decision.py): runs a realansible-playbookthroughpropose; fails on main (marker created), passes with the fix; the play sees the literal payload,3as an integer andtrueas a booleantest_a_replay_matches_inputs_holding_jinja_as_recorded(new,sdk/tests/test_host_stats.py) and the existing replay test both fail when theobject_hook=unmarkedread is removed, and pass with ituv run ruff check .,uv run mypy: clean.uv run ruff format --check .reports 72 files on main already (not a CI step); the lines this PR adds are formatteduv run pytest sdk/tests/test_decision.py sdk/tests/test_host_stats.py sdk/tests/test_investigation.py sdk/tests/test_operator.py: 194 passeduv run pytest engine/tests: 319 passed, 1 skippedFixes #181
🤖 Generated with Claude Code