Skip to content

An approved catalog operation's inputs are Ansible-templated on the management host #181

Closed

Description

@shipmill-romamo

Found while fixing #174 (PR #179).

The problem

An approved catalog operation's inputs reach the team's own playbook as --extra-vars JSON (sdk/src/cloudfall/decision.py:874, run_command), and Ansible templates extra-var values. An input holding {{ lookup('pipe', '...') }} is evaluated on the management host when the playbook starts, with the operator's rights.

Why it matters

The proposal is approved before it runs, so a person sees the input; but an approver reads it as a value, not as code, and an agent proposing the operation chooses it. It also breaks an honest input holding {{.

Why it isn't in #179

The inputs aren't Cloudfall config: marking them unsafe changes what the team's playbook receives (a playbook that templates an input on purpose would stop working), and the replay code in sdk/src/cloudfall/recording.py would need to read the marked form.

Related: #174, #179.

Activity

  1. shipmill-romamo commented on Oct 30, 2026

    @shipmill-romamo
    ContributorAuthor

    Triage: implement. Waits on your decision below.

    Checked on main (745c129): run_command in sdk/src/cloudfall/decision.py passes invocation.inputs as --extra-vars JSON unmarked, and Ansible templates extra-var values, the same class of bug as #174.

    The plan, once decided:

    • Mark each input value as Ansible data ({"__ansible_unsafe": ...}) in the --extra-vars JSON, the form Hand declared config strings to Ansible as data, never as templates (#174) #179 uses for the inventory
    • Have the replay in sdk/src/cloudfall/recording.py read the marked form, so recorded runs replay as before
    • A regression test running a real ansible-playbook with an input holding {{ lookup('pipe', 'touch <marker>') }}: the marker is never created and the playbook sees the literal text

    Behaviour change: a team playbook that relied on an input being evaluated as Jinja would get the literal text. I'll link the PR here.

  2. shipmill-romamo commented on Oct 30, 2026

    @shipmill-romamo
    ContributorAuthor

    @romamo Decision needed: should the inputs of an approved catalog operation reach the team's playbook as plain text, never evaluated as {{ ... }}?

    1. Plain text (recommended): an input can't run code on the management host, and an honest {{ in an input works; a team playbook that wanted an input evaluated as a template gets the literal text instead
    2. Refuse inputs holding {{ or {% when the operation is proposed: nothing changes for the playbook, but such inputs can't be given at all
    3. Leave it: the approval of the proposal is the boundary, and the approver has to spot the expression

    Reply here with a number or your own answer; shipmill takes this up on the tick after your reply.

  3. shipmill-romamo commented on Oct 31, 2026

    @shipmill-romamo
    ContributorAuthor

    Decision by @romamo, relayed by Claude Code

    1. Plain text.
  4. shipmill-romamo commented on Oct 31, 2026

    @shipmill-romamo
    ContributorAuthor

    PR: #189, on the maintainer's answer (1, plain text).

    run_command in sdk/src/cloudfall/decision.py, the one place that builds a catalog operation's --extra-vars, now marks every input string with as_ansible_data from #179, so check mode, decisions approve and agent investigate hand an input holding {{ ... }} to the team's playbook as written; integers and booleans keep their types. The replay in recording.py reads the marked argv back unmarked, so recordings made before the change still match. The decision record's inputs and its schema are unchanged.

    Behaviour change: a team playbook that relied on an input being evaluated as Jinja gets the literal text. Proof: a real ansible-playbook run with {{ lookup('pipe', 'touch <marker>') }} as an input creates the marker on main and not on the PR.

  5. shipmill-romamo commented on Oct 31, 2026

    @shipmill-romamo
    ContributorAuthor

    Released in v0.8.0rc14 (pre-release).
    A plain install gets it with the next stable release.
    If you filed this: react 👍 on this comment once it works for you, or reply if it doesn't.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions