Skip to content

Commit b92af8b

Browse files
authored
Merge pull request #5042 from RasmusWL/django-more-view-classes
Python: Add full-path modeling of Django more view classes
2 parents c0511ca + 4b6a59a commit b92af8b

2 files changed

Lines changed: 279 additions & 4 deletions

File tree

python/ql/src/semmle/python/frameworks/Django.qll

Lines changed: 272 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ private module Django {
3535
* WARNING: Only holds for a few predefined attributes.
3636
*/
3737
private DataFlow::Node django_attr(DataFlow::TypeTracker t, string attr_name) {
38-
attr_name in ["db", "urls", "http", "conf"] and
38+
attr_name in ["db", "urls", "http", "conf", "views"] and
3939
(
4040
t.start() and
4141
result = DataFlow::importNode("django" + "." + attr_name)
@@ -1567,7 +1567,9 @@ private module Django {
15671567
"View", "TemplateView", "RedirectView", "ArchiveIndexView", "YearArchiveView",
15681568
"MonthArchiveView", "WeekArchiveView", "DayArchiveView", "TodayArchiveView",
15691569
"DateDetailView", "DetailView", "FormView", "CreateView", "UpdateView", "DeleteView",
1570-
"ListView", "GenericViewError"
1570+
"ListView", "GenericViewError",
1571+
// modules
1572+
"base", "dates", "detail", "edit", "list"
15711573
] and
15721574
(
15731575
t.start() and
@@ -1603,6 +1605,254 @@ private module Django {
16031605
result = generic_attr(DataFlow::TypeTracker::end(), attr_name)
16041606
}
16051607

1608+
// -------------------------------------------------------------------------
1609+
// django.views.generic.base
1610+
// -------------------------------------------------------------------------
1611+
/** Gets a reference to the `django.views.generic.base` module. */
1612+
DataFlow::Node base() { result = generic_attr("base") }
1613+
1614+
/** Provides models for the `django.views.generic.base` module */
1615+
module base {
1616+
/**
1617+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.base` module.
1618+
* WARNING: Only holds for a few predefined attributes.
1619+
*/
1620+
private DataFlow::Node base_attr(DataFlow::TypeTracker t, string attr_name) {
1621+
attr_name in ["RedirectView", "TemplateView", "View"] and
1622+
(
1623+
t.start() and
1624+
result = DataFlow::importNode("django.views.generic.base" + "." + attr_name)
1625+
or
1626+
t.startInAttr(attr_name) and
1627+
result = base()
1628+
)
1629+
or
1630+
// Due to bad performance when using normal setup with `base_attr(t2, attr_name).track(t2, t)`
1631+
// we have inlined that code and forced a join
1632+
exists(DataFlow::TypeTracker t2 |
1633+
exists(DataFlow::StepSummary summary |
1634+
base_attr_first_join(t2, attr_name, result, summary) and
1635+
t = t2.append(summary)
1636+
)
1637+
)
1638+
}
1639+
1640+
pragma[nomagic]
1641+
private predicate base_attr_first_join(
1642+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
1643+
DataFlow::StepSummary summary
1644+
) {
1645+
DataFlow::StepSummary::step(base_attr(t2, attr_name), res, summary)
1646+
}
1647+
1648+
/**
1649+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.base` module.
1650+
* WARNING: Only holds for a few predefined attributes.
1651+
*/
1652+
DataFlow::Node base_attr(string attr_name) {
1653+
result = base_attr(DataFlow::TypeTracker::end(), attr_name)
1654+
}
1655+
}
1656+
1657+
// -------------------------------------------------------------------------
1658+
// django.views.generic.dates
1659+
// -------------------------------------------------------------------------
1660+
/** Gets a reference to the `django.views.generic.dates` module. */
1661+
DataFlow::Node dates() { result = generic_attr("dates") }
1662+
1663+
/** Provides models for the `django.views.generic.dates` module */
1664+
module dates {
1665+
/**
1666+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.dates` module.
1667+
* WARNING: Only holds for a few predefined attributes.
1668+
*/
1669+
private DataFlow::Node dates_attr(DataFlow::TypeTracker t, string attr_name) {
1670+
attr_name in [
1671+
"ArchiveIndexView", "DateDetailView", "DayArchiveView", "MonthArchiveView",
1672+
"TodayArchiveView", "WeekArchiveView", "YearArchiveView"
1673+
] and
1674+
(
1675+
t.start() and
1676+
result = DataFlow::importNode("django.views.generic.dates" + "." + attr_name)
1677+
or
1678+
t.startInAttr(attr_name) and
1679+
result = dates()
1680+
)
1681+
or
1682+
// Due to bad performance when using normal setup with `dates_attr(t2, attr_name).track(t2, t)`
1683+
// we have inlined that code and forced a join
1684+
exists(DataFlow::TypeTracker t2 |
1685+
exists(DataFlow::StepSummary summary |
1686+
dates_attr_first_join(t2, attr_name, result, summary) and
1687+
t = t2.append(summary)
1688+
)
1689+
)
1690+
}
1691+
1692+
pragma[nomagic]
1693+
private predicate dates_attr_first_join(
1694+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
1695+
DataFlow::StepSummary summary
1696+
) {
1697+
DataFlow::StepSummary::step(dates_attr(t2, attr_name), res, summary)
1698+
}
1699+
1700+
/**
1701+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.dates` module.
1702+
* WARNING: Only holds for a few predefined attributes.
1703+
*/
1704+
DataFlow::Node dates_attr(string attr_name) {
1705+
result = dates_attr(DataFlow::TypeTracker::end(), attr_name)
1706+
}
1707+
}
1708+
1709+
// -------------------------------------------------------------------------
1710+
// django.views.generic.detail
1711+
// -------------------------------------------------------------------------
1712+
/** Gets a reference to the `django.views.generic.detail` module. */
1713+
DataFlow::Node detail() { result = generic_attr("detail") }
1714+
1715+
/** Provides models for the `django.views.generic.detail` module */
1716+
module detail {
1717+
/**
1718+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.detail` module.
1719+
* WARNING: Only holds for a few predefined attributes.
1720+
*/
1721+
private DataFlow::Node detail_attr(DataFlow::TypeTracker t, string attr_name) {
1722+
attr_name in ["DetailView"] and
1723+
(
1724+
t.start() and
1725+
result = DataFlow::importNode("django.views.generic.detail" + "." + attr_name)
1726+
or
1727+
t.startInAttr(attr_name) and
1728+
result = detail()
1729+
)
1730+
or
1731+
// Due to bad performance when using normal setup with `detail_attr(t2, attr_name).track(t2, t)`
1732+
// we have inlined that code and forced a join
1733+
exists(DataFlow::TypeTracker t2 |
1734+
exists(DataFlow::StepSummary summary |
1735+
detail_attr_first_join(t2, attr_name, result, summary) and
1736+
t = t2.append(summary)
1737+
)
1738+
)
1739+
}
1740+
1741+
pragma[nomagic]
1742+
private predicate detail_attr_first_join(
1743+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
1744+
DataFlow::StepSummary summary
1745+
) {
1746+
DataFlow::StepSummary::step(detail_attr(t2, attr_name), res, summary)
1747+
}
1748+
1749+
/**
1750+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.detail` module.
1751+
* WARNING: Only holds for a few predefined attributes.
1752+
*/
1753+
DataFlow::Node detail_attr(string attr_name) {
1754+
result = detail_attr(DataFlow::TypeTracker::end(), attr_name)
1755+
}
1756+
}
1757+
1758+
// -------------------------------------------------------------------------
1759+
// django.views.generic.edit
1760+
// -------------------------------------------------------------------------
1761+
/** Gets a reference to the `django.views.generic.edit` module. */
1762+
DataFlow::Node edit() { result = generic_attr("edit") }
1763+
1764+
/** Provides models for the `django.views.generic.edit` module */
1765+
module edit {
1766+
/**
1767+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.edit` module.
1768+
* WARNING: Only holds for a few predefined attributes.
1769+
*/
1770+
private DataFlow::Node edit_attr(DataFlow::TypeTracker t, string attr_name) {
1771+
attr_name in ["CreateView", "DeleteView", "FormView", "UpdateView"] and
1772+
(
1773+
t.start() and
1774+
result = DataFlow::importNode("django.views.generic.edit" + "." + attr_name)
1775+
or
1776+
t.startInAttr(attr_name) and
1777+
result = edit()
1778+
)
1779+
or
1780+
// Due to bad performance when using normal setup with `edit_attr(t2, attr_name).track(t2, t)`
1781+
// we have inlined that code and forced a join
1782+
exists(DataFlow::TypeTracker t2 |
1783+
exists(DataFlow::StepSummary summary |
1784+
edit_attr_first_join(t2, attr_name, result, summary) and
1785+
t = t2.append(summary)
1786+
)
1787+
)
1788+
}
1789+
1790+
pragma[nomagic]
1791+
private predicate edit_attr_first_join(
1792+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
1793+
DataFlow::StepSummary summary
1794+
) {
1795+
DataFlow::StepSummary::step(edit_attr(t2, attr_name), res, summary)
1796+
}
1797+
1798+
/**
1799+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.edit` module.
1800+
* WARNING: Only holds for a few predefined attributes.
1801+
*/
1802+
DataFlow::Node edit_attr(string attr_name) {
1803+
result = edit_attr(DataFlow::TypeTracker::end(), attr_name)
1804+
}
1805+
}
1806+
1807+
// -------------------------------------------------------------------------
1808+
// django.views.generic.list
1809+
// -------------------------------------------------------------------------
1810+
/** Gets a reference to the `django.views.generic.list` module. */
1811+
DataFlow::Node list() { result = generic_attr("list") }
1812+
1813+
/** Provides models for the `django.views.generic.list` module */
1814+
module list {
1815+
/**
1816+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.list` module.
1817+
* WARNING: Only holds for a few predefined attributes.
1818+
*/
1819+
private DataFlow::Node list_attr(DataFlow::TypeTracker t, string attr_name) {
1820+
attr_name in ["ListView"] and
1821+
(
1822+
t.start() and
1823+
result = DataFlow::importNode("django.views.generic.list" + "." + attr_name)
1824+
or
1825+
t.startInAttr(attr_name) and
1826+
result = list()
1827+
)
1828+
or
1829+
// Due to bad performance when using normal setup with `list_attr(t2, attr_name).track(t2, t)`
1830+
// we have inlined that code and forced a join
1831+
exists(DataFlow::TypeTracker t2 |
1832+
exists(DataFlow::StepSummary summary |
1833+
list_attr_first_join(t2, attr_name, result, summary) and
1834+
t = t2.append(summary)
1835+
)
1836+
)
1837+
}
1838+
1839+
pragma[nomagic]
1840+
private predicate list_attr_first_join(
1841+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
1842+
DataFlow::StepSummary summary
1843+
) {
1844+
DataFlow::StepSummary::step(list_attr(t2, attr_name), res, summary)
1845+
}
1846+
1847+
/**
1848+
* Gets a reference to the attribute `attr_name` of the `django.views.generic.list` module.
1849+
* WARNING: Only holds for a few predefined attributes.
1850+
*/
1851+
DataFlow::Node list_attr(string attr_name) {
1852+
result = list_attr(DataFlow::TypeTracker::end(), attr_name)
1853+
}
1854+
}
1855+
16061856
/**
16071857
* Provides models for the `django.views.generic.View` class and subclasses.
16081858
*
@@ -1624,9 +1874,27 @@ private module Django {
16241874
"DeleteView", "ListView"
16251875
])
16261876
or
1627-
// `django.views.View` alias
1877+
// aliases
16281878
t.start() and
1629-
result = views_attr("View")
1879+
(
1880+
// django.views.View
1881+
result = views_attr("View")
1882+
or
1883+
// django.views.generic.base.*
1884+
result = base::base_attr(_)
1885+
or
1886+
// django.views.generic.dates.*
1887+
result = dates::dates_attr(_)
1888+
or
1889+
// django.views.generic.detail.*
1890+
result = detail::detail_attr(_)
1891+
or
1892+
// django.views.generic.edit.*
1893+
result = edit::edit_attr(_)
1894+
or
1895+
// django.views.generic.list.*
1896+
result = list::list_attr(_)
1897+
)
16301898
or
16311899
// subclasses in project code
16321900
result.asExpr().(ClassExpr).getABase() = subclassRef(t.continue()).asExpr()

python/ql/test/experimental/library-tests/frameworks/django-v2-v3/routing_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.urls import path, re_path
33
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse, HttpResponseNotFound
44
from django.views import View
5+
import django.views.generic.base
56

67

78
def url_match_xss(request, foo, bar, no_taint=None): # $requestHandler routedParameter=foo routedParameter=bar
@@ -36,6 +37,12 @@ def get(self, request, untrusted): # $ requestHandler routedParameter=untrusted
3637
return HttpResponse('ClassView get: {}'.format(untrusted)) # $HttpResponse
3738

3839

40+
# direct import with full path to `View` class
41+
class ClassView2(django.views.generic.base.View):
42+
def get(self, request): # $ requestHandler
43+
pass
44+
45+
3946
def show_articles(request, page_number=1): # $requestHandler routedParameter=page_number
4047
page_number = int(page_number)
4148
return HttpResponse('articles page: {}'.format(page_number)) # $HttpResponse

0 commit comments

Comments
 (0)