Skip to content
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

sec: Integrate Ariadne's query cost validator #509

Merged
merged 17 commits into from
Apr 29, 2024

Conversation

ajay-sentry
Copy link
Contributor

@ajay-sentry ajay-sentry commented Apr 15, 2024

Purpose/Motivation

Our goal with this PR is to mitigate the possibility of a DoS attack with users making calls to our GQL API. We aim to tackle this by using Ariadne's built in query cost validator, assigning a default cost of 1 to each field being requested, a multiplier of x amount (between 3 and 25) * number of edge resources requested (e.g. first:20 repos would add 20 * 25 to the cost, not 20 * number of fields requested for repo, but that is something we can update later on if we want additional granularity in the future)

This first pass PR is meant to lay the infrastructure and initial guidelines that I hope we can tinker and update as we better understand what cost "makes sense" for us long term. For now, I have set the maximum cost to the value mentioned on slack. This seems to be reasonable since we're fetching at most "first: 20" in gazebo, and the "lengthiest" query looks to have a size of ~40 fields. Refering to: src/services/user/useUser.js

Dev Notes:
Below is the thought process that got me to this point and why I chose to add the query complexity logic server side, and using ariadne.

Initially started search looking at what we are currently using for sending HTTP requests on the frontend. GraphQL api looks to be a wrapper function around the JS native Fetch API. Didn’t want to rip out existing infrastructure and put in something like Apollo Client, though that does look to have native capabilities around validating query complexity or looking at query depth, etc. Fetch API also doesn’t have much “built in,” but didn’t rule out the possibility of making creating some simple middleware for doing some type of complexity analysis natively. But there’s probably still a much lower friction way to do this, so started looking at ways we can maybe accomplish something similar server side at the resolver level.

It looks like Ariadne has some query cost validator built in: https://ariadnegraphql.org/docs/0.19/query-validators

Helpful resources / Further Reading:

Links to relevant tickets

Closes https://github.com/codecov/internal-issues/issues/385

What does this PR do?

  • Integrates Ariadne's cost complexity validator in our API
  • Assigns cost of "1" for related fetches
  • Assigns cost of "1" for each field requested
  • Assigns maximum query cost to be a number defined via env variable (prevent bad actors from knowing our limit and abusing right below that value)

Screenshots from Testing

Query cost massively ballooning when I set query cost to be 20 on the multiplier.

Screenshot 2024-04-15 at 2 31 15 PM

Query cost prior to setting 20 on multiplier
Screenshot 2024-04-15 at 2 04 54 PM

Fuzzy find for "first" in Gazebo
Screenshot 2024-04-15 at 2 38 01 PM

UPDATE FROM 4/18/2024

Testing out nesting pulls on the ReposForOwner query
Screenshot 2024-04-18 at 1 49 18 PM

Without nesting pulls on the ReposForOwner query (Original)
Screenshot 2024-04-18 at 1 50 06 PM

Query for Reference:
query ReposForOwner( $filters: RepositorySetFilters! $owner: String! $ordering: RepositoryOrdering! $direction: OrderingDirection! $after: String $first: Int ) { owner(username: $owner) { repositories( filters: $filters ordering: $ordering orderingDirection: $direction first: $first after: $after ) { edges { node { name active activated private coverage updatedAt latestCommitAt lines author { username } coverageEnabled bundleAnalysisEnabled repositoryConfig { indicationRange { upperRange lowerRange } } pulls( orderingDirection: $direction first: 20 after: $after ){ edges { node { title } } } } } pageInfo { hasNextPage endCursor } } } }

Legal Boilerplate

Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. In 2022 this entity acquired Codecov and as result Sentry is going to need some rights from me in order to utilize my contributions in this PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.

@ajay-sentry ajay-sentry requested a review from a team as a code owner April 15, 2024 21:53
@codecov-notifications
Copy link

codecov-notifications bot commented Apr 15, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

✅ All tests successful. No failed tests found ☺️

📢 Thoughts on this report? Let us know!

@codecov-qa
Copy link

codecov-qa bot commented Apr 15, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 91.46%. Comparing base (9570180) to head (79da642).

✅ All tests successful. No failed tests found ☺️

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #509      +/-   ##
==========================================
+ Coverage   91.45%   91.46%   +0.01%     
==========================================
  Files         599      599              
  Lines       16211    16235      +24     
==========================================
+ Hits        14825    14849      +24     
  Misses       1386     1386              
Flag Coverage Δ
unit 91.46% <100.00%> (+0.01%) ⬆️
unit-latest-uploader 91.46% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

codecov-public-qa bot commented Apr 15, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 91.46%. Comparing base (9570180) to head (79da642).

✅ All tests successful. No failed tests found ☺️

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #509      +/-   ##
==========================================
+ Coverage   91.45%   91.46%   +0.01%     
==========================================
  Files         599      599              
  Lines       16211    16235      +24     
==========================================
+ Hits        14825    14849      +24     
  Misses       1386     1386              
Flag Coverage Δ
unit 91.46% <100.00%> (+0.01%) ⬆️
unit-latest-uploader 91.46% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
codecov/settings_base.py 85.94% <100.00%> (+0.07%) ⬆️
graphql_api/types/__init__.py 100.00% <100.00%> (ø)
graphql_api/views.py 97.02% <100.00%> (+0.82%) ⬆️

Impacted file tree graph

Copy link

codecov bot commented Apr 23, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.77%. Comparing base (9570180) to head (79da642).

✅ All tests successful. No failed tests found ☺️

Additional details and impacted files
@@           Coverage Diff           @@
##            main    #509     +/-   ##
=======================================
+ Coverage   95.76   95.77   +0.01     
=======================================
  Files        774     774             
  Lines      17067   17091     +24     
=======================================
+ Hits       16344   16368     +24     
  Misses       723     723             
Flag Coverage Δ
unit 91.46% <100.00%> (+0.01%) ⬆️
unit-latest-uploader 91.46% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

maximum_cost=costs.get("maximumAvailable"),
),
)
return HttpResponseBadRequest(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we don't want to expose the max cost to the client

) -> Optional[Collection]:
return [
cost_validator(
maximum_cost=settings.GRAPHQL_QUERY_COST_THRESHOLD,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ingest our env variable when deriving the max cost

JerrySentry
JerrySentry previously approved these changes Apr 25, 2024
@ajay-sentry ajay-sentry added this pull request to the merge queue Apr 29, 2024
Merged via the queue into main with commit 9d70756 Apr 29, 2024
21 checks passed
@ajay-sentry ajay-sentry deleted the Ajay/385-gql-cost-validator branch April 29, 2024 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants