Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
instead of repeating its id (#270), and each result carries its confidence as the
`confidence` property (#271). A flow reaching its sink inside a project function points at
that sink as a related location, which the result's message links to (#271).
- Django templates are also read as each page is put together (#273): a variable a
child's `{% block %}` prints inside its parent's `<script>`, an `{% include %}` that ends
the attribute value it stands in, or one of a name several files have outside HTML data,
is reported as `xss` where it was not, as is the variable `block` named anywhere but in
a bare `{{ block.super }}`. So is a template that includes Django's
`django/forms/widgets/attrs.html` inside a tag, which prints attribute names, and a
template including itself inside a tag or a script. A page beyond 8 MiB once put
together, or a template with more than 64 pages, or 16 MiB in all, through names
several files have, is not established and is listed in `Coverage.unread_templates`.
- Plugin API, additive: `Plugin.rules` maps the rules a plugin reports to their
`RuleDescription`, optional, so that third-party plugins describe their own rules;
`Finding.related` lists `RelatedLocation`s (a span and a message). `Report.rules` carries the
Expand Down
21 changes: 19 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,25 @@ ends there and the browser would never run it. A template including one that end
such a block, a tag or an end tag is not established either: the markup after the
include continues it. A template extending another ends where that one ends. A comment
hides what it holds only up to its first template tag, since an include there may close
it and a condition around its `<!--` may leave it out. A `{% block %}` or
`{% include %}` placed inside a parent's `<script>` is not covered yet (#273).
it and a condition around its `<!--` may leave it out.

Each page is also put together as Django renders it, and read once more (#273): the
templates it extends, each block taking the definition of the most derived one and `{{
block.super }}` the next one up, and each template it includes in place. What a child's
block prints inside its parent's `<script>`, or what an include puts inside an
attribute, is read where it lands. `{{ block.super }}` is put in place only bare: the
variable `block` named anywhere else, through a filter, a tag or an alias (`{% with
parent=block %}{{ parent.super }}`), may still print the parent's markup, marked safe,
and the template is not established. A name several files have, which Django resolves
through loaders the analyzer does not read, counts as each of them: a page extending it
must escape with each file, except those already in its chain, which Django leaves out,
so that a template extending its own name extends another file of it; an include of it
is accepted only in HTML data, never inside an attribute, a tag, a `<script>` or
`<style>` block or a comment, and only when each file ends in HTML data, nothing of it
left open. A template including itself is read the same way. A page beyond 8 MiB once
put together, or a template with more than 64 pages, or 16 MiB in all, through the files
the templates it extends may be, is not established, and is listed among the templates
the analyzer could not read.

The analyzer never assumes escaping where it cannot read the template. That covers a
template file it cannot read, a template named by an expression, a template found
Expand Down
7 changes: 4 additions & 3 deletions src/coretrace_python/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@
SecurityModelRegistry,
TaintAnalysis,
TaintKind,
escaped_templates,
project_templates,
registered_routes,
request_processor,
template_escaping,
unread_renders,
)
from coretrace_python.taint.urls import flow_url
Expand Down Expand Up @@ -532,7 +532,8 @@ def analyze_project(
for name in sorted(analysable):
for symbol, registered in _routes_of(analysable[name]).items():
routes.setdefault(symbol, registered)
escaped = escaped_templates(root)
escaping = template_escaping(root)
escaped = escaping.escaped
templates = project_templates(root)
clearing = models.clearing(escaped)
template_calls = models.template_calls(
Expand Down Expand Up @@ -678,7 +679,7 @@ def analyze_project(
findings = [_sourced(finding, origins) for finding in findings]
accepted = tuple(f for f in findings if policy.accepts(f))
kept, suppressed = partition(apply_policy(policy, findings), _text_of(sources))
unread = list(templates.unread)
unread = [*templates.unread, *escaping.unread]
for name in sorted(call_graphs):
unread.extend(unread_renders(name, call_graphs[name], models, templates.names))
return ProjectAnalysis(
Expand Down
2 changes: 2 additions & 0 deletions src/coretrace_python/taint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
escaped_templates,
project_templates,
request_processor,
template_escaping,
unread_renders,
)

Expand Down Expand Up @@ -85,5 +86,6 @@
"propagate_taint",
"registered_routes",
"request_processor",
"template_escaping",
"unread_renders",
]
Loading
Loading