Enforce filter execution-context gate in infrahubctl render#957
Merged
gmazoyer merged 2 commits intoinfrahub-developfrom Apr 22, 2026
Merged
Enforce filter execution-context gate in infrahubctl render#957gmazoyer merged 2 commits intoinfrahub-developfrom
gmazoyer merged 2 commits intoinfrahub-developfrom
Conversation
render_jinja2_template in the CLI constructed a Jinja2Template and called .render() without ever calling .validate(). Today that is accidentally safe: WORKER-only filters like artifact_content fail with "requires an InfrahubClient" because infrahubctl render does not pass one. But the moment a future change threads a client through — a natural thing to do to enable iterative transform development — those filters would silently run locally, bypassing the WORKER-only gate. Add jinja_template.validate(context=ExecutionContext.LOCAL) before .render() so the context gate is enforced regardless of client state. Incidental: Jinja2Template.validate() did not handle jinja2.TemplateNotFound from env.loader.get_source() the way .render() does — a missing file-based template raised raw TemplateNotFound that escaped callers expecting JinjaTemplateError. Wrapped it in JinjaTemplateNotFoundError to match render()'s behavior and added a unit test for the case. Added a fixture template using artifact_content plus a parametrised CLI test asserting infrahubctl render rejects it with the expected violation message. Updated the invalid-filter test: missing filters are now caught at validate() time with the standard filter-gating error message rather than at render time. Fixes #955
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## infrahub-develop #957 +/- ##
====================================================
+ Coverage 81.11% 81.24% +0.13%
====================================================
Files 134 134
Lines 11314 11320 +6
Branches 1693 1693
====================================================
+ Hits 9177 9197 +20
+ Misses 1594 1581 -13
+ Partials 543 542 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 3 files with indirect coverage changes 🚀 New features to boost your workflow:
|
gmazoyer
approved these changes
Apr 22, 2026
Comment on lines
+334
to
+336
| """validate() on a non-existent file-based template must raise JinjaTemplateNotFoundError | ||
| so callers (e.g. infrahubctl render) can handle it the same way they handle it from render(). | ||
| """ |
Contributor
There was a problem hiding this comment.
The robot overdone that comment a bit. It's quite a simple test, the comment is too specific, you can remove it.
| @@ -0,0 +1 @@ | |||
| Fixed `infrahubctl render` to enforce the Jinja2 filter execution-context gate. `render_jinja2_template` in the CLI now calls `Jinja2Template.validate(context=ExecutionContext.LOCAL)` before rendering, so attempts to use WORKER-only filters like `artifact_content` are rejected with `JinjaTemplateOperationViolationError` instead of relying on the side-effect that no `InfrahubClient` is wired through. This closes the gap where a future refactor threading a client into local render would have silently bypassed the WORKER-only restriction. | |||
Contributor
There was a problem hiding this comment.
Not sure we need the changelog entry since this is still unreleased code.
The bug was introduced in the same release cycle, so a changelog record would appear as a fix for something that was never released. Also remove overly verbose test docstring per review feedback.
Deploying infrahub-sdk-python with
|
| Latest commit: |
c36311a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://5811e9eb.infrahub-sdk-python.pages.dev |
| Branch Preview URL: | https://fix-issue-955-infrahubctl-re.infrahub-sdk-python.pages.dev |
47f8f64 to
c36311a
Compare
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.
Why
infrahubctl renderconstructs aJinja2Templateand calls.render()without ever calling.validate(). Today that's accidentally safe —WORKER-only filters likeartifact_contentfail with"requires an InfrahubClient"becauseinfrahubctl renderdoesn't pass one — but the moment a future change threads a client through (a natural step for iterative local transform development), those filters would silently execute inLOCALcontext, bypassing the gate introduced in #889.Goal: close the gap so that
infrahubctl renderenforces theExecutionContext.LOCALfilter set regardless of whether a client is wired in.Non-goals: not changing which filters are
LOCAL-allowed, not adding new execution contexts, not modifying the worker or schema validation paths.Closes #955
What changed
infrahubctl renderon a template using aWORKER-only filter now surfaces"The 'artifact_content' filter isn't allowed to be used"(a proper filter-gating violation) instead of"requires an InfrahubClient". Missing filters that were previously caught at render time ("No filter named 'X'") are now caught at validate time with the standard filter-gating error.ExecutionContextimport tocli_commands.pyand ajinja_template.validate(context=ExecutionContext.LOCAL)call immediately before.render()inside the existing try/except.Jinja2Template.validate():env.loader.get_source(...)raised rawjinja2.TemplateNotFoundfor missing file-based templates, which now escapes past the CLI'sexcept JinjaTemplateErrorblock. Wrapped it inJinjaTemplateNotFoundErrorto matchrender()'s behaviour. Added a unit test for the case. Without this, the existingmain-template-not-foundCLI test would break oncevalidate()runs in the CLI path.worker-only-filter-rejected-in-local-contexttest case totest_render_app.pybacked by a newworker_only_filter.j2fixture; updated theinvalid-filtertest to expect the new validate-time error message.restricted/contextparameter API, and every other caller ofJinja2Template.validate().How to review
infrahub_sdk/ctl/cli_commands.py: two lines — one import and one.validate()call. This is the core of the PR.infrahub_sdk/template/__init__.py: a try/except aroundenv.loader.get_source(...)invalidate(). Worth a second read — it's a small bug fix in its own right (validate()should be symmetric withrender()for missing-template handling).tests/unit/ctl/test_render_app.py: updatedinvalid-filtererror string + new worker-only-filter case.tests/unit/sdk/test_template.py: one new test,test_validate_missing_file_raises_not_found.tests/fixtures/repos/missing_template_file/: newworker_only_filter.j2template and the corresponding.infrahub.ymlentry.changelog/955.fixed.md: towncrier fragment.How to test
Direct
infrahubctl renderreproductionThis test exercises the actual fix path end-to-end without needing a running Infrahub stack. Create a minimal repo on disk:
Before this PR, running
infrahubctl renderon the WORKER-only template surfaces a misleading error:After this PR, the same command surfaces the correct security-gating error:
The LOCAL-allowed template should render the same way before and after this PR (no regression on legitimate usage):
Incidental fix — missing-template handling in
validate()Verify the fix to
Jinja2Template.validate()handles missing file templates gracefully (matchesrender()):Alternative direct API verification:
End-to-end sanity with a live Infrahub stack (optional)
If you want to verify that existing artifact generation on a running Infrahub instance is unaffected (this PR only touches the
infrahubctl renderCLI path, not the worker path):artifact_contentagainst a known-good storage_id.POST /api/artifact/generate/<artifact_definition_id>.backend/infrahub/git/integrator.py:1215-1222, which already callsvalidate(context=CORE | WORKER)— unchanged by this PR.Impact & rollout
infrahubctl renderfor templates usingWORKER-only filters changes. This is an intentional clarification — the old message was misleading ("requires an InfrahubClient" implied you could fix it by passing one, which would actually bypass security). Any external tooling that asserts against the old string would need updating; unlikely to exist in practice.validate()is O(number of filter nodes) parse.Checklist
changelog/955.fixed.md)