-
Notifications
You must be signed in to change notification settings - Fork 28
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
fix: Add graphql max depth and aliases limits #955
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅ ✅ All tests successful. No failed tests found. 📢 Thoughts on this report? Let us know! |
Codecov ReportAll modified and coverable lines are covered by tests ✅
✅ All tests successful. No failed tests found. Additional details and impacted files@@ Coverage Diff @@
## main #955 +/- ##
=======================================
Coverage 96.25% 96.25%
=======================================
Files 826 827 +1
Lines 19048 19090 +42
=======================================
+ Hits 18334 18376 +42
Misses 714 714
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
self.max_depth_reached: bool = False | ||
self.max_depth: int = max_depth | ||
|
||
def enter_operation_definition( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just curious, are these functions base functions you had to override default behavior of?
Similar story with enter_field, leave_field, and enter_document
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup! Though I don't think about it so much as "override" as "implement the interface" that is defined here. The function names enter_X
and leave_X
are dynamic per here. And for any method that's not implemented explicitly, it behaves as a no-op (here).
this is the stuff I looked at in Ariadne doc & this example implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool! Thanks for linking all those docs, that was a fun set of reads
graphql_api/views.py
Outdated
@@ -201,7 +202,11 @@ def get_validation_rules( | |||
maximum_cost=settings.GRAPHQL_QUERY_COST_THRESHOLD, | |||
default_cost=1, | |||
variables=data.get("variables"), | |||
) | |||
), | |||
create_max_depth_rule(max_depth=getattr(settings, "GRAPHQL_MAX_DEPTH", 15)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Do we need to do the getattr() here if the value will always be set in the settings_base file?
Nitpicking since the rest of the codebase is pulling off of settings directly 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, my intention was to force defensive programming there as I feel like I've seen a lot of accessing of dicts and tribal knowledge on when it's a bug.
But thinking about it again, it does seem like burying a default at this level is the wrong practice and since settings is something we control, we would prefer it to fail early and actually throw an error instead of having it "be forgiving" here.
Fixed it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha yep totally agreed! Thanks for making the update
codecov/settings_base.py
Outdated
@@ -106,6 +106,10 @@ | |||
|
|||
GRAPHQL_INTROSPECTION_ENABLED = False | |||
|
|||
GRAPHQL_MAX_DEPTH = 15 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wanted to hear a little more about landing on 15 for both of these values, do you think stage/production/dev should have the same value for each?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I skimmed the existing queries and the max depth was around ~10, so I thought it would be a good buffer. Could potentially do like 20 if we want more wiggle room. I think keeping it the same in all envs makes sense since honestly I think it's just a set-and-forget kind of thing. Also, we are the main consumers of the graphql api so I'd want to catch any queries that would error in prod to be caught first in the lower envs at the same settings.
For aliases, I don't think we ever use more than a handful (<5?) at a time, so thought a reasonable use case wouldn't go beyond say 15 anyway (and anything above that seems to be a malicious actor).
Open to thoughts on those. If any become a problem, it's as simple as bumping this up & we would catch the issue during development of some new feature anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally makes sense! I agree with you having it ~15 for the depth makes a lot of sense. Could probably lower the alias one if we really wanted, though not a deal breaker
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
Some other things tested - |
Add protections against Denial of Service attacks on the GraphQL API that use high depth, high breadth, or high aliases.
Note that we also already have cost validation by ariadne. The additional rules in this PR can catch cases where the cost validation is correct against our day-to-day use cases, but may be too lenient against crafted "attacks", such as the ones composed by the pentesters.
We also already have rate limiting on the GraphQL endpoint so that pentest recommendation is already covered.
Closes https://github.com/codecov/internal-issues/issues/918
Closes https://github.com/codecov/internal-issues/issues/917