mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:07:30 +00:00
fix: actions v2 circular check for includes (#7563)
Add a check for circular includes in action v2 executions, so that no self-includes or infinite loops can happen. Closes #7445 ### Definition of Ready - [x] I am happy with the code - [x] Short description of the feature/issue is added in the pr description - [x] PR is linked to the corresponding user story - [x] Acceptance criteria are met - [x] All open todos and follow ups are defined in a new ticket and justified - [x] Deviations from the acceptance criteria and design are agreed with the PO and documented. - [x] No debug or dead code - [x] My code has no repetitions - [x] Critical parts are tested automatically - [x] Where possible E2E tests are implemented - [x] Documentation/examples are up-to-date - [x] All non-functional requirements are met - [x] Functionality of the acceptance criteria is checked manually on the dev system. --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -216,7 +216,9 @@ func (e *SetExecution) Existing(c *Commands, ctx context.Context, resourceOwner
|
||||
if len(includes) > 0 && !c.existsExecutionsByIDs(ctx, includes, resourceOwner) {
|
||||
return zerrors.ThrowNotFound(nil, "COMMAND-slgj0l4cdz", "Errors.Execution.IncludeNotFound")
|
||||
}
|
||||
return nil
|
||||
get, set := createIncludeCacheFunctions()
|
||||
// maxLevels could be configurable, but set as 3 for now
|
||||
return checkForIncludeCircular(ctx, e.AggregateID, resourceOwner, includes, c.getExecutionIncludes(get, set), 3)
|
||||
}
|
||||
|
||||
func (c *Commands) setExecution(ctx context.Context, set *SetExecution, resourceOwner string) (_ *domain.ObjectDetails, err error) {
|
||||
@@ -309,3 +311,75 @@ func (c *Commands) getExecutionWriteModelByID(ctx context.Context, id string, re
|
||||
}
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
func createIncludeCacheFunctions() (func(s string) ([]string, bool), func(s string, strings []string)) {
|
||||
tempCache := make(map[string][]string)
|
||||
return func(s string) ([]string, bool) {
|
||||
include, ok := tempCache[s]
|
||||
return include, ok
|
||||
}, func(s string, strings []string) {
|
||||
tempCache[s] = strings
|
||||
}
|
||||
}
|
||||
|
||||
type includeCacheFunc func(ctx context.Context, id string, resourceOwner string) ([]string, error)
|
||||
|
||||
func checkForIncludeCircular(ctx context.Context, id string, resourceOwner string, includes []string, cache includeCacheFunc, maxLevels int) error {
|
||||
if len(includes) == 0 {
|
||||
return nil
|
||||
}
|
||||
level := 0
|
||||
for _, include := range includes {
|
||||
if id == include {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-mo1cmjp5k7", "Errors.Execution.CircularInclude")
|
||||
}
|
||||
if err := checkForIncludeCircularRecur(ctx, []string{id}, resourceOwner, include, cache, maxLevels, level); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) getExecutionIncludes(
|
||||
getCache func(string) ([]string, bool),
|
||||
setCache func(string, []string),
|
||||
) includeCacheFunc {
|
||||
return func(ctx context.Context, id string, resourceOwner string) ([]string, error) {
|
||||
included, ok := getCache(id)
|
||||
if !ok {
|
||||
included, err := c.getExecutionWriteModelByID(ctx, id, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
includes := included.IncludeList()
|
||||
setCache(id, includes)
|
||||
return includes, nil
|
||||
}
|
||||
return included, nil
|
||||
}
|
||||
}
|
||||
|
||||
func checkForIncludeCircularRecur(ctx context.Context, ids []string, resourceOwner string, include string, cache includeCacheFunc, maxLevels, level int) error {
|
||||
included, err := cache(ctx, include, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentLevel := level + 1
|
||||
if currentLevel >= maxLevels {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-gbhd3g57oo", "Errors.Execution.MaxLevelsInclude")
|
||||
}
|
||||
for _, includedInclude := range included {
|
||||
if include == includedInclude {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-iuch02i656", "Errors.Execution.CircularInclude")
|
||||
}
|
||||
for _, id := range ids {
|
||||
if includedInclude == id {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-819opvhgjv", "Errors.Execution.CircularInclude")
|
||||
}
|
||||
}
|
||||
if err := checkForIncludeCircularRecur(ctx, append(ids, include), resourceOwner, includedInclude, cache, maxLevels, currentLevel); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user