Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit e10060c

Browse files
authored
[Backport 5.2] sec: Make GraphQL cost limits configurable (#58352)
sec: Make GraphQL cost limits configurable (#58346) * add ratelimit configuration to site-admin (cherry picked from commit 8ac56ca)
1 parent ae6d930 commit e10060c

File tree

9 files changed

+64
-10
lines changed

9 files changed

+64
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ All notable changes to Sourcegraph are documented in this file.
4444

4545
- Added a new authorization configuration options to GitLab code host connections: "markInternalReposAsPublic". Setting "markInternalReposAsPublic" to true is useful for organizations that have a large amount of internal repositories that everyone on the instance should be able to access, removing the need to have permissions to access these repositories. Additionally, when configuring a GitLab auth provider, you can specify "syncInternalRepoPermissions": false, which will remove the need to sync permissions for these internal repositories. [#57858](https://github.com/sourcegraph/sourcegraph/pull/57858)
4646
- Experimental support for OpenAI powered autocomplete has been added. [#57872](https://github.com/sourcegraph/sourcegraph/pull/57872)
47+
- Added configurable GraphQL query cost limitations to prevent unintended resource exhaustion. Default values are now provided and enforced, replacing the previously unlimited behaviour. For more information, please refer to: [GraphQL Cost Limits Documentation](https://docs.sourcegraph.com/api/graphql#cost-limits). See details at [#58346](https://github.com/sourcegraph/sourcegraph/pull/58346).
4748

4849
### Changed
4950

cmd/frontend/graphqlbackend/graphqlbackend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ func NewSchema(
634634
opts := []graphql.SchemaOpt{
635635
graphql.Tracer(newRequestTracer(logger, db)),
636636
graphql.UseStringDescriptions(),
637-
graphql.MaxDepth(maxDepth),
637+
graphql.MaxDepth(conf.RateLimits().GraphQLMaxDepth),
638638
}
639639
opts = append(opts, graphqlOpts...)
640640
return graphql.ParseSchema(

cmd/frontend/graphqlbackend/rate_limit.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ import (
2222
// the algorithm
2323
const costEstimateVersion = 2
2424

25-
const MaxAliasCount = 500 // SECURITY: prevent too many aliased queries
26-
const MaxFieldCount = 500 * 1000 // SECURITY: prevent deeply nested or overly broad queries
27-
const maxDepth = 30 // SECURITY: prevent deep queries that consume too many resources
28-
2925
type QueryCost struct {
3026
FieldCount int
3127
MaxDepth int

cmd/frontend/graphqlbackend/search_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func TestSearch(t *testing.T) {
122122
sr := newSchemaResolver(db, gsClient)
123123
gqlSchema, err := graphql.ParseSchema(mainSchema, sr,
124124
graphql.Tracer(newRequestTracer(logtest.Scoped(t), db)),
125-
graphql.MaxDepth(maxDepth))
125+
graphql.MaxDepth(conf.RateLimits().GraphQLMaxDepth))
126126
if err != nil {
127127
t.Fatal(err)
128128
}

cmd/frontend/graphqlbackend/testing.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
gqlerrors "github.com/graph-gophers/graphql-go/errors"
1616
"github.com/stretchr/testify/require"
1717

18+
"github.com/sourcegraph/sourcegraph/internal/conf"
1819
"github.com/sourcegraph/sourcegraph/internal/database"
1920
"github.com/sourcegraph/sourcegraph/internal/gitserver"
2021
)
@@ -31,7 +32,7 @@ func mustParseGraphQLSchemaWithClient(t *testing.T, db database.DB, gitserverCli
3132
gitserverClient,
3233
[]OptionalResolver{},
3334
graphql.PanicHandler(printStackTrace{&gqlerrors.DefaultPanicHandler{}}),
34-
graphql.MaxDepth(maxDepth),
35+
graphql.MaxDepth(conf.RateLimits().GraphQLMaxDepth),
3536
)
3637
if parseSchemaErr != nil {
3738
t.Fatal(parseSchemaErr)

cmd/frontend/internal/httpapi/graphql.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ func serveGraphQL(logger log.Logger, schema *graphql.Schema, rlw graphqlbackend.
102102
} else if cost != nil {
103103
traceData.cost = cost
104104

105-
if !isInternal && (cost.AliasCount > graphqlbackend.MaxAliasCount) {
105+
rl := conf.RateLimits()
106+
107+
if !isInternal && (cost.AliasCount > rl.GraphQLMaxAliases) {
106108
return writeViolationError(w, "query exceeds maximum query cost")
107109
}
108110

109-
if !isInternal && (cost.FieldCount > graphqlbackend.MaxFieldCount) {
111+
if !isInternal && (cost.FieldCount > rl.GraphQLMaxFieldCount) {
110112
return writeViolationError(w, "query exceeds maximum query cost")
111113
}
112114

internal/conf/computed.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,29 @@ func PasswordPolicyEnabled() bool {
450450
return pc.Enabled
451451
}
452452

453+
func RateLimits() schema.RateLimits {
454+
rl := schema.RateLimits{
455+
GraphQLMaxAliases: 500,
456+
GraphQLMaxFieldCount: 500_000,
457+
GraphQLMaxDepth: 30,
458+
}
459+
460+
configured := Get().RateLimits
461+
462+
if configured != nil {
463+
if configured.GraphQLMaxAliases <= 0 {
464+
rl.GraphQLMaxAliases = configured.GraphQLMaxAliases
465+
}
466+
if configured.GraphQLMaxFieldCount <= 0 {
467+
rl.GraphQLMaxFieldCount = configured.GraphQLMaxFieldCount
468+
}
469+
if configured.GraphQLMaxDepth <= 0 {
470+
rl.GraphQLMaxDepth = configured.GraphQLMaxDepth
471+
}
472+
}
473+
return rl
474+
}
475+
453476
// By default, password reset links are valid for 4 hours.
454477
const defaultPasswordLinkExpiry = 14400
455478

schema/schema.go

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schema/site.schema.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@
4949
"type": "boolean",
5050
"default": false
5151
},
52+
"rateLimits": {
53+
"type": "object",
54+
"additionalProperties": false,
55+
"properties": {
56+
"graphQLMaxDepth": {
57+
"description": "Maximum depth of nested objects allowed for GraphQL queries. Changes to this setting require a restart.",
58+
"type": "integer",
59+
"default": 30
60+
},
61+
"graphQLMaxAliases": {
62+
"description": "Maximum number of aliases allowed in a GraphQL query",
63+
"type": "integer",
64+
"default": 500
65+
},
66+
"graphQLMaxFieldCount": {
67+
"description": "Maximum number of estimated fields allowed in a GraphQL response",
68+
"type": "integer",
69+
"default": 500000
70+
}
71+
}
72+
},
5273
"exportUsageTelemetry": {
5374
"type": "object",
5475
"additionalProperties": false,

0 commit comments

Comments
 (0)