Skip to content

Commit 4338c06

Browse files
committed
Python: Support Django FileField.upload_to
1 parent 8d1e22b commit 4338c06

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,38 @@ module PrivateDjango {
576576
}
577577
}
578578

579+
/**
580+
* Provides models for the `django.db.models.FileField` class and `ImageField` subclasses.
581+
*
582+
* See
583+
* - https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.FileField
584+
* - https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ImageField
585+
*/
586+
module FileField {
587+
/** Gets a reference to the `flask.views.View` class or any subclass. */
588+
API::Node subclassRef() {
589+
exists(string className | className in ["FileField", "ImageField"] |
590+
// commonly used alias
591+
result =
592+
API::moduleImport("django")
593+
.getMember("db")
594+
.getMember("models")
595+
.getMember(className)
596+
.getASubclass*()
597+
or
598+
// actual class definition
599+
result =
600+
API::moduleImport("django")
601+
.getMember("db")
602+
.getMember("models")
603+
.getMember("fields")
604+
.getMember("files")
605+
.getMember(className)
606+
.getASubclass*()
607+
)
608+
}
609+
}
610+
579611
/**
580612
* Gets a reference to the Manager (django.db.models.Manager) for a django Model,
581613
* accessed by `<ModelName>.objects`.
@@ -2236,6 +2268,34 @@ module PrivateDjango {
22362268
}
22372269
}
22382270

2271+
/**
2272+
* A parameter that accepts the filename used to upload a file. This is the second
2273+
* parameter in functions used for the `upload_to` argument to a `FileField`.
2274+
*
2275+
* See
2276+
* - https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.FileField.upload_to
2277+
* - https://docs.djangoproject.com/en/3.1/topics/http/file-uploads/#handling-uploaded-files-with-a-model
2278+
*/
2279+
private class DjangoFileFieldUploadToFunctionFilenameParam extends RemoteFlowSource::Range,
2280+
DataFlow::ParameterNode {
2281+
DjangoFileFieldUploadToFunctionFilenameParam() {
2282+
exists(DataFlow::CallCfgNode call, DataFlow::Node uploadToArg, Function func |
2283+
this.getParameter() = func.getArg(1) and
2284+
call = django::db::models::FileField::subclassRef().getACall() and
2285+
(
2286+
uploadToArg = call.getArg(2)
2287+
or
2288+
uploadToArg = call.getArgByName("upload_to")
2289+
) and
2290+
uploadToArg = poorMansFunctionTracker(func)
2291+
)
2292+
}
2293+
2294+
override string getSourceType() {
2295+
result = "django filename parameter to function used in FileField.upload_to"
2296+
}
2297+
}
2298+
22392299
// ---------------------------------------------------------------------------
22402300
// django.shortcuts.redirect
22412301
// ---------------------------------------------------------------------------
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from django.db import models
2+
import django.db.models.fields.files
3+
4+
def custom_path_function_1(instance, filename):
5+
ensure_tainted(filename) # $ tainted
6+
7+
def custom_path_function_2(instance, filename):
8+
ensure_tainted(filename) # $ tainted
9+
10+
def custom_path_function_3(instance, filename):
11+
ensure_tainted(filename) # $ tainted
12+
13+
def custom_path_function_4(instance, filename):
14+
ensure_tainted(filename) # $ tainted
15+
16+
17+
class CustomFileFieldSubclass(models.FileField):
18+
pass
19+
20+
21+
class MyModel(models.Model):
22+
upload_1 = models.FileField(None, None, custom_path_function_1)
23+
upload_2 = django.db.models.fields.files.FileField(upload_to=custom_path_function_2)
24+
25+
upload_3 = models.ImageField(upload_to=custom_path_function_3)
26+
27+
upload_4 = CustomFileFieldSubclass(upload_to=custom_path_function_4)

0 commit comments

Comments
 (0)