Skip to content

Python: Model Django REST framework #7016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Nov 12, 2021
Merged

Conversation

RasmusWL
Copy link
Member

@RasmusWL RasmusWL commented Nov 1, 2021

No description provided.

@RasmusWL
Copy link
Member Author

RasmusWL commented Nov 2, 2021

QLDocs check complains about these:

Error: The following CodeQL elements are lacking documentation:
semmle/python/frameworks/Django.qll  Django::PrivateDjango::DjangoRouteHandler                                    module
semmle/python/frameworks/Django.qll  Django::PrivateDjango::DjangoRouteHandler::Range                             class
semmle/python/frameworks/Django.qll  Django::PrivateDjango::DjangoRouteHandler::StandardDjangoRouteHandlers       class
semmle/python/frameworks/Django.qll  Django::PrivateDjango::django::conf::conf_urls                               module
semmle/python/frameworks/Django.qll  Django::PrivateDjango::django::db::DjangoDbConnection                        class
semmle/python/frameworks/Django.qll  Django::PrivateDjango::django::http::response::HttpResponse::baseClassRef/0  classless-predicate

I guess I should just go ahead and write QLDocs for them ...

@RasmusWL RasmusWL force-pushed the django-rest-framework branch from 78c748b to 83389be Compare November 2, 2021 10:03
@RasmusWL RasmusWL marked this pull request as ready for review November 2, 2021 10:26
@RasmusWL RasmusWL requested a review from a team as a code owner November 2, 2021 10:26
Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good.

I caught this in the doc:

Any APIException exceptions will be caught and mediated into appropriate responses.

Could that lead to unintended exposure of information?

The rest framework also seems to provide some rich routing capabilities, but I am happy to postpone any modelling of these until it has been established as impactful.

*/
private class ModeledApiViewClasses extends Django::Views::View::ModeledSubclass {
ModeledApiViewClasses() {
this = API::moduleImport("rest_framework").getMember("views").getMember("APIView") or
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this pass the autoformatter? I would have thought that the or should be on its own line.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was a bit surprised the first time I saw this as well, but apparently there are 2 accepts way of using or:

foo()
or
bar()
or
baz()

or

foo() or
bar() or
baz()

Slightly embarrassingly I had problems getting the quick-eval SubclassFinder query to output \n without tripping up the extension, so that why I ended up with this way of doing this (which is certainly non-standard for the python CodeQL code)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that the autoformatter runs as part of our tests, so it would complain if the formatting was wrong

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that it generated code that looked more familiar, but if it is legal, we can fix the aesthetics later. (Indeed I was confused about the autoformat check not firing..)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ask and you shall receive 😊

Comment on lines 164 to 178
/**
* Holds if `newModelFullyQualified` describes either a new subclass, or a new alias, belonging to `spec` that we should include in our automated modeling.
* This new element is defined by `ast`, which is defined at `loc` in the module `mod`.
*/
query predicate newModel(
FindSubclassesSpec spec, string newModelFullyQualified, AstNode ast, Module mod, Location loc
) {
(
newSubclass(spec, newModelFullyQualified, ast, mod, loc)
or
newDirectAlias(spec, newModelFullyQualified, ast, mod, loc)
or
newImportStar(spec, newModelFullyQualified, ast, mod, _, _, loc)
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like this predicate to be moved up. I found myself reding this first and its constituents later (even though they all interdepend given the recursion). I would say just below FindSubclassesSpecz (so currently to line 50).

concat(string newModelFullyQualified |
newModel(any(MySpec spec), newModelFullyQualified, _, _, _)
|
fullyQualifiedToAPIGraphPath(newModelFullyQualified), " or this = API::"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If only newModelFullyQualified were an API ::Node, so you could just use toString...

Comment on lines 179 to 188
// inherint problem with API graphs is that there doesn't need to exist a result for all
// the stuff we have already modeled... as an example, the following query has no
// results when evaluated against Django
//
// select API::moduleImport("django")
// .getMember("contrib")
// .getMember("admin")
// .getMember("views")
// .getMember("main")
// .getMember("ChangeListSearchForm")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the explanation for why you need to build strings? Could it be moved up and elaborated into such an argument?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. I've tried to highlight that now. Please add a suggestion if what I did was not sufficient :blush

@RasmusWL
Copy link
Member Author

Any APIException exceptions will be caught and mediated into appropriate responses.

Good find 👍 I'll take a look

@RasmusWL RasmusWL requested a review from yoff November 12, 2021 10:38
Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small suggestion, otherwise looks good. Nice work, including the exceptions 👍

result = this.getArgByName("detail")
}

// How to support the `headers` argument here?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that even honoured when an exception is raised?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a bad copy-paste, I have fixed that up

Comment on lines 41 to 62
// ---------------------------------------------------------------------------
// Implementation below
// ---------------------------------------------------------------------------
//
// inherent problem with API graphs is that there doesn't need to exist a result for
// all the stuff we have already modeled... as an example, the following query has no
// results when evaluated against a django/django DB
//
// select API::moduleImport("django")
// .getMember("contrib")
// .getMember("admin")
// .getMember("views")
// .getMember("main")
// .getMember("ChangeListSearchForm")
//
// therefore we use fully qualified names to capture new classes/new aliases.
//
// note that this implementation was originally created to help with automatically
// modeling packages in mind, and was just copied for this purpose. See
// https://github.com/github/codeql/pull/5632 for more discussion. I wanted to get
// this into the codeql-repo, so it could be of use when modeling 3rd party libraries,
// and save some manual effort.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rewrite it slightly:

Suggested change
// ---------------------------------------------------------------------------
// Implementation below
// ---------------------------------------------------------------------------
//
// inherent problem with API graphs is that there doesn't need to exist a result for
// all the stuff we have already modeled... as an example, the following query has no
// results when evaluated against a django/django DB
//
// select API::moduleImport("django")
// .getMember("contrib")
// .getMember("admin")
// .getMember("views")
// .getMember("main")
// .getMember("ChangeListSearchForm")
//
// therefore we use fully qualified names to capture new classes/new aliases.
//
// note that this implementation was originally created to help with automatically
// modeling packages in mind, and was just copied for this purpose. See
// https://github.com/github/codeql/pull/5632 for more discussion. I wanted to get
// this into the codeql-repo, so it could be of use when modeling 3rd party libraries,
// and save some manual effort.
// ---------------------------------------------------------------------------
// Implementation below
// ---------------------------------------------------------------------------
// We are looking to find all subclassed of the already modelled classes, and
// ideally we would identify an `API::Node` for each (then `toString` would give
// the API path).
//
// However, an inherent problem with API graphs is that there doesn't need to exist a result for
// all the stuff we have already modeled... as an example, the following query has no
// results when evaluated against a django/django DB
//
// select API::moduleImport("django")
// .getMember("contrib")
// .getMember("admin")
// .getMember("views")
// .getMember("main")
// .getMember("ChangeListSearchForm")
//
// Therefore we use fully qualified names to capture new classes/new aliases, and transform these
// into API paths, see `fullyQualifiedToAPIGraphPath`.
//
// Further automation is planned her, see
// https://github.com/github/codeql/pull/5632 for more discussion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't fully agree, but have rewritten based on your suggestion 👍

@RasmusWL RasmusWL requested a review from yoff November 12, 2021 12:31
Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@yoff yoff merged commit 9f614b1 into github:main Nov 12, 2021
@RasmusWL RasmusWL deleted the django-rest-framework branch November 12, 2021 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants