django_sql_injection.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #
  2. # Copyright (C) 2018 [Victor Torre](https://github.com/ehooo)
  3. #
  4. # SPDX-License-Identifier: Apache-2.0
  5. import ast
  6. import bandit
  7. from bandit.core import issue
  8. from bandit.core import test_properties as test
  9. def keywords2dict(keywords):
  10. kwargs = {}
  11. for node in keywords:
  12. if isinstance(node, ast.keyword):
  13. kwargs[node.arg] = node.value
  14. return kwargs
  15. @test.checks("Call")
  16. @test.test_id("B610")
  17. def django_extra_used(context):
  18. """**B610: Potential SQL injection on extra function**
  19. :Example:
  20. .. code-block:: none
  21. >> Issue: [B610:django_extra_used] Use of extra potential SQL attack vector.
  22. Severity: Medium Confidence: Medium
  23. CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
  24. Location: examples/django_sql_injection_extra.py:29:0
  25. More Info: https://bandit.readthedocs.io/en/latest/plugins/b610_django_extra_used.html
  26. 28 tables_str = 'django_content_type" WHERE "auth_user"."username"="admin'
  27. 29 User.objects.all().extra(tables=[tables_str]).distinct()
  28. .. seealso::
  29. - https://docs.djangoproject.com/en/dev/topics/security/\
  30. #sql-injection-protection
  31. - https://cwe.mitre.org/data/definitions/89.html
  32. .. versionadded:: 1.5.0
  33. .. versionchanged:: 1.7.3
  34. CWE information added
  35. """ # noqa: E501
  36. description = "Use of extra potential SQL attack vector."
  37. if context.call_function_name == "extra":
  38. kwargs = keywords2dict(context.node.keywords)
  39. args = context.node.args
  40. if args:
  41. if len(args) >= 1:
  42. kwargs["select"] = args[0]
  43. if len(args) >= 2:
  44. kwargs["where"] = args[1]
  45. if len(args) >= 3:
  46. kwargs["params"] = args[2]
  47. if len(args) >= 4:
  48. kwargs["tables"] = args[3]
  49. if len(args) >= 5:
  50. kwargs["order_by"] = args[4]
  51. if len(args) >= 6:
  52. kwargs["select_params"] = args[5]
  53. insecure = False
  54. for key in ["where", "tables"]:
  55. if key in kwargs:
  56. if isinstance(kwargs[key], ast.List):
  57. for val in kwargs[key].elts:
  58. if not isinstance(val, ast.Str):
  59. insecure = True
  60. break
  61. else:
  62. insecure = True
  63. break
  64. if not insecure and "select" in kwargs:
  65. if isinstance(kwargs["select"], ast.Dict):
  66. for k in kwargs["select"].keys:
  67. if not isinstance(k, ast.Str):
  68. insecure = True
  69. break
  70. if not insecure:
  71. for v in kwargs["select"].values:
  72. if not isinstance(v, ast.Str):
  73. insecure = True
  74. break
  75. else:
  76. insecure = True
  77. if insecure:
  78. return bandit.Issue(
  79. severity=bandit.MEDIUM,
  80. confidence=bandit.MEDIUM,
  81. cwe=issue.Cwe.SQL_INJECTION,
  82. text=description,
  83. )
  84. @test.checks("Call")
  85. @test.test_id("B611")
  86. def django_rawsql_used(context):
  87. """**B611: Potential SQL injection on RawSQL function**
  88. :Example:
  89. .. code-block:: none
  90. >> Issue: [B611:django_rawsql_used] Use of RawSQL potential SQL attack vector.
  91. Severity: Medium Confidence: Medium
  92. CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
  93. Location: examples/django_sql_injection_raw.py:11:26
  94. More Info: https://bandit.readthedocs.io/en/latest/plugins/b611_django_rawsql_used.html
  95. 10 ' WHERE "username"="admin" OR 1=%s --'
  96. 11 User.objects.annotate(val=RawSQL(raw, [0]))
  97. .. seealso::
  98. - https://docs.djangoproject.com/en/dev/topics/security/\
  99. #sql-injection-protection
  100. - https://cwe.mitre.org/data/definitions/89.html
  101. .. versionadded:: 1.5.0
  102. .. versionchanged:: 1.7.3
  103. CWE information added
  104. """ # noqa: E501
  105. description = "Use of RawSQL potential SQL attack vector."
  106. if context.is_module_imported_like("django.db.models"):
  107. if context.call_function_name == "RawSQL":
  108. sql = context.node.args[0]
  109. if not isinstance(sql, ast.Str):
  110. return bandit.Issue(
  111. severity=bandit.MEDIUM,
  112. confidence=bandit.MEDIUM,
  113. cwe=issue.Cwe.SQL_INJECTION,
  114. text=description,
  115. )