mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-06 07:12:46 +01:00
187 lines
3.6 KiB
Go
187 lines
3.6 KiB
Go
package database
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"sort"
|
|
|
|
"github.com/SecurityBrewery/catalyst/caql"
|
|
"github.com/SecurityBrewery/catalyst/dag"
|
|
"github.com/SecurityBrewery/catalyst/generated/model"
|
|
)
|
|
|
|
func playbookGraph(playbook *model.Playbook) (*dag.Graph, error) {
|
|
d := dag.NewGraph()
|
|
|
|
var taskIDs []string
|
|
for taskID := range playbook.Tasks {
|
|
taskIDs = append(taskIDs, taskID)
|
|
}
|
|
sort.Strings(taskIDs)
|
|
|
|
for _, taskID := range taskIDs {
|
|
if err := d.AddNode(taskID); err != nil {
|
|
return nil, errors.New("could not add node")
|
|
}
|
|
}
|
|
for _, taskID := range taskIDs {
|
|
task := playbook.Tasks[taskID]
|
|
for next := range task.Next {
|
|
if err := d.AddEdge(taskID, next); err != nil {
|
|
return nil, errors.New("could not add edge")
|
|
}
|
|
}
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
func toTaskResponse(playbook *model.Playbook, taskID string, order int, graph *dag.Graph) (*model.TaskResponse, error) {
|
|
task, ok := playbook.Tasks[taskID]
|
|
if !ok {
|
|
return nil, fmt.Errorf("task %s not found", taskID)
|
|
}
|
|
|
|
tr := &model.TaskResponse{
|
|
Automation: task.Automation,
|
|
Closed: task.Closed,
|
|
Created: task.Created,
|
|
Data: task.Data,
|
|
Done: task.Done,
|
|
Join: task.Join,
|
|
Payload: task.Payload,
|
|
Name: task.Name,
|
|
Next: task.Next,
|
|
Owner: task.Owner,
|
|
Schema: task.Schema,
|
|
Type: task.Type,
|
|
// Active: active,
|
|
// Order: v.Order,
|
|
}
|
|
|
|
tr.Order = int64(order)
|
|
|
|
taskActive, _ := active(playbook, taskID, graph, task)
|
|
tr.Active = taskActive
|
|
|
|
return tr, nil
|
|
}
|
|
|
|
func activePlaybook(playbook *model.Playbook, taskID string) (bool, error) {
|
|
task, ok := playbook.Tasks[taskID]
|
|
if !ok {
|
|
return false, fmt.Errorf("playbook does not contain tasks %s", taskID)
|
|
}
|
|
|
|
d, err := playbookGraph(playbook)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return active(playbook, taskID, d, task)
|
|
}
|
|
|
|
func active(playbook *model.Playbook, taskID string, d *dag.Graph, task *model.Task) (bool, error) {
|
|
if task.Done {
|
|
return false, nil
|
|
}
|
|
|
|
parents := d.GetParents(taskID)
|
|
|
|
if len(parents) == 0 {
|
|
return true, nil // valid(&task)
|
|
}
|
|
|
|
if task.Join != nil && *task.Join {
|
|
for _, parent := range parents {
|
|
parentTask := playbook.Tasks[parent]
|
|
if !parentTask.Done {
|
|
return false, nil
|
|
}
|
|
requirement := parentTask.Next[taskID]
|
|
|
|
b, err := evalRequirement(requirement, parentTask.Data)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !b {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
for _, parent := range parents {
|
|
parentTask := playbook.Tasks[parent]
|
|
if !parentTask.Done {
|
|
// return false, nil
|
|
continue
|
|
}
|
|
requirement := parentTask.Next[taskID]
|
|
|
|
b, err := evalRequirement(requirement, parentTask.Data)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if b {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func evalRequirement(aql string, data any) (bool, error) {
|
|
if aql == "" {
|
|
return true, nil
|
|
}
|
|
|
|
parser := caql.Parser{}
|
|
tree, err := parser.Parse(aql)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var dataMap map[string]any
|
|
if data != nil {
|
|
if dataMapX, ok := data.(map[string]any); ok {
|
|
dataMap = dataMapX
|
|
} else {
|
|
log.Println("wrong data type for task data")
|
|
}
|
|
}
|
|
|
|
v, err := tree.Eval(dataMap)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if b, ok := v.(bool); ok {
|
|
return b, nil
|
|
}
|
|
|
|
return false, err
|
|
}
|
|
|
|
/*
|
|
// "github.com/qri-io/jsonschema"
|
|
func valid(task *model.Task) (bool, error) {
|
|
schema, err := json.Marshal(task.Schema)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
rs := &jsonschema.Schema{}
|
|
if err := json.Unmarshal(schema, rs); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
state := rs.Validate(context.Background(), task.Data)
|
|
return len(*state.Errs) > 0, nil
|
|
}
|
|
*/
|