snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
55
internal/check/step/call_limit.go
Normal file
55
internal/check/step/call_limit.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright © 2026 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package step
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/ory/keto/internal/check"
|
||||
)
|
||||
|
||||
type callBudgetKeyType struct{}
|
||||
|
||||
var callBudgetKey callBudgetKeyType
|
||||
|
||||
// maxCallSteps bounds the number of steps along a single resolution path. Most
|
||||
// recursion is already bounded by RestDepth, but same-object computed hops
|
||||
// (ComputedUsersetStep, InvertStep) don't decrement it, so a cyclic config
|
||||
// (e.g. two permits that reference each other) would otherwise recurse until
|
||||
// the stack overflows and crashes the process. This is a blanket per-path
|
||||
// ceiling, well above any real config and well below a stack overflow.
|
||||
const maxCallSteps = 1000
|
||||
|
||||
func errCallBudgetExceeded() error {
|
||||
return errors.WithStack(&herodot.DefaultError{
|
||||
StatusField: http.StatusText(http.StatusUnprocessableEntity),
|
||||
ErrorField: "The check could not be completed",
|
||||
ReasonField: "The check was aborted after too many resolution steps. This usually indicates a cyclic permit reference in the namespace configuration.",
|
||||
CodeField: http.StatusUnprocessableEntity,
|
||||
GRPCCodeField: codes.FailedPrecondition,
|
||||
})
|
||||
}
|
||||
|
||||
// CallLimitMiddleware aborts a check once any single resolution path exceeds
|
||||
// maxCallSteps. It returns a decisive error so the whole check stops
|
||||
// immediately, rather than a limitation (which is non-decisive and would let a
|
||||
// cyclic config keep exploring exponentially many branches). The budget is
|
||||
// carried per-path in the context and seeded lazily.
|
||||
func CallLimitMiddleware() check.Middleware {
|
||||
return func(ctx context.Context, s check.Step, req check.CheckRequest, next func(context.Context, check.Step, check.CheckRequest) check.Result) check.Result {
|
||||
remaining, ok := ctx.Value(callBudgetKey).(int)
|
||||
if !ok {
|
||||
remaining = maxCallSteps
|
||||
}
|
||||
if remaining <= 0 {
|
||||
return check.Result{Err: errCallBudgetExceeded()}
|
||||
}
|
||||
return next(context.WithValue(ctx, callBudgetKey, remaining-1), s, req)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user