Repository navigation
feat: count template filters as calls to the functions behind them - #173
Merged
Merged
Conversation
A filter of Django's own libraries that a project template applies runs the function behind it whenever the template is rendered. The tests specify that it counts as a reachable call at the template's line, in variables, tag arguments and filter blocks, and that a template the engine cannot read keeps an advisory a template filter can reach under investigation in the VEX report.
Project templates are read for the filters they apply, each mapped to the function Django registers it from, and reachable-vulnerability reports an entry point a template filter reaches at the template's line, whether or not a Python call names the template. The escaping judgement reads its filter names from the same table. Where the project names a template the engine cannot read (by an expression, or by a name found under no templates directory, in a render call, an include or an extends), coverage lists it, and the VEX report keeps an advisory whose entry points include a template filter under investigation. render, TemplateResponse and SimpleTemplateResponse are template renders like render_to_string.
{% filter striptags %} has no | before its filter; it is the usual form of a filter block.
A {% filter %} tag names its first filter without a | before it, so {% filter striptags %} applied no filter. The tag's argument is now read as a chain of filters.
Contributor
Author
|
Fix pushed (421046c, 3174c9a): |
This was referenced Sep 27, 2026
Merged
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.
Part of #146, first and third items. The second item, data flowing from the render context to the filter's argument (the exploitable level), follows in its own PR.
Summary
{% if bio|striptags %}) and in{% filter %}blocks. Comments,{% comment %}and{% verbatim %}blocks, quoted text and custom filters apply none. Every template found under atemplatesdirectory counts, whether or not a Python call names it: a class-based view renders itstemplate_nameinside Django.taint.templates.FILTER_FUNCTIONSmaps the 73 filters to the functions Django registers them from. It includes the renamed ones (escape→escape_filter,slice→slice_filter,timezone→tz.do_timezone) and those ofi18n,l10n,tzandhumanize. I checked it against the Django 5.2.6 sources by AST: nothing missing and no mismatch.length_isis kept because it exists up to Django 5.0. The escaping judgement now derives its filter names from this table; the set is identical (71 names).ProjectContext.templates(ProjectTemplates:names,calls,unread) gives the plugins the templates found and the filters they apply.reachable-vulnerabilityreports an entry point that a filter reaches at the template's line.Coverage.unread_templateslists it. That means a name given by an expression, or a name found under notemplatesdirectory, inrender,render_to_string,TemplateResponse,{% include %}or{% extends %}; an unreadable template file counts too. The VEX report then keeps an advisory whose entry points include a template filterunder_investigation, and the notes say where. Advisories that no template can reach are not concerned.django.shortcuts.render,TemplateResponseandSimpleTemplateResponseare nowTemplateRendermodels, likerender_to_string.Limits
template_nameorget_template.templatesdirectory is read as a Django template. A Jinja2 template applyingstriptags, in a project that requires a vulnerable Django, would count as a call.escaped_templatesstill reads an unreadable template file as empty text, and so judges it escaping.Verification
not_affected).tests/test_template_filter_calls.py: 32 passed. Full suite under Python 3.12.10, regression snapshots included: 1336 passed.ruff checkandmypypass.