Skip to content

Commit 27baffe

Browse files
joke1196sonartech
authored andcommitted
SONARPY-3776: Extend S5344: Detect direct password assignment to Django User model instead of using set_password() or create_user() (#920)
GitOrigin-RevId: b92267be8d757cafc0d293e80871330f844c4a9b
1 parent 670160a commit 27baffe

File tree

10 files changed

+361
-110
lines changed

10 files changed

+361
-110
lines changed

python-checks/src/main/java/org/sonar/python/checks/hotspots/FastHashingOrPlainTextCheck.java

Lines changed: 120 additions & 105 deletions
Large diffs are not rendered by default.

python-checks/src/test/resources/checks/fastHashingOrPlainText/fastHashingOrPlainText.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,43 @@ def flask_config():
368368
PASSWORD_HASHERS = [
369369
"django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher", # OK outside of settings.py
370370
]
371+
372+
373+
## Django User Password
374+
375+
def django_user_password_assignment():
376+
from django.contrib.auth.models import User
377+
378+
user = User()
379+
user.username = 'john'
380+
user.password = 'mysecretpassword' # Noncompliant {{Use set_password() or create_user() to properly hash passwords.}}
381+
user.save()
382+
383+
# Compliant - using set_password()
384+
user2 = User()
385+
user2.set_password('mysecretpassword')
386+
user2.save()
387+
388+
# Compliant - setting other attributes
389+
user3 = User()
390+
user3.username = 'jane'
391+
user3.email = 'jane@example.com'
392+
393+
394+
def django_user_create():
395+
from django.contrib.auth.models import User
396+
397+
# Using create() with password argument
398+
user = User.objects.create(
399+
username='john',
400+
password='mysecretpassword' # Noncompliant {{Use set_password() or create_user() to properly hash passwords.}}
401+
)
402+
403+
# Compliant - using create_user()
404+
user = User.objects.create_user(
405+
username='john',
406+
password='mysecretpassword'
407+
)
408+
409+
# Compliant - using create() without password argument
410+
user = User.objects.create(username='john')

python-frontend/src/main/resources/org/sonar/python/types/custom_protobuf/django.contrib.auth.models.protobuf

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
2+
django.contrib.auth.models�
3+
4+
UserManager&django.contrib.auth.models.UserManager" django.db.models.manager.Manager*�
5+
create-django.contrib.auth.models.UserManager.create"Z
6+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*Z
7+
selfP
8+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager*
9+
kwargs
10+
Any*�
11+
create_user2django.contrib.auth.models.UserManager.create_user"Z
12+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*Z
13+
selfP
14+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager**
15+
username
16+
builtins.str" builtins.str*Q
17+
emailD
18+
Union[builtins.str,None]
19+
builtins.str" builtins.str
20+
None *T
21+
passwordD
22+
Union[builtins.str,None]
23+
builtins.str" builtins.str
24+
None *
25+
extra_fields
26+
Any*�
27+
create_superuser7django.contrib.auth.models.UserManager.create_superuser"Z
28+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*Z
29+
selfP
30+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager**
31+
username
32+
builtins.str" builtins.str*Q
33+
emailD
34+
Union[builtins.str,None]
35+
builtins.str" builtins.str
36+
None *T
37+
passwordD
38+
Union[builtins.str,None]
39+
builtins.str" builtins.str
40+
None *
41+
extra_fields
42+
Any�
43+
AbstractBaseUser+django.contrib.auth.models.AbstractBaseUser"django.db.models.base.Model"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
44+
set_password8django.contrib.auth.models.AbstractBaseUser.set_password"
45+
None*d
46+
selfZ
47+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*.
48+
raw_password
49+
builtins.str" builtins.str*�
50+
check_password:django.contrib.auth.models.AbstractBaseUser.check_password"
51+
builtins.bool"builtins.bool*d
52+
selfZ
53+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*.
54+
raw_password
55+
builtins.str" builtins.strr^
56+
password4django.contrib.auth.models.AbstractBaseUser.password
57+
builtins.str" builtins.str�
58+
AbstractUser'django.contrib.auth.models.AbstractUser"+django.contrib.auth.models.AbstractBaseUserrZ
59+
username0django.contrib.auth.models.AbstractUser.username
60+
builtins.str" builtins.strrT
61+
email-django.contrib.auth.models.AbstractUser.email
62+
builtins.str" builtins.strr�
63+
objects/django.contrib.auth.models.AbstractUser.objectsP
64+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager�
65+
Userdjango.contrib.auth.models.User"'django.contrib.auth.models.AbstractUserr�
66+
objects'django.contrib.auth.models.User.objectsP
67+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManagere
68+
AnonymousUser(django.contrib.auth.models.AnonymousUser"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
69+
__annotations__*django.contrib.auth.models.__annotations__W
70+
builtins.dict[builtins.str,Any]
71+
builtins.str" builtins.str
72+
Any"builtins.dict

python-frontend/src/main/resources/org/sonar/python/types/custom_protobuf/django.contrib.auth.protobuf

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,71 @@
11

2-
django.contrib.auth*t
2+
django.contrib.auth�
3+
AbstractBaseUser+django.contrib.auth.models.AbstractBaseUser"django.db.models.base.Model"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
4+
set_password8django.contrib.auth.models.AbstractBaseUser.set_password"
5+
None*d
6+
selfZ
7+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*.
8+
raw_password
9+
builtins.str" builtins.str*�
10+
check_password:django.contrib.auth.models.AbstractBaseUser.check_password"
11+
builtins.bool"builtins.bool*d
12+
selfZ
13+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*.
14+
raw_password
15+
builtins.str" builtins.strr^
16+
password4django.contrib.auth.models.AbstractBaseUser.password
17+
builtins.str" builtins.str�
18+
AbstractUser'django.contrib.auth.models.AbstractUser"+django.contrib.auth.models.AbstractBaseUserrZ
19+
username0django.contrib.auth.models.AbstractUser.username
20+
builtins.str" builtins.strrT
21+
email-django.contrib.auth.models.AbstractUser.email
22+
builtins.str" builtins.strr�
23+
objects/django.contrib.auth.models.AbstractUser.objectsP
24+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManagere
25+
AnonymousUser(django.contrib.auth.models.AnonymousUser"*SonarPythonAnalyzerFakeStub.CustomStubBase�
26+
Userdjango.contrib.auth.models.User"'django.contrib.auth.models.AbstractUserr�
27+
objects'django.contrib.auth.models.User.objectsP
28+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager�
29+
30+
UserManager&django.contrib.auth.models.UserManager" django.db.models.manager.Manager*�
31+
create-django.contrib.auth.models.UserManager.create"Z
32+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*Z
33+
selfP
34+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager*
35+
kwargs
36+
Any*�
37+
create_user2django.contrib.auth.models.UserManager.create_user"Z
38+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*Z
39+
selfP
40+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager**
41+
username
42+
builtins.str" builtins.str*Q
43+
emailD
44+
Union[builtins.str,None]
45+
builtins.str" builtins.str
46+
None *T
47+
passwordD
48+
Union[builtins.str,None]
49+
builtins.str" builtins.str
50+
None *
51+
extra_fields
52+
Any*�
53+
create_superuser7django.contrib.auth.models.UserManager.create_superuser"Z
54+
+django.contrib.auth.models.AbstractBaseUser"+django.contrib.auth.models.AbstractBaseUser*Z
55+
selfP
56+
&django.contrib.auth.models.UserManager"&django.contrib.auth.models.UserManager**
57+
username
58+
builtins.str" builtins.str*Q
59+
emailD
60+
Union[builtins.str,None]
61+
builtins.str" builtins.str
62+
None *T
63+
passwordD
64+
Union[builtins.str,None]
65+
builtins.str" builtins.str
66+
None *
67+
extra_fields
68+
Any*t
369
__path__django.contrib.auth.__path__J
470
builtins.list[builtins.str]
571
builtins.str" builtins.str"builtins.list*�
@@ -8,4 +74,5 @@
874
builtins.str" builtins.str
975
Any"builtins.dict*.
1076

11-
middlewaredjango.contrib.auth.middleware 
77+
middlewaredjango.contrib.auth.middleware *&
78+
modelsdjango.contrib.auth.models 

python-frontend/src/main/resources/org/sonar/python/types/custom_protobuf/django.utils.protobuf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
builtins.dict[builtins.str,Any]
88
builtins.str" builtins.str
99
Any"builtins.dict*
10-
htmldjango.utils.html 
10+
htmldjango.utils.html *)
11+
deprecationdjango.utils.deprecation 

python-frontend/src/test/java/org/sonar/python/types/TypeShedTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,16 @@ void package_django() {
284284
assertThat(responseSymbol.fullyQualifiedName()).isEqualTo("django.http.response.HttpResponse");
285285
}
286286

287+
@Test
288+
void package_django_contrib_auth() {
289+
Map<String, Symbol> djangoAuthSymbols = symbolsForModule("django.contrib.auth.models");
290+
assertThat(djangoAuthSymbols).isNotEmpty();
291+
Symbol userSymbol = djangoAuthSymbols.get("User");
292+
assertThat(userSymbol).isNotNull();
293+
assertThat(userSymbol.kind()).isEqualTo(Kind.CLASS);
294+
assertThat(userSymbol.fullyQualifiedName()).isEqualTo("django.contrib.auth.models.User");
295+
}
296+
287297
@Test
288298
void return_type_hints() {
289299
Map<String, Symbol> symbols = symbolsForModule("typing");
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
1c8010bc70819847c25235e37ca98a48d47747b77edc77887b1bdd3ee5c49521
2-
c854205ed8f8a5d5935b2164fd2f15bf962159b9a8045e243aec3c3b4e393e76
1+
843addd5d5f7af6ecacd2ed21b251d4a89a44fdbdcc9dd2af52fb12939f697fe
2+
d67269ba8194374e815fa405dfbf957e07d196202cd8d3328d4a5a40f90628b5

python-frontend/typeshed_serializer/resources/custom/django/__init__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ import django.conf as conf
66
import django.apps as apps
77
import django.contrib as contrib
88
import django.views as views
9+
import django.contrib as contrib
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
11
import django.contrib.auth.middleware as middleware
2+
import django.contrib.auth.models as models
3+
4+
from .models import (
5+
AbstractBaseUser as AbstractBaseUser,
6+
AbstractUser as AbstractUser,
7+
AnonymousUser as AnonymousUser,
8+
User as User,
9+
UserManager as UserManager,
10+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from SonarPythonAnalyzerFakeStub import CustomStubBase
2+
from django.db.models.base import Model
3+
from django.db.models.manager import Manager
4+
from typing import Any
5+
6+
class UserManager(Manager):
7+
def create(self, **kwargs: Any) -> "AbstractBaseUser": ...
8+
def create_user(
9+
self,
10+
username: str,
11+
email: str | None = None,
12+
password: str | None = None,
13+
**extra_fields: Any,
14+
) -> "AbstractBaseUser": ...
15+
def create_superuser(
16+
self,
17+
username: str,
18+
email: str | None = None,
19+
password: str | None = None,
20+
**extra_fields: Any,
21+
) -> "AbstractBaseUser": ...
22+
23+
class AbstractBaseUser(Model, CustomStubBase):
24+
password: str
25+
def set_password(self, raw_password: str) -> None: ...
26+
def check_password(self, raw_password: str) -> bool: ...
27+
28+
class AbstractUser(AbstractBaseUser):
29+
username: str
30+
email: str
31+
objects: UserManager
32+
33+
class User(AbstractUser):
34+
objects: UserManager
35+
36+
class AnonymousUser(CustomStubBase): ...

0 commit comments

Comments
 (0)