Check input schema (#33)

This commit is contained in:
Jonas Plum
2022-02-27 12:25:41 +01:00
committed by GitHub
parent 54312893a2
commit 338aba8342
38 changed files with 3221 additions and 1676 deletions

View File

@@ -47,14 +47,12 @@ func (h *busService) handleJob(automationMsg *bus.JobMsg) {
return
}
if _, err := h.db.JobUpdate(ctx, automationMsg.ID, &model.Job{
Automation: job.Automation,
Container: &containerID,
Origin: job.Origin,
Output: job.Output,
Log: &logs,
Payload: job.Payload,
Status: job.Status,
if _, err := h.db.JobUpdate(ctx, automationMsg.ID, &model.JobUpdate{
Container: &containerID,
Running: true,
Output: job.Output,
Log: &logs,
Status: job.Status,
}); err != nil {
log.Println(err)
return

View File

@@ -5,11 +5,9 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/arangodb/go-driver"
"github.com/docker/docker/client"
"github.com/xeipuuv/gojsonschema"
"github.com/SecurityBrewery/catalyst/bus"
"github.com/SecurityBrewery/catalyst/caql"
@@ -39,15 +37,19 @@ func (db *Database) toJobResponse(ctx context.Context, key string, doc *model.Jo
if doc.Running {
inspect, err := cli.ContainerInspect(ctx, key)
if err != nil || inspect.State == nil {
doc.Running = false
if update {
db.JobUpdate(ctx, key, doc)
db.JobUpdate(ctx, key, &model.JobUpdate{
Status: doc.Status,
Running: false,
})
}
} else if doc.Status != inspect.State.Status {
status = inspect.State.Status
doc.Status = inspect.State.Status
if update {
db.JobUpdate(ctx, key, doc)
db.JobUpdate(ctx, key, &model.JobUpdate{
Status: status,
Running: doc.Running,
})
}
}
}
@@ -72,25 +74,7 @@ func (db *Database) JobCreate(ctx context.Context, id string, job *model.JobForm
var doc model.Job
newctx := driver.WithReturnNew(ctx, &doc)
/* Start validation */
j := toJob(job)
b, _ := json.Marshal(j)
r, err := model.JobSchema.Validate(gojsonschema.NewBytesLoader(b))
if err != nil {
return nil, err
}
if !r.Valid() {
var errs []string
for _, e := range r.Errors() {
errs = append(errs, e.String())
}
return nil, errors.New(strings.Join(errs, ", "))
}
/* End validation */
meta, err := db.jobCollection.CreateDocument(ctx, newctx, id, j)
meta, err := db.jobCollection.CreateDocument(ctx, newctx, id, toJob(job))
if err != nil {
return nil, err
}
@@ -108,28 +92,11 @@ func (db *Database) JobGet(ctx context.Context, id string) (*model.JobResponse,
return db.toJobResponse(ctx, meta.Key, &doc, true)
}
func (db *Database) JobUpdate(ctx context.Context, id string, job *model.Job) (*model.JobResponse, error) {
func (db *Database) JobUpdate(ctx context.Context, id string, job *model.JobUpdate) (*model.JobResponse, error) {
var doc model.Job
ctx = driver.WithReturnNew(ctx, &doc)
/* Start validation */
b, _ := json.Marshal(job)
r, err := model.JobSchema.Validate(gojsonschema.NewBytesLoader(b))
if err != nil {
return nil, err
}
if !r.Valid() {
var errs []string
for _, e := range r.Errors() {
errs = append(errs, e.String())
}
return nil, errors.New(strings.Join(errs, ", "))
}
/* End validation */
meta, err := db.jobCollection.ReplaceDocument(ctx, id, job)
meta, err := db.jobCollection.UpdateDocument(ctx, id, job)
if err != nil {
return nil, err
}

View File

@@ -113,18 +113,18 @@ func extractTicketResponse(ticket *model.TicketWithTickets) *model.TicketRespons
}
}
func (db *Database) TaskUpdate(ctx context.Context, id int64, playbookID string, taskID string, task *model.Task) (*model.TicketWithTickets, error) {
func (db *Database) TaskUpdateOwner(ctx context.Context, id int64, playbookID string, taskID string, owner string) (*model.TicketWithTickets, error) {
ticketFilterQuery, ticketFilterVars, err := db.Hooks.TicketWriteFilter(ctx)
if err != nil {
return nil, err
}
task.Created = time.Now().UTC()
query := `LET d = DOCUMENT(@@collection, @ID)
` + ticketFilterQuery + `
LET playbook = d.playbooks[@playbookID]
LET newtasks = MERGE(playbook.tasks, { @taskID: @task } )
LET task = playbook.tasks[@taskID]
LET newtask = MERGE(task, {"owner": @owner })
LET newtasks = MERGE(playbook.tasks, { @taskID: newtask } )
LET newplaybook = MERGE(playbook, {"tasks": newtasks})
LET newplaybooks = MERGE(d.playbooks, { @playbookID: newplaybook } )
@@ -133,7 +133,42 @@ func (db *Database) TaskUpdate(ctx context.Context, id int64, playbookID string,
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
"playbookID": playbookID,
"taskID": taskID,
"task": task,
"owner": owner,
"now": time.Now().UTC(),
}, ticketFilterVars), &busdb.Operation{
Type: bus.DatabaseEntryUpdated,
Ids: []driver.DocumentID{
driver.NewDocumentID(TicketCollectionName, fmt.Sprintf("%d", id)),
},
})
if err != nil {
return nil, err
}
return ticket, nil
}
func (db *Database) TaskUpdateData(ctx context.Context, id int64, playbookID string, taskID string, data map[string]interface{}) (*model.TicketWithTickets, error) {
ticketFilterQuery, ticketFilterVars, err := db.Hooks.TicketWriteFilter(ctx)
if err != nil {
return nil, err
}
query := `LET d = DOCUMENT(@@collection, @ID)
` + ticketFilterQuery + `
LET playbook = d.playbooks[@playbookID]
LET task = playbook.tasks[@taskID]
LET newtask = MERGE(task, {"data": @data })
LET newtasks = MERGE(playbook.tasks, { @taskID: newtask } )
LET newplaybook = MERGE(playbook, {"tasks": newtasks})
LET newplaybooks = MERGE(d.playbooks, { @playbookID: newplaybook } )
UPDATE d WITH { "modified": @now, "playbooks": newplaybooks } IN @@collection
RETURN NEW`
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
"playbookID": playbookID,
"taskID": taskID,
"data": data,
"now": time.Now().UTC(),
}, ticketFilterVars), &busdb.Operation{
Type: bus.DatabaseEntryUpdated,

View File

@@ -191,6 +191,8 @@ func (db *Database) UserUpdate(ctx context.Context, id string, user *model.UserF
ctx = driver.WithReturnNew(ctx, &doc)
user.ID = id
meta, err := db.userCollection.ReplaceDocument(ctx, id, toUser(user, nil))
if err != nil {
return nil, err

View File

@@ -48,7 +48,7 @@ paths:
operationId: "updateJob"
parameters:
- { name: "id", in: "path", description: "Job ID", required: true, type: string, x-example: "99cd67131b48" }
- { name: "job", in: "body", description: "Job object that needs to be added", required: true, schema: { $ref: "#/definitions/Job" }, x-example: { id: "99cd67131b48", automation: "hash.sha1", payload: "test", status: "failed" } }
- { name: "job", in: "body", description: "Job object that needs to be added", required: true, schema: { $ref: "#/definitions/JobUpdate" }, x-example: { status: "failed", running: false } }
responses:
"200":
description: "successful operation"
@@ -103,6 +103,16 @@ definitions:
payload: { }
origin: { $ref: "#/definitions/Origin" }
JobUpdate:
type: object
required: [ running, status ]
properties:
container: { type: string }
running: { type: boolean }
status: { type: string }
log: { type: string }
output: { type: object }
Job:
type: object
required: [ automation, running, status ]

View File

@@ -16,31 +16,6 @@ paths:
security: [ { roles: [ "ticket:read" ] } ]
definitions:
TaskForm:
type: object
required: [ name, type ]
properties:
name: { type: string, example: "Inform user" }
type: { type: string, enum: [ task, input, automation ], example: "task" }
done: { type: boolean }
owner: { type: string }
data: { type: object }
# automation
automation: { type: string }
payload: { type: object, additionalProperties: { type: string } }
# input
schema: { type: object }
# workflow
join: { type: boolean, example: false }
next: { type: object, additionalProperties: { type: string } }
created: { type: string, format: "date-time", example: "1985-04-12T23:20:50.52Z" }
closed: { type: string, format: "date-time", example: "1985-04-12T23:20:50.52Z" }
Task:
type: object
required: [ name, type, done, created ]

View File

@@ -80,7 +80,7 @@ paths:
summary: "Create a new tickets in batch"
operationId: "createTicketBatch"
parameters:
- { name: "ticket", in: "body", description: "New ticket", required: true, schema: { type: array, items: { $ref: "#/definitions/TicketForm" } }, x-example: [ { id: 123, owner: bob, name: "Wannacry infection", status: "open", type: "incident" } ] }
- { name: "ticket", in: "body", description: "New ticket", required: true, schema: { $ref: "#/definitions/TicketFormArray" }, x-example: [ { id: 123, owner: bob, name: "Wannacry infection", status: "open", type: "incident" } ] }
responses:
"204": { description: "successful operation" }
security: [ { roles: [ "ticket:write" ] } ]
@@ -336,7 +336,7 @@ paths:
operationId: "setReferences"
parameters:
- { name: "id", in: "path", description: "Ticket ID", required: true, type: integer, format: "int64", x-example: 8125 }
- { name: "references", in: "body", description: "All ticket references", required: true, schema: { type: array, items: { $ref: "#/definitions/Reference" } }, x-example: [ { href: "http://www.leadscalable.biz/envisioneer", name: "fund" } ] }
- { name: "references", in: "body", description: "All ticket references", required: true, schema: { $ref: "#/definitions/ReferenceArray" }, x-example: [ { href: "http://www.leadscalable.biz/envisioneer", name: "fund" } ] }
responses:
"200":
description: "successful operation"
@@ -583,13 +583,13 @@ paths:
/tickets/{id}/playbooks/{playbookID}/task/{taskID}:
put:
tags: [ "tickets" ]
summary: "Set a ticket playbook task"
operationId: "setTask"
summary: "Set a ticket playbook task data"
operationId: "setTaskData"
parameters:
- { name: "id", in: "path", description: "Ticket ID", required: true, type: integer, format: "int64", x-example: 8123 }
- { name: "playbookID", in: "path", description: "Playbook ID", required: true, type: string, x-example: "phishing" }
- { name: "taskID", in: "path", description: "Task ID", required: true, type: string, x-example: "board" }
- { name: "task", in: "body", description: "Task", required: true, schema: { $ref: "#/definitions/Task" }, x-example: { done: false, "active": true, "order": 0, name: "Board Involvement?","next": { "escalate": "boardInvolved == true","mail-available": "boardInvolved == false" },"schema": { "properties": { "boardInvolved": { "default": false, "title": "A board member is involved.", type: "boolean" } }, "required": [ "boardInvolved" ], "title": "Board Involvement?", type: "object" }, type: "input", data: { boardInvolved: true } } }
- { name: "data", in: "body", description: "Task data", required: true, schema: { type: object }, x-example: { boardInvolved: true } }
responses:
"200":
description: "successful operation"
@@ -625,6 +625,51 @@ paths:
- { name: "leadreintermediate.io", status: "malicious" }
security: [ { roles: [ "ticket:write" ] } ]
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/owner:
put:
tags: [ "tickets" ]
summary: "Set a ticket playbook task owner"
operationId: "setTaskOwner"
parameters:
- { name: "id", in: "path", description: "Ticket ID", required: true, type: integer, format: "int64", x-example: 8123 }
- { name: "playbookID", in: "path", description: "Playbook ID", required: true, type: string, x-example: "phishing" }
- { name: "taskID", in: "path", description: "Task ID", required: true, type: string, x-example: "board" }
- { name: "owner", in: "body", description: "Task owner", required: true, schema: { type: string }, x-example: "eve" }
responses:
"200":
description: "successful operation"
schema: { $ref: "#/definitions/TicketWithTickets" }
examples:
test:
id: 8123
created: "2021-10-02T16:04:59.078206Z"
modified: "2021-12-12T12:12:12.000000012Z"
name: "live zebra"
owner: "demo"
playbooks:
phishing:
name: "Phishing"
tasks:
"block-iocs": { created: "2021-12-12T12:12:12.000000012Z", done: false, "active": false, "order": 6, name: "Block IOCs", type: "task" }
"block-sender": { created: "2021-12-12T12:12:12.000000012Z", done: false, "active": false, "order": 3, name: "Block sender","next": { "extract-iocs": "" }, type: "task" }
"board": { created: "2021-12-12T12:12:12.000000012Z", done: false, "active": true, "order": 0, name: "Board Involvement?","next": { "escalate": "boardInvolved == true","mail-available": "boardInvolved == false" },"schema": { "properties": { "boardInvolved": { "default": false, "title": "A board member is involved.", type: "boolean" } }, "required": [ "boardInvolved" ], "title": "Board Involvement?", type: "object" }, type: "input", owner: "eve" }
"escalate": { created: "2021-12-12T12:12:12.000000012Z", done: false, "active": false, "order": 1, name: "Escalate to CISO", type: "task" }
"extract-iocs": { created: "2021-12-12T12:12:12.000000012Z", done: false, "active": false, "order": 5, name: "Extract IOCs", "next": { "block-iocs": "" },"schema": { "properties": { "iocs": { "items": { type: "string" },"title": "IOCs", type: "array" } }, "title": "Extract IOCs", type: "object" }, type: "input" }
"mail-available": { created: "2021-12-12T12:12:12.000000012Z", done: false, "active": false, "order": 2, name: "Mail available","next": { "block-sender": "schemaKey == 'yes'", "extract-iocs": "schemaKey == 'yes'", "search-email-gateway": "schemaKey == 'no'" },"schema": { "oneOf": [ { "properties": { "mail": { "title": "Mail", type: "string", "x-display": "textarea" }, "schemaKey": { "const": "yes", type: "string" } },"required": [ "mail" ], "title": "Yes" },{ "properties": { "schemaKey": { "const": "no", type: "string" } },"title": "No" } ],"title": "Mail available", type: "object" }, type: "input" }
"search-email-gateway": { created: "2021-12-12T12:12:12.000000012Z", done: false, "active": false, "order": 4, name: "Search email gateway","next": { "extract-iocs": "" }, type: "task" }
references:
- { href: "https://www.leadmaximize.net/e-services/back-end", name: "performance" }
- { href: "http://www.corporateinteractive.name/rich", name: "autumn" }
- { href: "https://www.corporateintuitive.org/intuitive/platforms/integrate", name: "suggest" }
"schema": "{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Event\",\n \"type\": \"object\",\n \"required\": [\n \"severity\",\n \"description\",\n \"tlp\"\n ],\n \"properties\": {\n \"severity\": {\n \"$id\": \"#root/severity\",\n \"title\": \"Severity\",\n \"type\": \"string\",\n \"default\": \"Medium\",\n \"nx-enum\": [\n \"Low\",\n \"Medium\",\n \"High\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"Low\",\n \"title\": \"Low\",\n \"icon\": \"mdi-chevron-up\"\n },\n {\n \"const\": \"Medium\",\n \"title\": \"Medium\",\n \"icon\": \"mdi-chevron-double-up\"\n },\n {\n \"const\": \"High\",\n \"title\": \"High\",\n \"icon\": \"mdi-chevron-triple-up\"\n }\n ]\n },\n \"tlp\": {\n \"$id\": \"#root/tlp\",\n \"title\": \"TLP\",\n \"type\": \"string\",\n \"nx-enum\": [\n \"White\",\n \"Green\",\n \"Amber\",\n \"Red\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"White\",\n \"title\": \"White\",\n \"icon\": \"mdi-alpha-w\"\n },\n {\n \"const\": \"Green\",\n \"title\": \"Green\",\n \"icon\": \"mdi-alpha-g\"\n },\n {\n \"const\": \"Amber\",\n \"title\": \"Amber\",\n \"icon\": \"mdi-alpha-a\"\n },\n {\n \"const\": \"Red\",\n \"title\": \"Red\",\n \"icon\": \"mdi-alpha-r\"\n }\n ]\n },\n \"description\": {\n \"$id\": \"#root/description\",\n \"title\": \"Description\",\n \"type\": \"string\",\n \"x-display\": \"textarea\",\n \"x-class\": \"pr-2\"\n }\n }\n}\n"
status: "closed"
type: "incident"
artifacts:
- { name: "94d5cab6f5fe3422a447ab15436e7a672bc0c09a", status: "unknown" }
- { name: "http://www.customerviral.io/scalable/vertical/killer", status: "clean" }
- { name: "leadreintermediate.io", status: "malicious" }
security: [ { roles: [ "ticket:write" ] } ]
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/complete:
put:
tags: [ "tickets" ]
@@ -883,6 +928,9 @@ paths:
security: [ { roles: [ "ticket:write" ] } ]
definitions:
TicketFormArray:
type: array
items: { $ref: "#/definitions/TicketForm" }
TicketForm:
type: object
@@ -1035,6 +1083,10 @@ definitions:
created: { type: string, format: "date-time" }
message: { type: string }
ReferenceArray:
type: array
items: { $ref: '#/definitions/Reference' }
Reference:
type: object
required: [ name, href ]

View File

@@ -34,7 +34,7 @@ paths:
summary: "Create user"
operationId: "createUser"
parameters:
- { name: "user", in: "body", description: "user object that needs to be added", required: true, schema: { $ref: "#/definitions/UserForm" }, x-example: { id: "syncscript", roles: [ "analyst" ] } }
- { name: "user", in: "body", description: "user object that needs to be added", required: true, schema: { $ref: "#/definitions/UserForm" }, x-example: { id: "syncscript", roles: [ "analyst" ], blocked: false, apikey: true } }
responses:
"200":
description: "successful operation"
@@ -62,7 +62,7 @@ paths:
operationId: "updateUser"
parameters:
- { name: "id", in: "path", description: "Template ID", required: true, type: string, x-example: "bob" }
- { name: "user", in: "body", description: "user object that needs to be added", required: true, schema: { $ref: "#/definitions/UserForm" }, x-example: { roles: [ "analyst", "admin" ] } }
- { name: "user", in: "body", description: "user object that needs to be added", required: true, schema: { $ref: "#/definitions/UserForm" }, x-example: { id: "syncscript", roles: [ "analyst", "admin" ], blocked: false, apikey: false } }
responses:
"200":
description: "successful operation"

View File

@@ -1,4 +1,4 @@
version: '2.2'
version: '2.4'
services:
nginx:
image: nginx:1.21
@@ -38,6 +38,7 @@ services:
keycloak:
image: quay.io/keycloak/keycloak:14.0.0
platform: linux/amd64
environment:
DB_VENDOR: POSTGRES
DB_ADDR: postgres

View File

@@ -18,12 +18,8 @@ mv generated/openapi.json generated/community.json
openapi-generator generate -i generated/catalyst.yml -o generated -g openapi
mv generated/openapi.json generated/catalyst.json
# generate python client
# openapi-generator generate -i generated/community.yml -o generated/python -g python --package-name catalystpy --ignore-file-override .openapi-generator-ignore
echo generate server and tests
# go run ./generator/. ./generator
swachigo generated/community.yml generated
swagger-go-chi generated/community.yml generated
echo generate typescript client
openapi-generator generate -i generated/catalyst.yml -o ui/src/client -g typescript-axios --artifact-version 1.0.0-SNAPSHOT
@@ -31,8 +27,7 @@ openapi-generator generate -i generated/catalyst.yml -o ui/src/client -g typescr
rm -rf gen
rm -rf generated/models/old
rm -rf generated/.openapi-generator generated/.openapi-generator-ignore generated/README.md
# rm -rf generated/python/.openapi-generator generated/python/.gitlab-ci.yml generated/python/git_push.sh generated/python/.travis.yml generated/python/.gitignore generated/python/.openapi-generator-ignore
rm -rf ui/src/client/.openapi-generator ui/src/client/git_push.sh ui/src/client/.gitignore ui/src/client/.openapi-generator-ignore
go mod tidy
gci -w -local "github.com/SecurityBrewery/catalyst" .
gci write --Section Standard --Section Default --Section "Prefix(github.com/SecurityBrewery/catalyst)" .

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
// Code generated from CAQLLexer.g4 by ANTLR 4.9.2. DO NOT EDIT.
// Code generated from CAQLLexer.g4 by ANTLR 4.9.3. DO NOT EDIT.
package parser

View File

@@ -1,4 +1,4 @@
// Code generated from CAQLParser.g4 by ANTLR 4.9.2. DO NOT EDIT.
// Code generated from CAQLParser.g4 by ANTLR 4.9.3. DO NOT EDIT.
package parser // CAQLParser
@@ -332,6 +332,9 @@ func (s *ParseContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Parse() (localctx IParseContext) {
this := p
_ = this
localctx = NewParseContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 0, CAQLParserRULE_parse)
@@ -587,6 +590,9 @@ func (p *CAQLParser) Expression() (localctx IExpressionContext) {
}
func (p *CAQLParser) expression(_p int) (localctx IExpressionContext) {
this := p
_ = this
var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext()
_parentState := p.GetState()
localctx = NewExpressionContext(p, p.GetParserRuleContext(), _parentState)
@@ -1076,6 +1082,9 @@ func (s *Operator_unaryContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Operator_unary() (localctx IOperator_unaryContext) {
this := p
_ = this
localctx = NewOperator_unaryContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 4, CAQLParserRULE_operator_unary)
@@ -1264,6 +1273,9 @@ func (p *CAQLParser) Reference() (localctx IReferenceContext) {
}
func (p *CAQLParser) reference(_p int) (localctx IReferenceContext) {
this := p
_ = this
var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext()
_parentState := p.GetState()
localctx = NewReferenceContext(p, p.GetParserRuleContext(), _parentState)
@@ -1469,6 +1481,9 @@ func (s *Compound_valueContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Compound_value() (localctx ICompound_valueContext) {
this := p
_ = this
localctx = NewCompound_valueContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 8, CAQLParserRULE_compound_value)
@@ -1614,6 +1629,9 @@ func (s *Function_callContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Function_call() (localctx IFunction_callContext) {
this := p
_ = this
localctx = NewFunction_callContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 10, CAQLParserRULE_function_call)
var _la int
@@ -1778,6 +1796,9 @@ func (s *Value_literalContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Value_literal() (localctx IValue_literalContext) {
this := p
_ = this
localctx = NewValue_literalContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 12, CAQLParserRULE_value_literal)
var _la int
@@ -1912,6 +1933,9 @@ func (s *ArrayContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Array() (localctx IArrayContext) {
this := p
_ = this
localctx = NewArrayContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 14, CAQLParserRULE_array)
var _la int
@@ -2087,6 +2111,9 @@ func (s *ObjectContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Object() (localctx IObjectContext) {
this := p
_ = this
localctx = NewObjectContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 16, CAQLParserRULE_object)
var _la int
@@ -2272,6 +2299,9 @@ func (s *Object_elementContext) ExitRule(listener antlr.ParseTreeListener) {
}
func (p *CAQLParser) Object_element() (localctx IObject_elementContext) {
this := p
_ = this
localctx = NewObject_elementContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 18, CAQLParserRULE_object_element)
@@ -2409,6 +2439,9 @@ func (s *Object_element_nameContext) ExitRule(listener antlr.ParseTreeListener)
}
func (p *CAQLParser) Object_element_name() (localctx IObject_element_nameContext) {
this := p
_ = this
localctx = NewObject_element_nameContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 20, CAQLParserRULE_object_element_name)
var _la int
@@ -2467,6 +2500,9 @@ func (p *CAQLParser) Sempred(localctx antlr.RuleContext, ruleIndex, predIndex in
}
func (p *CAQLParser) Expression_Sempred(localctx antlr.RuleContext, predIndex int) bool {
this := p
_ = this
switch predIndex {
case 0:
return p.Precpred(p.GetParserRuleContext(), 13)
@@ -2513,6 +2549,9 @@ func (p *CAQLParser) Expression_Sempred(localctx antlr.RuleContext, predIndex in
}
func (p *CAQLParser) Reference_Sempred(localctx antlr.RuleContext, predIndex int) bool {
this := p
_ = this
switch predIndex {
case 13:
return p.Precpred(p.GetParserRuleContext(), 2)

View File

@@ -1,4 +1,4 @@
// Code generated from CAQLParser.g4 by ANTLR 4.9.2. DO NOT EDIT.
// Code generated from CAQLParser.g4 by ANTLR 4.9.3. DO NOT EDIT.
package parser // CAQLParser

View File

@@ -1,4 +1,4 @@
// Code generated from CAQLParser.g4 by ANTLR 4.9.2. DO NOT EDIT.
// Code generated from CAQLParser.g4 by ANTLR 4.9.3. DO NOT EDIT.
package parser // CAQLParser

View File

@@ -659,7 +659,7 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Job"
"$ref" : "#/components/schemas/JobUpdate"
}
}
},
@@ -3404,7 +3404,7 @@
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}" : {
"put" : {
"operationId" : "setTask",
"operationId" : "setTaskData",
"parameters" : [ {
"description" : "Ticket ID",
"example" : 8123,
@@ -3438,11 +3438,11 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Task"
"type" : "object"
}
}
},
"description" : "Task",
"description" : "Task data",
"required" : true
},
"responses" : {
@@ -3628,9 +3628,9 @@
"security" : [ {
"roles" : [ "ticket:write" ]
} ],
"summary" : "Set a ticket playbook task",
"summary" : "Set a ticket playbook task data",
"tags" : [ "tickets" ],
"x-codegen-request-body-name" : "task"
"x-codegen-request-body-name" : "data"
}
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}/complete" : {
@@ -3865,6 +3865,235 @@
"x-codegen-request-body-name" : "data"
}
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}/owner" : {
"put" : {
"operationId" : "setTaskOwner",
"parameters" : [ {
"description" : "Ticket ID",
"example" : 8123,
"in" : "path",
"name" : "id",
"required" : true,
"schema" : {
"format" : "int64",
"type" : "integer"
}
}, {
"description" : "Playbook ID",
"example" : "phishing",
"in" : "path",
"name" : "playbookID",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"description" : "Task ID",
"example" : "board",
"in" : "path",
"name" : "taskID",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"type" : "string"
}
}
},
"description" : "Task owner",
"required" : true
},
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/TicketWithTickets"
}
},
"test" : {
"example" : {
"artifacts" : [ {
"name" : "94d5cab6f5fe3422a447ab15436e7a672bc0c09a",
"status" : "unknown"
}, {
"name" : "http://www.customerviral.io/scalable/vertical/killer",
"status" : "clean"
}, {
"name" : "leadreintermediate.io",
"status" : "malicious"
} ],
"created" : "2021-10-02T16:04:59.078+0000",
"id" : 8123,
"modified" : "2021-12-12T12:12:12.000+0000",
"name" : "live zebra",
"owner" : "demo",
"playbooks" : {
"phishing" : {
"name" : "Phishing",
"tasks" : {
"block-iocs" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Block IOCs",
"order" : 6,
"type" : "task"
},
"block-sender" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Block sender",
"next" : {
"extract-iocs" : ""
},
"order" : 3,
"type" : "task"
},
"board" : {
"active" : true,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Board Involvement?",
"next" : {
"escalate" : "boardInvolved == true",
"mail-available" : "boardInvolved == false"
},
"order" : 0,
"owner" : "eve",
"schema" : {
"properties" : {
"boardInvolved" : {
"default" : false,
"title" : "A board member is involved.",
"type" : "boolean"
}
},
"required" : [ "boardInvolved" ],
"title" : "Board Involvement?",
"type" : "object"
},
"type" : "input"
},
"escalate" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Escalate to CISO",
"order" : 1,
"type" : "task"
},
"extract-iocs" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Extract IOCs",
"next" : {
"block-iocs" : ""
},
"order" : 5,
"schema" : {
"properties" : {
"iocs" : {
"items" : {
"type" : "string"
},
"title" : "IOCs",
"type" : "array"
}
},
"title" : "Extract IOCs",
"type" : "object"
},
"type" : "input"
},
"mail-available" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Mail available",
"next" : {
"block-sender" : "schemaKey == 'yes'",
"extract-iocs" : "schemaKey == 'yes'",
"search-email-gateway" : "schemaKey == 'no'"
},
"order" : 2,
"schema" : {
"oneOf" : [ {
"properties" : {
"mail" : {
"title" : "Mail",
"type" : "string",
"x-display" : "textarea"
},
"schemaKey" : {
"const" : "yes",
"type" : "string"
}
},
"required" : [ "mail" ],
"title" : "Yes"
}, {
"properties" : {
"schemaKey" : {
"const" : "no",
"type" : "string"
}
},
"title" : "No"
} ],
"title" : "Mail available",
"type" : "object"
},
"type" : "input"
},
"search-email-gateway" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Search email gateway",
"next" : {
"extract-iocs" : ""
},
"order" : 4,
"type" : "task"
}
}
}
},
"references" : [ {
"href" : "https://www.leadmaximize.net/e-services/back-end",
"name" : "performance"
}, {
"href" : "http://www.corporateinteractive.name/rich",
"name" : "autumn"
}, {
"href" : "https://www.corporateintuitive.org/intuitive/platforms/integrate",
"name" : "suggest"
} ],
"schema" : "{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Event\",\n \"type\": \"object\",\n \"required\": [\n \"severity\",\n \"description\",\n \"tlp\"\n ],\n \"properties\": {\n \"severity\": {\n \"$id\": \"#root/severity\",\n \"title\": \"Severity\",\n \"type\": \"string\",\n \"default\": \"Medium\",\n \"nx-enum\": [\n \"Low\",\n \"Medium\",\n \"High\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"Low\",\n \"title\": \"Low\",\n \"icon\": \"mdi-chevron-up\"\n },\n {\n \"const\": \"Medium\",\n \"title\": \"Medium\",\n \"icon\": \"mdi-chevron-double-up\"\n },\n {\n \"const\": \"High\",\n \"title\": \"High\",\n \"icon\": \"mdi-chevron-triple-up\"\n }\n ]\n },\n \"tlp\": {\n \"$id\": \"#root/tlp\",\n \"title\": \"TLP\",\n \"type\": \"string\",\n \"nx-enum\": [\n \"White\",\n \"Green\",\n \"Amber\",\n \"Red\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"White\",\n \"title\": \"White\",\n \"icon\": \"mdi-alpha-w\"\n },\n {\n \"const\": \"Green\",\n \"title\": \"Green\",\n \"icon\": \"mdi-alpha-g\"\n },\n {\n \"const\": \"Amber\",\n \"title\": \"Amber\",\n \"icon\": \"mdi-alpha-a\"\n },\n {\n \"const\": \"Red\",\n \"title\": \"Red\",\n \"icon\": \"mdi-alpha-r\"\n }\n ]\n },\n \"description\": {\n \"$id\": \"#root/description\",\n \"title\": \"Description\",\n \"type\": \"string\",\n \"x-display\": \"textarea\",\n \"x-class\": \"pr-2\"\n }\n }\n}\n",
"status" : "closed",
"type" : "incident"
}
}
},
"description" : "successful operation"
}
},
"security" : [ {
"roles" : [ "ticket:write" ]
} ],
"summary" : "Set a ticket playbook task owner",
"tags" : [ "tickets" ],
"x-codegen-request-body-name" : "owner"
}
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}/run" : {
"post" : {
"operationId" : "runTask",
@@ -3928,10 +4157,7 @@
"content" : {
"application/json" : {
"schema" : {
"items" : {
"$ref" : "#/components/schemas/Reference"
},
"type" : "array"
"$ref" : "#/components/schemas/ReferenceArray"
}
}
},
@@ -4390,10 +4616,7 @@
"content" : {
"application/json" : {
"schema" : {
"items" : {
"$ref" : "#/components/schemas/TicketForm"
},
"type" : "array"
"$ref" : "#/components/schemas/TicketFormArray"
}
}
},
@@ -5295,6 +5518,28 @@
"required" : [ "automation", "id", "status" ],
"type" : "object"
},
"JobUpdate" : {
"properties" : {
"container" : {
"type" : "string"
},
"log" : {
"type" : "string"
},
"output" : {
"properties" : { },
"type" : "object"
},
"running" : {
"type" : "boolean"
},
"status" : {
"type" : "string"
}
},
"required" : [ "running", "status" ],
"type" : "object"
},
"Link" : {
"properties" : {
"id" : {
@@ -5478,6 +5723,12 @@
"required" : [ "href", "name" ],
"type" : "object"
},
"ReferenceArray" : {
"items" : {
"$ref" : "#/components/schemas/Reference"
},
"type" : "array"
},
"Rule" : {
"properties" : {
"condition" : {
@@ -5655,62 +5906,6 @@
"required" : [ "created", "done", "name", "type" ],
"type" : "object"
},
"TaskForm" : {
"properties" : {
"automation" : {
"type" : "string"
},
"closed" : {
"format" : "date-time",
"type" : "string"
},
"created" : {
"format" : "date-time",
"type" : "string"
},
"data" : {
"properties" : { },
"type" : "object"
},
"done" : {
"type" : "boolean"
},
"join" : {
"example" : false,
"type" : "boolean"
},
"name" : {
"example" : "Inform user",
"type" : "string"
},
"next" : {
"additionalProperties" : {
"type" : "string"
},
"type" : "object"
},
"owner" : {
"type" : "string"
},
"payload" : {
"additionalProperties" : {
"type" : "string"
},
"type" : "object"
},
"schema" : {
"properties" : { },
"type" : "object"
},
"type" : {
"enum" : [ "task", "input", "automation" ],
"example" : "task",
"type" : "string"
}
},
"required" : [ "name", "type" ],
"type" : "object"
},
"TaskOrigin" : {
"properties" : {
"playbook_id" : {
@@ -5992,6 +6187,12 @@
"required" : [ "name", "status", "type" ],
"type" : "object"
},
"TicketFormArray" : {
"items" : {
"$ref" : "#/components/schemas/TicketForm"
},
"type" : "array"
},
"TicketList" : {
"properties" : {
"count" : {

View File

@@ -288,6 +288,22 @@ definitions:
- automation
- status
type: object
JobUpdate:
properties:
container:
type: string
log:
type: string
output:
type: object
running:
type: boolean
status:
type: string
required:
- running
- status
type: object
Link:
properties:
id:
@@ -435,6 +451,10 @@ definitions:
- name
- href
type: object
ReferenceArray:
items:
$ref: '#/definitions/Reference'
type: array
Rule:
properties:
condition:
@@ -584,51 +604,6 @@ definitions:
- done
- created
type: object
TaskForm:
properties:
automation:
type: string
closed:
example: 1985-04-12T23:20:50.52Z
format: date-time
type: string
created:
example: 1985-04-12T23:20:50.52Z
format: date-time
type: string
data:
type: object
done:
type: boolean
join:
example: false
type: boolean
name:
example: Inform user
type: string
next:
additionalProperties:
type: string
type: object
owner:
type: string
payload:
additionalProperties:
type: string
type: object
schema:
type: object
type:
enum:
- task
- input
- automation
example: task
type: string
required:
- name
- type
type: object
TaskOrigin:
properties:
playbook_id:
@@ -861,6 +836,10 @@ definitions:
- type
- status
type: object
TicketFormArray:
items:
$ref: '#/definitions/TicketForm'
type: array
TicketList:
properties:
count:
@@ -1899,11 +1878,9 @@ paths:
name: job
required: true
schema:
$ref: '#/definitions/Job'
$ref: '#/definitions/JobUpdate'
x-example:
automation: hash.sha1
id: 99cd67131b48
payload: test
running: false
status: failed
responses:
"200":
@@ -5465,7 +5442,7 @@ paths:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}:
put:
operationId: setTask
operationId: setTaskData
parameters:
- description: Ticket ID
format: int64
@@ -5486,33 +5463,14 @@ paths:
required: true
type: string
x-example: board
- description: Task
- description: Task data
in: body
name: task
name: data
required: true
schema:
$ref: '#/definitions/Task'
type: object
x-example:
active: true
data:
boardInvolved: true
done: false
name: Board Involvement?
next:
escalate: boardInvolved == true
mail-available: boardInvolved == false
order: 0
schema:
properties:
boardInvolved:
default: false
title: A board member is involved.
type: boolean
required:
- boardInvolved
title: Board Involvement?
type: object
type: input
boardInvolved: true
responses:
"200":
description: successful operation
@@ -5742,7 +5700,7 @@ paths:
security:
- roles:
- ticket:write
summary: Set a ticket playbook task
summary: Set a ticket playbook task data
tags:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/complete:
@@ -6009,6 +5967,267 @@ paths:
summary: Complete ticket playbook task
tags:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/owner:
put:
operationId: setTaskOwner
parameters:
- description: Ticket ID
format: int64
in: path
name: id
required: true
type: integer
x-example: 8123
- description: Playbook ID
in: path
name: playbookID
required: true
type: string
x-example: phishing
- description: Task ID
in: path
name: taskID
required: true
type: string
x-example: board
- description: Task owner
in: body
name: owner
required: true
schema:
type: string
x-example: eve
responses:
"200":
description: successful operation
examples:
test:
artifacts:
- name: 94d5cab6f5fe3422a447ab15436e7a672bc0c09a
status: unknown
- name: http://www.customerviral.io/scalable/vertical/killer
status: clean
- name: leadreintermediate.io
status: malicious
created: 2021-10-02T16:04:59.078206Z
id: 8123
modified: 2021-12-12T12:12:12.000000012Z
name: live zebra
owner: demo
playbooks:
phishing:
name: Phishing
tasks:
block-iocs:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Block IOCs
order: 6
type: task
block-sender:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Block sender
next:
extract-iocs: ""
order: 3
type: task
board:
active: true
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Board Involvement?
next:
escalate: boardInvolved == true
mail-available: boardInvolved == false
order: 0
owner: eve
schema:
properties:
boardInvolved:
default: false
title: A board member is involved.
type: boolean
required:
- boardInvolved
title: Board Involvement?
type: object
type: input
escalate:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Escalate to CISO
order: 1
type: task
extract-iocs:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Extract IOCs
next:
block-iocs: ""
order: 5
schema:
properties:
iocs:
items:
type: string
title: IOCs
type: array
title: Extract IOCs
type: object
type: input
mail-available:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Mail available
next:
block-sender: schemaKey == 'yes'
extract-iocs: schemaKey == 'yes'
search-email-gateway: schemaKey == 'no'
order: 2
schema:
oneOf:
- properties:
mail:
title: Mail
type: string
x-display: textarea
schemaKey:
const: "yes"
type: string
required:
- mail
title: "Yes"
- properties:
schemaKey:
const: "no"
type: string
title: "No"
title: Mail available
type: object
type: input
search-email-gateway:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Search email gateway
next:
extract-iocs: ""
order: 4
type: task
references:
- href: https://www.leadmaximize.net/e-services/back-end
name: performance
- href: http://www.corporateinteractive.name/rich
name: autumn
- href: https://www.corporateintuitive.org/intuitive/platforms/integrate
name: suggest
schema: |
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1618746510.json",
"title": "Event",
"type": "object",
"required": [
"severity",
"description",
"tlp"
],
"properties": {
"severity": {
"$id": "#root/severity",
"title": "Severity",
"type": "string",
"default": "Medium",
"nx-enum": [
"Low",
"Medium",
"High"
],
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "Low",
"title": "Low",
"icon": "mdi-chevron-up"
},
{
"const": "Medium",
"title": "Medium",
"icon": "mdi-chevron-double-up"
},
{
"const": "High",
"title": "High",
"icon": "mdi-chevron-triple-up"
}
]
},
"tlp": {
"$id": "#root/tlp",
"title": "TLP",
"type": "string",
"nx-enum": [
"White",
"Green",
"Amber",
"Red"
],
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "White",
"title": "White",
"icon": "mdi-alpha-w"
},
{
"const": "Green",
"title": "Green",
"icon": "mdi-alpha-g"
},
{
"const": "Amber",
"title": "Amber",
"icon": "mdi-alpha-a"
},
{
"const": "Red",
"title": "Red",
"icon": "mdi-alpha-r"
}
]
},
"description": {
"$id": "#root/description",
"title": "Description",
"type": "string",
"x-display": "textarea",
"x-class": "pr-2"
}
}
}
status: closed
type: incident
schema:
$ref: '#/definitions/TicketWithTickets'
security:
- roles:
- ticket:write
summary: Set a ticket playbook task owner
tags:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/run:
post:
operationId: runTask
@@ -6057,9 +6276,7 @@ paths:
name: references
required: true
schema:
items:
$ref: '#/definitions/Reference'
type: array
$ref: '#/definitions/ReferenceArray'
x-example:
- href: http://www.leadscalable.biz/envisioneer
name: fund
@@ -6486,9 +6703,7 @@ paths:
name: ticket
required: true
schema:
items:
$ref: '#/definitions/TicketForm'
type: array
$ref: '#/definitions/TicketFormArray'
x-example:
- id: 123
name: Wannacry infection
@@ -6825,6 +7040,8 @@ paths:
schema:
$ref: '#/definitions/UserForm'
x-example:
apikey: true
blocked: false
id: syncscript
roles:
- analyst
@@ -6939,6 +7156,9 @@ paths:
schema:
$ref: '#/definitions/UserForm'
x-example:
apikey: false
blocked: false
id: syncscript
roles:
- analyst
- admin

View File

@@ -427,7 +427,7 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Job"
"$ref" : "#/components/schemas/JobUpdate"
}
}
},
@@ -2974,7 +2974,7 @@
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}" : {
"put" : {
"operationId" : "setTask",
"operationId" : "setTaskData",
"parameters" : [ {
"description" : "Ticket ID",
"example" : 8123,
@@ -3008,11 +3008,11 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Task"
"type" : "object"
}
}
},
"description" : "Task",
"description" : "Task data",
"required" : true
},
"responses" : {
@@ -3198,9 +3198,9 @@
"security" : [ {
"roles" : [ "ticket:write" ]
} ],
"summary" : "Set a ticket playbook task",
"summary" : "Set a ticket playbook task data",
"tags" : [ "tickets" ],
"x-codegen-request-body-name" : "task"
"x-codegen-request-body-name" : "data"
}
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}/complete" : {
@@ -3435,6 +3435,235 @@
"x-codegen-request-body-name" : "data"
}
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}/owner" : {
"put" : {
"operationId" : "setTaskOwner",
"parameters" : [ {
"description" : "Ticket ID",
"example" : 8123,
"in" : "path",
"name" : "id",
"required" : true,
"schema" : {
"format" : "int64",
"type" : "integer"
}
}, {
"description" : "Playbook ID",
"example" : "phishing",
"in" : "path",
"name" : "playbookID",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"description" : "Task ID",
"example" : "board",
"in" : "path",
"name" : "taskID",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"type" : "string"
}
}
},
"description" : "Task owner",
"required" : true
},
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/TicketWithTickets"
}
},
"test" : {
"example" : {
"artifacts" : [ {
"name" : "94d5cab6f5fe3422a447ab15436e7a672bc0c09a",
"status" : "unknown"
}, {
"name" : "http://www.customerviral.io/scalable/vertical/killer",
"status" : "clean"
}, {
"name" : "leadreintermediate.io",
"status" : "malicious"
} ],
"created" : "2021-10-02T16:04:59.078+0000",
"id" : 8123,
"modified" : "2021-12-12T12:12:12.000+0000",
"name" : "live zebra",
"owner" : "demo",
"playbooks" : {
"phishing" : {
"name" : "Phishing",
"tasks" : {
"block-iocs" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Block IOCs",
"order" : 6,
"type" : "task"
},
"block-sender" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Block sender",
"next" : {
"extract-iocs" : ""
},
"order" : 3,
"type" : "task"
},
"board" : {
"active" : true,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Board Involvement?",
"next" : {
"escalate" : "boardInvolved == true",
"mail-available" : "boardInvolved == false"
},
"order" : 0,
"owner" : "eve",
"schema" : {
"properties" : {
"boardInvolved" : {
"default" : false,
"title" : "A board member is involved.",
"type" : "boolean"
}
},
"required" : [ "boardInvolved" ],
"title" : "Board Involvement?",
"type" : "object"
},
"type" : "input"
},
"escalate" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Escalate to CISO",
"order" : 1,
"type" : "task"
},
"extract-iocs" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Extract IOCs",
"next" : {
"block-iocs" : ""
},
"order" : 5,
"schema" : {
"properties" : {
"iocs" : {
"items" : {
"type" : "string"
},
"title" : "IOCs",
"type" : "array"
}
},
"title" : "Extract IOCs",
"type" : "object"
},
"type" : "input"
},
"mail-available" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Mail available",
"next" : {
"block-sender" : "schemaKey == 'yes'",
"extract-iocs" : "schemaKey == 'yes'",
"search-email-gateway" : "schemaKey == 'no'"
},
"order" : 2,
"schema" : {
"oneOf" : [ {
"properties" : {
"mail" : {
"title" : "Mail",
"type" : "string",
"x-display" : "textarea"
},
"schemaKey" : {
"const" : "yes",
"type" : "string"
}
},
"required" : [ "mail" ],
"title" : "Yes"
}, {
"properties" : {
"schemaKey" : {
"const" : "no",
"type" : "string"
}
},
"title" : "No"
} ],
"title" : "Mail available",
"type" : "object"
},
"type" : "input"
},
"search-email-gateway" : {
"active" : false,
"created" : "2021-12-12T12:12:12.000+0000",
"done" : false,
"name" : "Search email gateway",
"next" : {
"extract-iocs" : ""
},
"order" : 4,
"type" : "task"
}
}
}
},
"references" : [ {
"href" : "https://www.leadmaximize.net/e-services/back-end",
"name" : "performance"
}, {
"href" : "http://www.corporateinteractive.name/rich",
"name" : "autumn"
}, {
"href" : "https://www.corporateintuitive.org/intuitive/platforms/integrate",
"name" : "suggest"
} ],
"schema" : "{\n \"definitions\": {},\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"https://example.com/object1618746510.json\",\n \"title\": \"Event\",\n \"type\": \"object\",\n \"required\": [\n \"severity\",\n \"description\",\n \"tlp\"\n ],\n \"properties\": {\n \"severity\": {\n \"$id\": \"#root/severity\",\n \"title\": \"Severity\",\n \"type\": \"string\",\n \"default\": \"Medium\",\n \"nx-enum\": [\n \"Low\",\n \"Medium\",\n \"High\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"Low\",\n \"title\": \"Low\",\n \"icon\": \"mdi-chevron-up\"\n },\n {\n \"const\": \"Medium\",\n \"title\": \"Medium\",\n \"icon\": \"mdi-chevron-double-up\"\n },\n {\n \"const\": \"High\",\n \"title\": \"High\",\n \"icon\": \"mdi-chevron-triple-up\"\n }\n ]\n },\n \"tlp\": {\n \"$id\": \"#root/tlp\",\n \"title\": \"TLP\",\n \"type\": \"string\",\n \"nx-enum\": [\n \"White\",\n \"Green\",\n \"Amber\",\n \"Red\"\n ],\n \"x-cols\": 6,\n \"x-class\": \"pr-2\",\n \"x-display\": \"icon\",\n \"x-itemIcon\": \"icon\",\n \"oneOf\": [\n {\n \"const\": \"White\",\n \"title\": \"White\",\n \"icon\": \"mdi-alpha-w\"\n },\n {\n \"const\": \"Green\",\n \"title\": \"Green\",\n \"icon\": \"mdi-alpha-g\"\n },\n {\n \"const\": \"Amber\",\n \"title\": \"Amber\",\n \"icon\": \"mdi-alpha-a\"\n },\n {\n \"const\": \"Red\",\n \"title\": \"Red\",\n \"icon\": \"mdi-alpha-r\"\n }\n ]\n },\n \"description\": {\n \"$id\": \"#root/description\",\n \"title\": \"Description\",\n \"type\": \"string\",\n \"x-display\": \"textarea\",\n \"x-class\": \"pr-2\"\n }\n }\n}\n",
"status" : "closed",
"type" : "incident"
}
}
},
"description" : "successful operation"
}
},
"security" : [ {
"roles" : [ "ticket:write" ]
} ],
"summary" : "Set a ticket playbook task owner",
"tags" : [ "tickets" ],
"x-codegen-request-body-name" : "owner"
}
},
"/tickets/{id}/playbooks/{playbookID}/task/{taskID}/run" : {
"post" : {
"operationId" : "runTask",
@@ -3498,10 +3727,7 @@
"content" : {
"application/json" : {
"schema" : {
"items" : {
"$ref" : "#/components/schemas/Reference"
},
"type" : "array"
"$ref" : "#/components/schemas/ReferenceArray"
}
}
},
@@ -3960,10 +4186,7 @@
"content" : {
"application/json" : {
"schema" : {
"items" : {
"$ref" : "#/components/schemas/TicketForm"
},
"type" : "array"
"$ref" : "#/components/schemas/TicketFormArray"
}
}
},
@@ -4797,6 +5020,28 @@
"required" : [ "automation", "id", "status" ],
"type" : "object"
},
"JobUpdate" : {
"properties" : {
"container" : {
"type" : "string"
},
"log" : {
"type" : "string"
},
"output" : {
"properties" : { },
"type" : "object"
},
"running" : {
"type" : "boolean"
},
"status" : {
"type" : "string"
}
},
"required" : [ "running", "status" ],
"type" : "object"
},
"LogEntry" : {
"properties" : {
"created" : {
@@ -4953,6 +5198,12 @@
"required" : [ "href", "name" ],
"type" : "object"
},
"ReferenceArray" : {
"items" : {
"$ref" : "#/components/schemas/Reference"
},
"type" : "array"
},
"Settings" : {
"properties" : {
"artifactStates" : {
@@ -5076,62 +5327,6 @@
"required" : [ "created", "done", "name", "type" ],
"type" : "object"
},
"TaskForm" : {
"properties" : {
"automation" : {
"type" : "string"
},
"closed" : {
"format" : "date-time",
"type" : "string"
},
"created" : {
"format" : "date-time",
"type" : "string"
},
"data" : {
"properties" : { },
"type" : "object"
},
"done" : {
"type" : "boolean"
},
"join" : {
"example" : false,
"type" : "boolean"
},
"name" : {
"example" : "Inform user",
"type" : "string"
},
"next" : {
"additionalProperties" : {
"type" : "string"
},
"type" : "object"
},
"owner" : {
"type" : "string"
},
"payload" : {
"additionalProperties" : {
"type" : "string"
},
"type" : "object"
},
"schema" : {
"properties" : { },
"type" : "object"
},
"type" : {
"enum" : [ "task", "input", "automation" ],
"example" : "task",
"type" : "string"
}
},
"required" : [ "name", "type" ],
"type" : "object"
},
"TaskOrigin" : {
"properties" : {
"playbook_id" : {
@@ -5413,6 +5608,12 @@
"required" : [ "name", "status", "type" ],
"type" : "object"
},
"TicketFormArray" : {
"items" : {
"$ref" : "#/components/schemas/TicketForm"
},
"type" : "array"
},
"TicketList" : {
"properties" : {
"count" : {

View File

@@ -236,6 +236,22 @@ definitions:
- automation
- status
type: object
JobUpdate:
properties:
container:
type: string
log:
type: string
output:
type: object
running:
type: boolean
status:
type: string
required:
- running
- status
type: object
LogEntry:
properties:
created:
@@ -360,6 +376,10 @@ definitions:
- name
- href
type: object
ReferenceArray:
items:
$ref: '#/definitions/Reference'
type: array
Settings:
properties:
artifactStates:
@@ -465,51 +485,6 @@ definitions:
- done
- created
type: object
TaskForm:
properties:
automation:
type: string
closed:
example: 1985-04-12T23:20:50.52Z
format: date-time
type: string
created:
example: 1985-04-12T23:20:50.52Z
format: date-time
type: string
data:
type: object
done:
type: boolean
join:
example: false
type: boolean
name:
example: Inform user
type: string
next:
additionalProperties:
type: string
type: object
owner:
type: string
payload:
additionalProperties:
type: string
type: object
schema:
type: object
type:
enum:
- task
- input
- automation
example: task
type: string
required:
- name
- type
type: object
TaskOrigin:
properties:
playbook_id:
@@ -742,6 +717,10 @@ definitions:
- type
- status
type: object
TicketFormArray:
items:
$ref: '#/definitions/TicketForm'
type: array
TicketList:
properties:
count:
@@ -1628,11 +1607,9 @@ paths:
name: job
required: true
schema:
$ref: '#/definitions/Job'
$ref: '#/definitions/JobUpdate'
x-example:
automation: hash.sha1
id: 99cd67131b48
payload: test
running: false
status: failed
responses:
"200":
@@ -5053,7 +5030,7 @@ paths:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}:
put:
operationId: setTask
operationId: setTaskData
parameters:
- description: Ticket ID
format: int64
@@ -5074,33 +5051,14 @@ paths:
required: true
type: string
x-example: board
- description: Task
- description: Task data
in: body
name: task
name: data
required: true
schema:
$ref: '#/definitions/Task'
type: object
x-example:
active: true
data:
boardInvolved: true
done: false
name: Board Involvement?
next:
escalate: boardInvolved == true
mail-available: boardInvolved == false
order: 0
schema:
properties:
boardInvolved:
default: false
title: A board member is involved.
type: boolean
required:
- boardInvolved
title: Board Involvement?
type: object
type: input
boardInvolved: true
responses:
"200":
description: successful operation
@@ -5330,7 +5288,7 @@ paths:
security:
- roles:
- ticket:write
summary: Set a ticket playbook task
summary: Set a ticket playbook task data
tags:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/complete:
@@ -5597,6 +5555,267 @@ paths:
summary: Complete ticket playbook task
tags:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/owner:
put:
operationId: setTaskOwner
parameters:
- description: Ticket ID
format: int64
in: path
name: id
required: true
type: integer
x-example: 8123
- description: Playbook ID
in: path
name: playbookID
required: true
type: string
x-example: phishing
- description: Task ID
in: path
name: taskID
required: true
type: string
x-example: board
- description: Task owner
in: body
name: owner
required: true
schema:
type: string
x-example: eve
responses:
"200":
description: successful operation
examples:
test:
artifacts:
- name: 94d5cab6f5fe3422a447ab15436e7a672bc0c09a
status: unknown
- name: http://www.customerviral.io/scalable/vertical/killer
status: clean
- name: leadreintermediate.io
status: malicious
created: 2021-10-02T16:04:59.078206Z
id: 8123
modified: 2021-12-12T12:12:12.000000012Z
name: live zebra
owner: demo
playbooks:
phishing:
name: Phishing
tasks:
block-iocs:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Block IOCs
order: 6
type: task
block-sender:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Block sender
next:
extract-iocs: ""
order: 3
type: task
board:
active: true
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Board Involvement?
next:
escalate: boardInvolved == true
mail-available: boardInvolved == false
order: 0
owner: eve
schema:
properties:
boardInvolved:
default: false
title: A board member is involved.
type: boolean
required:
- boardInvolved
title: Board Involvement?
type: object
type: input
escalate:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Escalate to CISO
order: 1
type: task
extract-iocs:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Extract IOCs
next:
block-iocs: ""
order: 5
schema:
properties:
iocs:
items:
type: string
title: IOCs
type: array
title: Extract IOCs
type: object
type: input
mail-available:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Mail available
next:
block-sender: schemaKey == 'yes'
extract-iocs: schemaKey == 'yes'
search-email-gateway: schemaKey == 'no'
order: 2
schema:
oneOf:
- properties:
mail:
title: Mail
type: string
x-display: textarea
schemaKey:
const: "yes"
type: string
required:
- mail
title: "Yes"
- properties:
schemaKey:
const: "no"
type: string
title: "No"
title: Mail available
type: object
type: input
search-email-gateway:
active: false
created: 2021-12-12T12:12:12.000000012Z
done: false
name: Search email gateway
next:
extract-iocs: ""
order: 4
type: task
references:
- href: https://www.leadmaximize.net/e-services/back-end
name: performance
- href: http://www.corporateinteractive.name/rich
name: autumn
- href: https://www.corporateintuitive.org/intuitive/platforms/integrate
name: suggest
schema: |
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1618746510.json",
"title": "Event",
"type": "object",
"required": [
"severity",
"description",
"tlp"
],
"properties": {
"severity": {
"$id": "#root/severity",
"title": "Severity",
"type": "string",
"default": "Medium",
"nx-enum": [
"Low",
"Medium",
"High"
],
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "Low",
"title": "Low",
"icon": "mdi-chevron-up"
},
{
"const": "Medium",
"title": "Medium",
"icon": "mdi-chevron-double-up"
},
{
"const": "High",
"title": "High",
"icon": "mdi-chevron-triple-up"
}
]
},
"tlp": {
"$id": "#root/tlp",
"title": "TLP",
"type": "string",
"nx-enum": [
"White",
"Green",
"Amber",
"Red"
],
"x-cols": 6,
"x-class": "pr-2",
"x-display": "icon",
"x-itemIcon": "icon",
"oneOf": [
{
"const": "White",
"title": "White",
"icon": "mdi-alpha-w"
},
{
"const": "Green",
"title": "Green",
"icon": "mdi-alpha-g"
},
{
"const": "Amber",
"title": "Amber",
"icon": "mdi-alpha-a"
},
{
"const": "Red",
"title": "Red",
"icon": "mdi-alpha-r"
}
]
},
"description": {
"$id": "#root/description",
"title": "Description",
"type": "string",
"x-display": "textarea",
"x-class": "pr-2"
}
}
}
status: closed
type: incident
schema:
$ref: '#/definitions/TicketWithTickets'
security:
- roles:
- ticket:write
summary: Set a ticket playbook task owner
tags:
- tickets
/tickets/{id}/playbooks/{playbookID}/task/{taskID}/run:
post:
operationId: runTask
@@ -5645,9 +5864,7 @@ paths:
name: references
required: true
schema:
items:
$ref: '#/definitions/Reference'
type: array
$ref: '#/definitions/ReferenceArray'
x-example:
- href: http://www.leadscalable.biz/envisioneer
name: fund
@@ -6074,9 +6291,7 @@ paths:
name: ticket
required: true
schema:
items:
$ref: '#/definitions/TicketForm'
type: array
$ref: '#/definitions/TicketFormArray'
x-example:
- id: 123
name: Wannacry infection
@@ -6413,6 +6628,8 @@ paths:
schema:
$ref: '#/definitions/UserForm'
x-example:
apikey: true
blocked: false
id: syncscript
roles:
- analyst
@@ -6527,6 +6744,9 @@ paths:
schema:
$ref: '#/definitions/UserForm'
x-example:
apikey: false
blocked: false
id: syncscript
roles:
- analyst
- admin

View File

@@ -1,8 +1,6 @@
package model
import (
"fmt"
"strings"
"time"
"github.com/xeipuuv/gojsonschema"
@@ -24,6 +22,7 @@ var (
JobSchema = new(gojsonschema.Schema)
JobFormSchema = new(gojsonschema.Schema)
JobResponseSchema = new(gojsonschema.Schema)
JobUpdateSchema = new(gojsonschema.Schema)
LogEntrySchema = new(gojsonschema.Schema)
MessageSchema = new(gojsonschema.Schema)
NewUserResponseSchema = new(gojsonschema.Schema)
@@ -34,15 +33,16 @@ var (
PlaybookTemplateFormSchema = new(gojsonschema.Schema)
PlaybookTemplateResponseSchema = new(gojsonschema.Schema)
ReferenceSchema = new(gojsonschema.Schema)
ReferenceArraySchema = new(gojsonschema.Schema)
SettingsSchema = new(gojsonschema.Schema)
StatisticsSchema = new(gojsonschema.Schema)
TaskSchema = new(gojsonschema.Schema)
TaskFormSchema = new(gojsonschema.Schema)
TaskOriginSchema = new(gojsonschema.Schema)
TaskResponseSchema = new(gojsonschema.Schema)
TaskWithContextSchema = new(gojsonschema.Schema)
TicketSchema = new(gojsonschema.Schema)
TicketFormSchema = new(gojsonschema.Schema)
TicketFormArraySchema = new(gojsonschema.Schema)
TicketListSchema = new(gojsonschema.Schema)
TicketResponseSchema = new(gojsonschema.Schema)
TicketSimpleResponseSchema = new(gojsonschema.Schema)
@@ -63,55 +63,57 @@ var (
func init() {
err := schemaLoader.AddSchemas(
gojsonschema.NewStringLoader(`{"type":"object","required":["name"],"x-embed":"","properties":{"enrichments":{"type":"object","additionalProperties":{"$ref":"#/definitions/Enrichment"}},"name":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"}},"$id":"#/definitions/Artifact"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["ticket_id","artifact"],"x-embed":"","properties":{"artifact":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"$id":"#/definitions/ArtifactOrigin"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["image","script","type"],"x-embed":"","properties":{"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"$id":"#/definitions/Automation"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","image","script","type"],"x-embed":"","properties":{"id":{"type":"string"},"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"$id":"#/definitions/AutomationForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","image","script","type"],"x-embed":"","properties":{"id":{"type":"string"},"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"$id":"#/definitions/AutomationResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["creator","created","message"],"x-embed":"","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"$id":"#/definitions/Comment"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["message"],"x-embed":"","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"$id":"#/definitions/CommentForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","x-embed":"","properties":{"artifact":{"$ref":"#/definitions/Artifact"},"playbook":{"$ref":"#/definitions/PlaybookResponse"},"task":{"$ref":"#/definitions/TaskResponse"},"ticket":{"$ref":"#/definitions/TicketResponse"}},"$id":"#/definitions/Context"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","data","created"],"x-embed":"","properties":{"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"name":{"type":"string"}},"$id":"#/definitions/Enrichment"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","data"],"x-embed":"","properties":{"data":{"type":"object"},"name":{"type":"string"}},"$id":"#/definitions/EnrichmentForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["key","name"],"x-embed":"","properties":{"key":{"type":"string"},"name":{"type":"string"}},"$id":"#/definitions/File"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["automation","running","status"],"x-embed":"","properties":{"automation":{"type":"string"},"container":{"type":"string"},"log":{"type":"string"},"origin":{"$ref":"#/definitions/Origin"},"output":{"type":"object"},"payload":{},"running":{"type":"boolean"},"status":{"type":"string"}},"$id":"#/definitions/Job"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["automation"],"x-embed":"","properties":{"automation":{"type":"string"},"origin":{"$ref":"#/definitions/Origin"},"payload":{}},"$id":"#/definitions/JobForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","automation","status"],"x-embed":"","properties":{"automation":{"type":"string"},"container":{"type":"string"},"id":{"type":"string"},"log":{"type":"string"},"origin":{"$ref":"#/definitions/Origin"},"output":{"type":"object"},"payload":{},"status":{"type":"string"}},"$id":"#/definitions/JobResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["type","reference","creator","created","message"],"x-embed":"","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"},"reference":{"type":"string"},"type":{"type":"string"}},"$id":"#/definitions/LogEntry"}`),
gojsonschema.NewStringLoader(`{"type":"object","x-embed":"","properties":{"context":{"$ref":"#/definitions/Context"},"payload":{},"secrets":{"type":"object","additionalProperties":{"type":"string"}}},"$id":"#/definitions/Message"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","blocked","roles"],"x-embed":"","properties":{"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"},"secret":{"type":"string"}},"$id":"#/definitions/NewUserResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","x-embed":"","properties":{"artifact_origin":{"$ref":"#/definitions/ArtifactOrigin"},"task_origin":{"$ref":"#/definitions/TaskOrigin"}},"$id":"#/definitions/Origin"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","tasks"],"x-embed":"","properties":{"name":{"type":"string"},"tasks":{"type":"object","additionalProperties":{"$ref":"#/definitions/Task"}}},"$id":"#/definitions/Playbook"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","tasks"],"x-embed":"","properties":{"name":{"type":"string"},"tasks":{"type":"object","additionalProperties":{"$ref":"#/definitions/TaskResponse"}}},"$id":"#/definitions/PlaybookResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","yaml"],"x-embed":"","properties":{"name":{"type":"string"},"yaml":{"type":"string"}},"$id":"#/definitions/PlaybookTemplate"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["yaml"],"x-embed":"","properties":{"id":{"type":"string"},"yaml":{"type":"string"}},"$id":"#/definitions/PlaybookTemplateForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","name","yaml"],"x-embed":"","properties":{"id":{"type":"string"},"name":{"type":"string"},"yaml":{"type":"string"}},"$id":"#/definitions/PlaybookTemplateResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","href"],"x-embed":"","properties":{"href":{"type":"string"},"name":{"type":"string"}},"$id":"#/definitions/Reference"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["version","tier","timeformat","ticketTypes","artifactStates"],"x-embed":"","properties":{"artifactStates":{"title":"Artifact States","items":{"$ref":"#/definitions/Type"},"type":"array"},"roles":{"title":"Roles","items":{"type":"string"},"type":"array"},"ticketTypes":{"title":"Ticket Types","items":{"$ref":"#/definitions/TicketTypeResponse"},"type":"array"},"tier":{"title":"Tier","type":"string","enum":["community","enterprise"]},"timeformat":{"title":"Time Format","type":"string"},"version":{"title":"Version","type":"string"}},"$id":"#/definitions/Settings"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["unassigned","open_tickets_per_user","tickets_per_week","tickets_per_type"],"x-embed":"","properties":{"open_tickets_per_user":{"type":"object","additionalProperties":{"type":"integer"}},"tickets_per_type":{"type":"object","additionalProperties":{"type":"integer"}},"tickets_per_week":{"type":"object","additionalProperties":{"type":"integer"}},"unassigned":{"type":"integer"}},"$id":"#/definitions/Statistics"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","type","done","created"],"x-embed":"","properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"done":{"type":"boolean"},"join":{"type":"boolean"},"name":{"type":"string"},"next":{"type":"object","additionalProperties":{"type":"string"}},"owner":{"type":"string"},"payload":{"type":"object","additionalProperties":{"type":"string"}},"schema":{"type":"object"},"type":{"type":"string","enum":["task","input","automation"]}},"$id":"#/definitions/Task"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","type"],"x-embed":"","properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"done":{"type":"boolean"},"join":{"type":"boolean"},"name":{"type":"string"},"next":{"type":"object","additionalProperties":{"type":"string"}},"owner":{"type":"string"},"payload":{"type":"object","additionalProperties":{"type":"string"}},"schema":{"type":"object"},"type":{"type":"string","enum":["task","input","automation"]}},"$id":"#/definitions/TaskForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["ticket_id","playbook_id","task_id"],"x-embed":"","properties":{"playbook_id":{"type":"string"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"$id":"#/definitions/TaskOrigin"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","type","done","created","order","active"],"x-embed":"","properties":{"active":{"type":"boolean"},"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"done":{"type":"boolean"},"join":{"type":"boolean"},"name":{"type":"string"},"next":{"type":"object","additionalProperties":{"type":"string"}},"order":{"format":"int64","type":"number"},"owner":{"type":"string"},"payload":{"type":"object","additionalProperties":{"type":"string"}},"schema":{"type":"object"},"type":{"type":"string","enum":["task","input","automation"]}},"$id":"#/definitions/TaskResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["ticket_id","ticket_name","playbook_id","playbook_name","task_id","task"],"x-embed":"","properties":{"playbook_id":{"type":"string"},"playbook_name":{"type":"string"},"task":{"$ref":"#/definitions/TaskResponse"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"number"},"ticket_name":{"type":"string"}},"$id":"#/definitions/TaskWithContext"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","type","status","created","modified","schema"],"x-embed":"","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/Playbook"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"$id":"#/definitions/Ticket"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","type","status"],"x-embed":"","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"items":{"$ref":"#/definitions/PlaybookTemplateForm"},"type":"array"},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"$id":"#/definitions/TicketForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["tickets","count"],"x-embed":"","properties":{"count":{"type":"number"},"tickets":{"items":{"$ref":"#/definitions/TicketSimpleResponse"},"type":"array"}},"$id":"#/definitions/TicketList"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","name","type","status","created","modified","schema"],"x-embed":"","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/PlaybookResponse"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"$id":"#/definitions/TicketResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","name","type","status","created","modified","schema"],"x-embed":"","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/Playbook"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"$id":"#/definitions/TicketSimpleResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","schema"],"x-embed":"","properties":{"name":{"type":"string"},"schema":{"type":"string"}},"$id":"#/definitions/TicketTemplate"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","schema"],"x-embed":"","properties":{"id":{"type":"string"},"name":{"type":"string"},"schema":{"type":"string"}},"$id":"#/definitions/TicketTemplateForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","name","schema"],"x-embed":"","properties":{"id":{"type":"string"},"name":{"type":"string"},"schema":{"type":"string"}},"$id":"#/definitions/TicketTemplateResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","icon","default_template","default_playbooks"],"x-embed":"","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"name":{"type":"string"}},"$id":"#/definitions/TicketType"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["name","icon","default_template","default_playbooks"],"x-embed":"","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"$id":"#/definitions/TicketTypeForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","name","icon","default_template","default_playbooks"],"x-embed":"","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"$id":"#/definitions/TicketTypeResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","name","type","status","created","modified","schema"],"x-embed":"","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"logs":{"items":{"$ref":"#/definitions/LogEntry"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/PlaybookResponse"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"tickets":{"items":{"$ref":"#/definitions/TicketSimpleResponse"},"type":"array"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"$id":"#/definitions/TicketWithTickets"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","name","icon"],"x-embed":"","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"$id":"#/definitions/Type"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["blocked","apikey","roles"],"x-embed":"","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"sha256":{"type":"string"}},"$id":"#/definitions/User"}`),
gojsonschema.NewStringLoader(`{"type":"object","x-embed":"","properties":{"email":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"$id":"#/definitions/UserData"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id"],"x-embed":"","properties":{"email":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"$id":"#/definitions/UserDataResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","blocked","roles","apikey"],"x-embed":"","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"$id":"#/definitions/UserForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","required":["id","blocked","roles","apikey"],"x-embed":"","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"$id":"#/definitions/UserResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"enrichments":{"type":"object","additionalProperties":{"$ref":"#/definitions/Enrichment"}},"name":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"}},"required":["name"],"$id":"#/definitions/Artifact"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifact":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["ticket_id","artifact"],"$id":"#/definitions/ArtifactOrigin"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"required":["image","script","type"],"$id":"#/definitions/Automation"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"required":["id","image","script","type"],"$id":"#/definitions/AutomationForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"required":["id","image","script","type"],"$id":"#/definitions/AutomationResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["creator","created","message"],"$id":"#/definitions/Comment"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["message"],"$id":"#/definitions/CommentForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifact":{"$ref":"#/definitions/Artifact"},"playbook":{"$ref":"#/definitions/PlaybookResponse"},"task":{"$ref":"#/definitions/TaskResponse"},"ticket":{"$ref":"#/definitions/TicketResponse"}},"$id":"#/definitions/Context"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"name":{"type":"string"}},"required":["name","data","created"],"$id":"#/definitions/Enrichment"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"data":{"type":"object"},"name":{"type":"string"}},"required":["name","data"],"$id":"#/definitions/EnrichmentForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"}},"required":["key","name"],"$id":"#/definitions/File"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"automation":{"type":"string"},"container":{"type":"string"},"log":{"type":"string"},"origin":{"$ref":"#/definitions/Origin"},"output":{"type":"object"},"payload":{},"running":{"type":"boolean"},"status":{"type":"string"}},"required":["automation","running","status"],"$id":"#/definitions/Job"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"automation":{"type":"string"},"origin":{"$ref":"#/definitions/Origin"},"payload":{}},"required":["automation"],"$id":"#/definitions/JobForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"automation":{"type":"string"},"container":{"type":"string"},"id":{"type":"string"},"log":{"type":"string"},"origin":{"$ref":"#/definitions/Origin"},"output":{"type":"object"},"payload":{},"status":{"type":"string"}},"required":["id","automation","status"],"$id":"#/definitions/JobResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"container":{"type":"string"},"log":{"type":"string"},"output":{"type":"object"},"running":{"type":"boolean"},"status":{"type":"string"}},"required":["running","status"],"$id":"#/definitions/JobUpdate"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"},"reference":{"type":"string"},"type":{"type":"string"}},"required":["type","reference","creator","created","message"],"$id":"#/definitions/LogEntry"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"context":{"$ref":"#/definitions/Context"},"payload":{},"secrets":{"type":"object","additionalProperties":{"type":"string"}}},"$id":"#/definitions/Message"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"},"secret":{"type":"string"}},"required":["id","blocked","roles"],"$id":"#/definitions/NewUserResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifact_origin":{"$ref":"#/definitions/ArtifactOrigin"},"task_origin":{"$ref":"#/definitions/TaskOrigin"}},"$id":"#/definitions/Origin"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"name":{"type":"string"},"tasks":{"type":"object","additionalProperties":{"$ref":"#/definitions/Task"}}},"required":["name","tasks"],"$id":"#/definitions/Playbook"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"name":{"type":"string"},"tasks":{"type":"object","additionalProperties":{"$ref":"#/definitions/TaskResponse"}}},"required":["name","tasks"],"$id":"#/definitions/PlaybookResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"name":{"type":"string"},"yaml":{"type":"string"}},"required":["name","yaml"],"$id":"#/definitions/PlaybookTemplate"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"yaml":{"type":"string"}},"required":["yaml"],"$id":"#/definitions/PlaybookTemplateForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"yaml":{"type":"string"}},"required":["id","name","yaml"],"$id":"#/definitions/PlaybookTemplateResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"href":{"type":"string"},"name":{"type":"string"}},"required":["name","href"],"$id":"#/definitions/Reference"}`),
gojsonschema.NewStringLoader(`{"items":{"$ref":"#/definitions/Reference"},"type":"array","$id":"#/definitions/ReferenceArray"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifactStates":{"title":"Artifact States","items":{"$ref":"#/definitions/Type"},"type":"array"},"roles":{"title":"Roles","items":{"type":"string"},"type":"array"},"ticketTypes":{"title":"Ticket Types","items":{"$ref":"#/definitions/TicketTypeResponse"},"type":"array"},"tier":{"title":"Tier","type":"string","enum":["community","enterprise"]},"timeformat":{"title":"Time Format","type":"string"},"version":{"title":"Version","type":"string"}},"required":["version","tier","timeformat","ticketTypes","artifactStates"],"$id":"#/definitions/Settings"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"open_tickets_per_user":{"type":"object","additionalProperties":{"type":"integer"}},"tickets_per_type":{"type":"object","additionalProperties":{"type":"integer"}},"tickets_per_week":{"type":"object","additionalProperties":{"type":"integer"}},"unassigned":{"type":"integer"}},"required":["unassigned","open_tickets_per_user","tickets_per_week","tickets_per_type"],"$id":"#/definitions/Statistics"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"done":{"type":"boolean"},"join":{"type":"boolean"},"name":{"type":"string"},"next":{"type":"object","additionalProperties":{"type":"string"}},"owner":{"type":"string"},"payload":{"type":"object","additionalProperties":{"type":"string"}},"schema":{"type":"object"},"type":{"type":"string","enum":["task","input","automation"]}},"required":["name","type","done","created"],"$id":"#/definitions/Task"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"playbook_id":{"type":"string"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["ticket_id","playbook_id","task_id"],"$id":"#/definitions/TaskOrigin"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"active":{"type":"boolean"},"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"done":{"type":"boolean"},"join":{"type":"boolean"},"name":{"type":"string"},"next":{"type":"object","additionalProperties":{"type":"string"}},"order":{"format":"int64","type":"number"},"owner":{"type":"string"},"payload":{"type":"object","additionalProperties":{"type":"string"}},"schema":{"type":"object"},"type":{"type":"string","enum":["task","input","automation"]}},"required":["name","type","done","created","order","active"],"$id":"#/definitions/TaskResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"playbook_id":{"type":"string"},"playbook_name":{"type":"string"},"task":{"$ref":"#/definitions/TaskResponse"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"number"},"ticket_name":{"type":"string"}},"required":["ticket_id","ticket_name","playbook_id","playbook_name","task_id","task"],"$id":"#/definitions/TaskWithContext"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/Playbook"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"required":["name","type","status","created","modified","schema"],"$id":"#/definitions/Ticket"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"items":{"$ref":"#/definitions/PlaybookTemplateForm"},"type":"array"},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"required":["name","type","status"],"$id":"#/definitions/TicketForm"}`),
gojsonschema.NewStringLoader(`{"items":{"$ref":"#/definitions/TicketForm"},"type":"array","$id":"#/definitions/TicketFormArray"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"count":{"type":"number"},"tickets":{"items":{"$ref":"#/definitions/TicketSimpleResponse"},"type":"array"}},"required":["tickets","count"],"$id":"#/definitions/TicketList"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/PlaybookResponse"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"required":["id","name","type","status","created","modified","schema"],"$id":"#/definitions/TicketResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/Playbook"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"required":["id","name","type","status","created","modified","schema"],"$id":"#/definitions/TicketSimpleResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"name":{"type":"string"},"schema":{"type":"string"}},"required":["name","schema"],"$id":"#/definitions/TicketTemplate"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"schema":{"type":"string"}},"required":["name","schema"],"$id":"#/definitions/TicketTemplateForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"schema":{"type":"string"}},"required":["id","name","schema"],"$id":"#/definitions/TicketTemplateResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"name":{"type":"string"}},"required":["name","icon","default_template","default_playbooks"],"$id":"#/definitions/TicketType"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"required":["name","icon","default_template","default_playbooks"],"$id":"#/definitions/TicketTypeForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name","icon","default_template","default_playbooks"],"$id":"#/definitions/TicketTypeResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"logs":{"items":{"$ref":"#/definitions/LogEntry"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/PlaybookResponse"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"tickets":{"items":{"$ref":"#/definitions/TicketSimpleResponse"},"type":"array"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"required":["id","name","type","status","created","modified","schema"],"$id":"#/definitions/TicketWithTickets"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"required":["id","name","icon"],"$id":"#/definitions/Type"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"sha256":{"type":"string"}},"required":["blocked","apikey","roles"],"$id":"#/definitions/User"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"$id":"#/definitions/UserData"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"required":["id"],"$id":"#/definitions/UserDataResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserForm"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserResponse"}`),
)
if err != nil {
panic(err)
@@ -131,6 +133,7 @@ func init() {
JobSchema = mustCompile(`#/definitions/Job`)
JobFormSchema = mustCompile(`#/definitions/JobForm`)
JobResponseSchema = mustCompile(`#/definitions/JobResponse`)
JobUpdateSchema = mustCompile(`#/definitions/JobUpdate`)
LogEntrySchema = mustCompile(`#/definitions/LogEntry`)
MessageSchema = mustCompile(`#/definitions/Message`)
NewUserResponseSchema = mustCompile(`#/definitions/NewUserResponse`)
@@ -141,15 +144,16 @@ func init() {
PlaybookTemplateFormSchema = mustCompile(`#/definitions/PlaybookTemplateForm`)
PlaybookTemplateResponseSchema = mustCompile(`#/definitions/PlaybookTemplateResponse`)
ReferenceSchema = mustCompile(`#/definitions/Reference`)
ReferenceArraySchema = mustCompile(`#/definitions/ReferenceArray`)
SettingsSchema = mustCompile(`#/definitions/Settings`)
StatisticsSchema = mustCompile(`#/definitions/Statistics`)
TaskSchema = mustCompile(`#/definitions/Task`)
TaskFormSchema = mustCompile(`#/definitions/TaskForm`)
TaskOriginSchema = mustCompile(`#/definitions/TaskOrigin`)
TaskResponseSchema = mustCompile(`#/definitions/TaskResponse`)
TaskWithContextSchema = mustCompile(`#/definitions/TaskWithContext`)
TicketSchema = mustCompile(`#/definitions/Ticket`)
TicketFormSchema = mustCompile(`#/definitions/TicketForm`)
TicketFormArraySchema = mustCompile(`#/definitions/TicketFormArray`)
TicketListSchema = mustCompile(`#/definitions/TicketList`)
TicketResponseSchema = mustCompile(`#/definitions/TicketResponse`)
TicketSimpleResponseSchema = mustCompile(`#/definitions/TicketSimpleResponse`)
@@ -266,6 +270,14 @@ type JobResponse struct {
Status string `json:"status"`
}
type JobUpdate struct {
Container *string `json:"container,omitempty"`
Log *string `json:"log,omitempty"`
Output map[string]interface{} `json:"output,omitempty"`
Running bool `json:"running"`
Status string `json:"status"`
}
type LogEntry struct {
Created time.Time `json:"created"`
Creator string `json:"creator"`
@@ -323,6 +335,8 @@ type Reference struct {
Name string `json:"name"`
}
type ReferenceArray []*Reference
type Settings struct {
ArtifactStates []*Type `json:"artifactStates"`
Roles []string `json:"roles,omitempty"`
@@ -354,21 +368,6 @@ type Task struct {
Type string `json:"type"`
}
type TaskForm struct {
Automation *string `json:"automation,omitempty"`
Closed *time.Time `json:"closed,omitempty"`
Created *time.Time `json:"created,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
Done *bool `json:"done,omitempty"`
Join *bool `json:"join,omitempty"`
Name string `json:"name"`
Next map[string]string `json:"next,omitempty"`
Owner *string `json:"owner,omitempty"`
Payload map[string]string `json:"payload,omitempty"`
Schema map[string]interface{} `json:"schema,omitempty"`
Type string `json:"type"`
}
type TaskOrigin struct {
PlaybookId string `json:"playbook_id"`
TaskId string `json:"task_id"`
@@ -438,6 +437,8 @@ type TicketForm struct {
Write []string `json:"write,omitempty"`
}
type TicketFormArray []*TicketForm
type TicketList struct {
Count int `json:"count"`
Tickets []*TicketSimpleResponse `json:"tickets"`
@@ -596,22 +597,6 @@ func mustCompile(uri string) *gojsonschema.Schema {
return s
}
func validate(s *gojsonschema.Schema, b []byte) error {
res, err := s.Validate(gojsonschema.NewStringLoader(string(b)))
if err != nil {
return err
}
if len(res.Errors()) > 0 {
var l []string
for _, e := range res.Errors() {
l = append(l, e.String())
}
return fmt.Errorf("validation failed: %v", strings.Join(l, ", "))
}
return nil
}
const (
SettingsTierCommunity = "community"
@@ -623,12 +608,6 @@ const (
TaskTypeAutomation = "automation"
TaskFormTypeTask = "task"
TaskFormTypeInput = "input"
TaskFormTypeAutomation = "automation"
TaskResponseTypeTask = "task"
TaskResponseTypeInput = "input"

View File

@@ -38,7 +38,7 @@ func (s *Service) GetJob(ctx context.Context, id string) (*model.JobResponse, er
return s.database.JobGet(ctx, id)
}
func (s *Service) UpdateJob(ctx context.Context, id string, job *model.Job) (doc *model.JobResponse, err error) {
func (s *Service) UpdateJob(ctx context.Context, id string, job *model.JobUpdate) (doc *model.JobResponse, err error) {
defer s.publishRequest(ctx, err, "UpdateJob", jobResponseID(doc))
return s.database.JobUpdate(ctx, id, job)
}

View File

@@ -2,12 +2,15 @@ package service
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/arangodb/go-driver"
"github.com/google/uuid"
"github.com/SecurityBrewery/catalyst/database"
"github.com/SecurityBrewery/catalyst/generated/api"
"github.com/SecurityBrewery/catalyst/generated/model"
)
@@ -63,8 +66,11 @@ func (s *Service) CreateTicket(ctx context.Context, form *model.TicketForm) (doc
return nil, err
}
func (s *Service) CreateTicketBatch(ctx context.Context, forms []*model.TicketForm) error {
createdTickets, err := s.database.TicketBatchCreate(ctx, forms)
func (s *Service) CreateTicketBatch(ctx context.Context, ticketFormArray *model.TicketFormArray) error {
if ticketFormArray == nil {
return &api.HTTPError{Status: http.StatusUnprocessableEntity, Internal: errors.New("no tickets given")}
}
createdTickets, err := s.database.TicketBatchCreate(ctx, *ticketFormArray)
defer s.publishRequest(ctx, err, "CreateTicket", ticketIDs(createdTickets))
return err
}
@@ -140,9 +146,14 @@ func (s *Service) RemoveTicketPlaybook(ctx context.Context, i int64, s2 string)
return s.database.RemoveTicketPlaybook(ctx, i, s2)
}
func (s *Service) SetTask(ctx context.Context, i int64, s3 string, s2 string, task *model.Task) (doc *model.TicketWithTickets, err error) {
func (s *Service) SetTaskData(ctx context.Context, i int64, s3 string, s2 string, data map[string]interface{}) (doc *model.TicketWithTickets, err error) {
defer s.publishRequest(ctx, err, "SetTask", ticketWithTicketsID(doc))
return s.database.TaskUpdate(ctx, i, s3, s2, task)
return s.database.TaskUpdateData(ctx, i, s3, s2, data)
}
func (s *Service) SetTaskOwner(ctx context.Context, i int64, s3 string, s2 string, owner string) (doc *model.TicketWithTickets, err error) {
defer s.publishRequest(ctx, err, "SetTask", ticketWithTicketsID(doc))
return s.database.TaskUpdateOwner(ctx, i, s3, s2, owner)
}
func (s *Service) CompleteTask(ctx context.Context, i int64, s3 string, s2 string, m map[string]interface{}) (doc *model.TicketWithTickets, err error) {
@@ -155,9 +166,12 @@ func (s *Service) RunTask(ctx context.Context, i int64, s3 string, s2 string) (e
return s.database.TaskRun(ctx, i, s3, s2)
}
func (s *Service) SetReferences(ctx context.Context, i int64, references []*model.Reference) (doc *model.TicketWithTickets, err error) {
func (s *Service) SetReferences(ctx context.Context, i int64, references *model.ReferenceArray) (doc *model.TicketWithTickets, err error) {
if references == nil {
return nil, &api.HTTPError{Status: http.StatusUnprocessableEntity, Internal: errors.New("no references given")}
}
defer s.publishRequest(ctx, err, "SetReferences", ticketID(i))
return s.database.SetReferences(ctx, i, references)
return s.database.SetReferences(ctx, i, *references)
}
func (s *Service) SetSchema(ctx context.Context, i int64, s2 string) (doc *model.TicketWithTickets, err error) {

235
test/backup_test.go Normal file
View File

@@ -0,0 +1,235 @@
package test
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"io"
"log"
"mime/multipart"
"net/http"
"net/http/httptest"
"regexp"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/stretchr/testify/assert"
"github.com/SecurityBrewery/catalyst"
"github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/pointer"
)
func TestBackupAndRestore(t *testing.T) {
log.SetFlags(log.LstdFlags | log.Lshortfile)
type want struct {
status int
}
tests := []struct {
name string
want want
}{
{name: "Backup", want: want{status: http.StatusOK}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, _, server, err := Catalyst(t)
if err != nil {
t.Fatal(err)
}
if err := SetupTestData(ctx, server.DB); err != nil {
t.Fatal(err)
}
createFile(ctx, server)
zipB := assertBackup(t, server)
assertZipFile(t, readZipFile(t, zipB))
clearAllDatabases(server)
_, err = server.DB.UserCreateSetupAPIKey(ctx, "test")
if err != nil {
log.Fatal(err)
}
deleteAllBuckets(t, server)
assertRestore(t, zipB, server)
assertTicketExists(t, server)
assertFileExists(t, server)
})
}
}
func assertBackup(t *testing.T, server *catalyst.Server) []byte {
// setup request
req := httptest.NewRequest(http.MethodGet, "/api/backup/create", nil)
req.Header.Set("PRIVATE-TOKEN", "test")
// run request
backupRequestRecorder := httptest.NewRecorder()
server.Server.ServeHTTP(backupRequestRecorder, req)
backupResult := backupRequestRecorder.Result()
// assert results
assert.Equal(t, http.StatusOK, backupResult.StatusCode)
zipBuf := &bytes.Buffer{}
if _, err := io.Copy(zipBuf, backupResult.Body); err != nil {
t.Fatal(err)
}
assert.NoError(t, backupResult.Body.Close())
return zipBuf.Bytes()
}
func assertZipFile(t *testing.T, r *zip.Reader) {
var names []string
for _, f := range r.File {
names = append(names, f.Name)
}
if !includes(t, names, "minio/catalyst-8125/test.txt") {
t.Error("Minio file missing")
}
for _, p := range []string{
"arango/ENCRYPTION", "arango/automations_.*.data.json.gz", "arango/automations_.*.structure.json", "arango/dump.json", "arango/jobs_.*.data.json.gz", "arango/jobs_.*.structure.json", "arango/logs_.*.data.json.gz", "arango/logs_.*.structure.json", "arango/migrations_.*.data.json.gz", "arango/migrations_.*.structure.json", "arango/playbooks_.*.data.json.gz", "arango/playbooks_.*.structure.json", "arango/related_.*.data.json.gz", "arango/related_.*.structure.json", "arango/templates_.*.data.json.gz", "arango/templates_.*.structure.json", "arango/tickets_.*.data.json.gz", "arango/tickets_.*.structure.json", "arango/tickettypes_.*.data.json.gz", "arango/tickettypes_.*.structure.json", "arango/userdata_.*.data.json.gz", "arango/userdata_.*.structure.json", "arango/users_.*.data.json.gz", "arango/users_.*.structure.json",
} {
if !includes(t, names, p) {
t.Errorf("Arango file missing: %s", p)
}
}
}
func clearAllDatabases(server *catalyst.Server) {
server.DB.Truncate(context.Background())
}
func deleteAllBuckets(t *testing.T, server *catalyst.Server) {
buckets, err := server.Storage.S3().ListBuckets(&s3.ListBucketsInput{})
for _, bucket := range buckets.Buckets {
server.Storage.S3().DeleteBucket(&s3.DeleteBucketInput{
Bucket: bucket.Name,
})
}
if err != nil {
t.Fatal(err)
}
}
func assertRestore(t *testing.T, zipB []byte, server *catalyst.Server) {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("backup", "backup.zip")
if err != nil {
log.Fatal(err)
}
_, err = fileWriter.Write(zipB)
if err != nil {
log.Fatal(err)
}
assert.NoError(t, bodyWriter.Close())
req := httptest.NewRequest(http.MethodPost, "/api/backup/restore", bodyBuf)
req.Header.Set("PRIVATE-TOKEN", "test")
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
// run request
restoreRequestRecorder := httptest.NewRecorder()
server.Server.ServeHTTP(restoreRequestRecorder, req)
restoreResult := restoreRequestRecorder.Result()
if !assert.Equal(t, http.StatusOK, restoreResult.StatusCode) {
b, _ := io.ReadAll(restoreResult.Body)
log.Println(string(b))
t.FailNow()
}
}
func createFile(ctx context.Context, server *catalyst.Server) {
buf := bytes.NewBufferString("test text")
server.Storage.S3().CreateBucket(&s3.CreateBucketInput{Bucket: pointer.String("catalyst-8125")})
if _, err := server.Storage.Uploader().Upload(&s3manager.UploadInput{Body: buf, Bucket: pointer.String("catalyst-8125"), Key: pointer.String("test.txt")}); err != nil {
log.Fatal(err)
}
if _, err := server.DB.AddFile(ctx, 8125, &model.File{Key: "test.txt", Name: "test.txt"}); err != nil {
log.Fatal(err)
}
}
func assertTicketExists(t *testing.T, server *catalyst.Server) {
req := httptest.NewRequest(http.MethodGet, "/api/tickets/8125", nil)
req.Header.Set("PRIVATE-TOKEN", "test")
// run request
backupRequestRecorder := httptest.NewRecorder()
server.Server.ServeHTTP(backupRequestRecorder, req)
backupResult := backupRequestRecorder.Result()
// assert results
assert.Equal(t, http.StatusOK, backupResult.StatusCode)
zipBuf := &bytes.Buffer{}
if _, err := io.Copy(zipBuf, backupResult.Body); err != nil {
t.Fatal(err)
}
assert.NoError(t, backupResult.Body.Close())
var ticket model.Ticket
assert.NoError(t, json.Unmarshal(zipBuf.Bytes(), &ticket))
assert.Equal(t, "phishing from selenafadel@von.com detected", ticket.Name)
}
func assertFileExists(t *testing.T, server *catalyst.Server) {
obj, err := server.Storage.S3().GetObject(&s3.GetObjectInput{
Bucket: aws.String("catalyst-8125"),
Key: aws.String("test.txt"),
})
assert.NoError(t, err)
b, err := io.ReadAll(obj.Body)
assert.NoError(t, err)
assert.Equal(t, "test text", string(b))
}
func includes(t *testing.T, names []string, s string) bool {
for _, name := range names {
match, err := regexp.MatchString(s, name)
if err != nil {
t.Fatal(err)
}
if match {
return true
}
}
return false
}
func readZipFile(t *testing.T, b []byte) *zip.Reader {
buf := bytes.NewReader(b)
zr, err := zip.NewReader(buf, int64(buf.Len()))
if err != nil {
t.Fatal(string(b), err)
}
return zr
}

49
test/job_test.go Normal file
View File

@@ -0,0 +1,49 @@
package test
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/SecurityBrewery/catalyst/generated/model"
)
func TestJob(t *testing.T) {
_, _, _, _, _, _, _, server, cleanup, err := Server(t)
if err != nil {
t.Fatal(err)
}
defer cleanup()
// server.ConfigureRoutes()
w := httptest.NewRecorder()
// setup request
var req *http.Request
b, err := json.Marshal(model.JobForm{
Automation: "hash.sha1",
Origin: nil,
Payload: nil,
})
if err != nil {
t.Fatal(err)
}
req = httptest.NewRequest(http.MethodPost, "/jobs", bytes.NewBuffer(b))
req.Header.Set("Content-Type", "application/json")
// run request
server.ServeHTTP(w, req)
result := w.Result()
// assert results
if result.StatusCode != http.StatusNoContent {
t.Fatalf("Status got = %v, want %v", result.Status, http.StatusNoContent)
}
// if tt.want.status != http.StatusNoContent {
// jsonEqual(t, result.Body, tt.want.body)
// }
}

View File

@@ -1,31 +1,20 @@
package test
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"io"
"log"
"mime/multipart"
"net/http"
"net/http/httptest"
"regexp"
"strings"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
"github.com/SecurityBrewery/catalyst"
"github.com/SecurityBrewery/catalyst/generated/api"
"github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/pointer"
ctime "github.com/SecurityBrewery/catalyst/time"
)
@@ -75,7 +64,7 @@ func TestServer(t *testing.T) {
if result.StatusCode != tt.Want.Status {
msg, _ := io.ReadAll(result.Body)
t.Fatalf("Status got = %v, want %v: %s", result.Status, tt.Want.Status, msg)
t.Fatalf("Status got = %v (%s), want %v", result.Status, msg, tt.Want.Status)
}
if tt.Want.Status != http.StatusNoContent {
jsonEqual(t, result.Body, tt.Want.Body)
@@ -84,276 +73,6 @@ func TestServer(t *testing.T) {
}
}
func TestService(t *testing.T) {
type args struct {
method string
url string
data interface{}
}
type want struct {
status int
body interface{}
}
tests := []struct {
name string
args args
want want
}{
{name: "GetUser not existing", args: args{method: http.MethodGet, url: "/users/123"}, want: want{status: http.StatusNotFound, body: map[string]string{"error": "document not found"}}},
{name: "ListUsers", args: args{method: http.MethodGet, url: "/users"}, want: want{status: http.StatusOK}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, _, _, _, _, _, _, server, cleanup, err := Server(t)
if err != nil {
t.Fatal(err)
}
defer cleanup()
// server.ConfigureRoutes()
w := httptest.NewRecorder()
// setup request
var req *http.Request
if tt.args.data != nil {
b, err := json.Marshal(tt.args.data)
if err != nil {
t.Fatal(err)
}
req = httptest.NewRequest(tt.args.method, tt.args.url, bytes.NewBuffer(b))
req.Header.Set("Content-Type", "application/json")
} else {
req = httptest.NewRequest(tt.args.method, tt.args.url, nil)
}
// run request
server.ServeHTTP(w, req)
result := w.Result()
// assert results
if result.StatusCode != tt.want.status {
t.Fatalf("Status got = %v, want %v", result.Status, tt.want.status)
}
if tt.want.status != http.StatusNoContent {
jsonEqual(t, result.Body, tt.want.body)
}
})
}
}
func TestBackupAndRestore(t *testing.T) {
log.SetFlags(log.LstdFlags | log.Lshortfile)
type want struct {
status int
}
tests := []struct {
name string
want want
}{
{name: "Backup", want: want{status: http.StatusOK}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, _, server, err := Catalyst(t)
if err != nil {
t.Fatal(err)
}
if err := SetupTestData(ctx, server.DB); err != nil {
t.Fatal(err)
}
createFile(ctx, server)
zipB := assertBackup(t, server)
assertZipFile(t, readZipFile(t, zipB))
clearAllDatabases(server)
_, err = server.DB.UserCreateSetupAPIKey(ctx, "test")
if err != nil {
log.Fatal(err)
}
deleteAllBuckets(t, server)
assertRestore(t, zipB, server)
assertTicketExists(t, server)
assertFileExists(t, server)
})
}
}
func assertBackup(t *testing.T, server *catalyst.Server) []byte {
// setup request
req := httptest.NewRequest(http.MethodGet, "/api/backup/create", nil)
req.Header.Set("PRIVATE-TOKEN", "test")
// run request
backupRequestRecorder := httptest.NewRecorder()
server.Server.ServeHTTP(backupRequestRecorder, req)
backupResult := backupRequestRecorder.Result()
// assert results
assert.Equal(t, http.StatusOK, backupResult.StatusCode)
zipBuf := &bytes.Buffer{}
if _, err := io.Copy(zipBuf, backupResult.Body); err != nil {
t.Fatal(err)
}
assert.NoError(t, backupResult.Body.Close())
return zipBuf.Bytes()
}
func assertZipFile(t *testing.T, r *zip.Reader) {
var names []string
for _, f := range r.File {
names = append(names, f.Name)
}
if !includes(t, names, "minio/catalyst-8125/test.txt") {
t.Error("Minio file missing")
}
for _, p := range []string{
"arango/ENCRYPTION", "arango/automations_.*.data.json.gz", "arango/automations_.*.structure.json", "arango/dump.json", "arango/jobs_.*.data.json.gz", "arango/jobs_.*.structure.json", "arango/logs_.*.data.json.gz", "arango/logs_.*.structure.json", "arango/migrations_.*.data.json.gz", "arango/migrations_.*.structure.json", "arango/playbooks_.*.data.json.gz", "arango/playbooks_.*.structure.json", "arango/related_.*.data.json.gz", "arango/related_.*.structure.json", "arango/templates_.*.data.json.gz", "arango/templates_.*.structure.json", "arango/tickets_.*.data.json.gz", "arango/tickets_.*.structure.json", "arango/tickettypes_.*.data.json.gz", "arango/tickettypes_.*.structure.json", "arango/userdata_.*.data.json.gz", "arango/userdata_.*.structure.json", "arango/users_.*.data.json.gz", "arango/users_.*.structure.json",
} {
if !includes(t, names, p) {
t.Errorf("Arango file missing: %s", p)
}
}
}
func clearAllDatabases(server *catalyst.Server) {
server.DB.Truncate(context.Background())
}
func deleteAllBuckets(t *testing.T, server *catalyst.Server) {
buckets, err := server.Storage.S3().ListBuckets(&s3.ListBucketsInput{})
for _, bucket := range buckets.Buckets {
server.Storage.S3().DeleteBucket(&s3.DeleteBucketInput{
Bucket: bucket.Name,
})
}
if err != nil {
t.Fatal(err)
}
}
func assertRestore(t *testing.T, zipB []byte, server *catalyst.Server) {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("backup", "backup.zip")
if err != nil {
log.Fatal(err)
}
_, err = fileWriter.Write(zipB)
if err != nil {
log.Fatal(err)
}
assert.NoError(t, bodyWriter.Close())
req := httptest.NewRequest(http.MethodPost, "/api/backup/restore", bodyBuf)
req.Header.Set("PRIVATE-TOKEN", "test")
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
// run request
restoreRequestRecorder := httptest.NewRecorder()
server.Server.ServeHTTP(restoreRequestRecorder, req)
restoreResult := restoreRequestRecorder.Result()
if !assert.Equal(t, http.StatusOK, restoreResult.StatusCode) {
b, _ := io.ReadAll(restoreResult.Body)
log.Println(string(b))
t.FailNow()
}
}
func createFile(ctx context.Context, server *catalyst.Server) {
buf := bytes.NewBufferString("test text")
server.Storage.S3().CreateBucket(&s3.CreateBucketInput{Bucket: pointer.String("catalyst-8125")})
if _, err := server.Storage.Uploader().Upload(&s3manager.UploadInput{Body: buf, Bucket: pointer.String("catalyst-8125"), Key: pointer.String("test.txt")}); err != nil {
log.Fatal(err)
}
if _, err := server.DB.AddFile(ctx, 8125, &model.File{Key: "test.txt", Name: "test.txt"}); err != nil {
log.Fatal(err)
}
}
func assertTicketExists(t *testing.T, server *catalyst.Server) {
req := httptest.NewRequest(http.MethodGet, "/api/tickets/8125", nil)
req.Header.Set("PRIVATE-TOKEN", "test")
// run request
backupRequestRecorder := httptest.NewRecorder()
server.Server.ServeHTTP(backupRequestRecorder, req)
backupResult := backupRequestRecorder.Result()
// assert results
assert.Equal(t, http.StatusOK, backupResult.StatusCode)
zipBuf := &bytes.Buffer{}
if _, err := io.Copy(zipBuf, backupResult.Body); err != nil {
t.Fatal(err)
}
assert.NoError(t, backupResult.Body.Close())
var ticket model.Ticket
assert.NoError(t, json.Unmarshal(zipBuf.Bytes(), &ticket))
assert.Equal(t, "phishing from selenafadel@von.com detected", ticket.Name)
}
func assertFileExists(t *testing.T, server *catalyst.Server) {
obj, err := server.Storage.S3().GetObject(&s3.GetObjectInput{
Bucket: aws.String("catalyst-8125"),
Key: aws.String("test.txt"),
})
assert.NoError(t, err)
b, err := io.ReadAll(obj.Body)
assert.NoError(t, err)
assert.Equal(t, "test text", string(b))
}
func includes(t *testing.T, names []string, s string) bool {
for _, name := range names {
match, err := regexp.MatchString(s, name)
if err != nil {
t.Fatal(err)
}
if match {
return true
}
}
return false
}
func readZipFile(t *testing.T, b []byte) *zip.Reader {
buf := bytes.NewReader(b)
zr, err := zip.NewReader(buf, int64(buf.Len()))
if err != nil {
t.Fatal(string(b), err)
}
return zr
}
func jsonEqual(t *testing.T, got io.Reader, want interface{}) {
var gotObject, wantObject interface{}

68
test/user_test.go Normal file
View File

@@ -0,0 +1,68 @@
package test
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func TestUser(t *testing.T) {
type args struct {
method string
url string
data interface{}
}
type want struct {
status int
body interface{}
}
tests := []struct {
name string
args args
want want
}{
{name: "GetUser not existing", args: args{method: http.MethodGet, url: "/users/123"}, want: want{status: http.StatusNotFound, body: map[string]string{"error": "document not found"}}},
{name: "ListUsers", args: args{method: http.MethodGet, url: "/users"}, want: want{status: http.StatusOK}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, _, _, _, _, _, _, server, cleanup, err := Server(t)
if err != nil {
t.Fatal(err)
}
defer cleanup()
// server.ConfigureRoutes()
w := httptest.NewRecorder()
// setup request
var req *http.Request
if tt.args.data != nil {
b, err := json.Marshal(tt.args.data)
if err != nil {
t.Fatal(err)
}
req = httptest.NewRequest(tt.args.method, tt.args.url, bytes.NewBuffer(b))
req.Header.Set("Content-Type", "application/json")
} else {
req = httptest.NewRequest(tt.args.method, tt.args.url, nil)
}
// run request
server.ServeHTTP(w, req)
result := w.Result()
// assert results
if result.StatusCode != tt.want.status {
t.Fatalf("Status got = %v, want %v", result.Status, tt.want.status)
}
if tt.want.status != http.StatusNoContent {
jsonEqual(t, result.Body, tt.want.body)
}
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@
import { Configuration } from "./configuration";
// Some imports not used depending on template conditions
// @ts-ignore
import globalAxios, { AxiosPromise, AxiosInstance } from 'axios';
import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
export const BASE_PATH = "http://./api".replace(/\/+$/, "");
@@ -38,7 +38,7 @@ export const COLLECTION_FORMATS = {
*/
export interface RequestArgs {
url: string;
options: any;
options: AxiosRequestConfig;
}
/**

View File

@@ -14,8 +14,8 @@
import { Configuration } from "./configuration";
import { RequiredError, RequestArgs } from "./base";
import { AxiosInstance } from 'axios';
import { RequiredError, RequestArgs } from "./base";
import { AxiosInstance, AxiosResponse } from 'axios';
/**
*
@@ -131,8 +131,8 @@ export const toPathString = function (url: URL) {
* @export
*/
export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url};
return axios.request(axiosRequestArgs);
return axios.request<T, R>(axiosRequestArgs);
};
}

View File

@@ -40,10 +40,10 @@ export default Vue.extend({
return icon;
},
statusColor: function () {
let color = TypeColorEnum.Info;
let color = TypeColorEnum.Info as TypeColorEnum;
this.lodash.forEach(this.$store.state.settings.artifactStates, (state: Type) => {
if (this.artifact.status === state.id && state.color) {
color = state.color
color = state.color;
}
})
return color;

View File

@@ -1,4 +1,4 @@
// Generated from CAQLLexer.g4 by ANTLR 4.9.2
// Generated from CAQLLexer.g4 by ANTLR 4.9.3
// jshint ignore: start
import antlr4 from 'antlr4';

View File

@@ -1,4 +1,4 @@
// Generated from CAQLParser.g4 by ANTLR 4.9.2
// Generated from CAQLParser.g4 by ANTLR 4.9.3
// jshint ignore: start
import antlr4 from 'antlr4';
import CAQLParserListener from './CAQLParserListener.js';

View File

@@ -1,4 +1,4 @@
// Generated from CAQLParser.g4 by ANTLR 4.9.2
// Generated from CAQLParser.g4 by ANTLR 4.9.3
// jshint ignore: start
import antlr4 from 'antlr4';

View File

@@ -203,7 +203,7 @@ export default Vue.extend({
return icon;
},
statusColor: function (status: string) {
let color = TypeColorEnum.Info;
let color = TypeColorEnum.Info as TypeColorEnum;
this.lodash.forEach(this.$store.state.settings.artifactStates, (state: Type) => {
if (status === state.id && state.color) {
color = state.color

View File

@@ -152,7 +152,7 @@ export default Vue.extend({
}
},
loadAutomations() {
API.listAutomations(this.$route.params.id).then((response) => {
API.listAutomations().then((response) => {
this.automations = response.data;
});
},

View File

@@ -1145,30 +1145,13 @@ export default Vue.extend({
this.selectedTask = undefined;
this.selectedTaskPlaybook = undefined;
},
toTaskForm(task: TaskResponse): Task {
return {
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.toString() as TaskTypeEnum,
} as Task
},
save(playbookID: string, taskID: string) {
if (!this.ticket || !this.ticket.id || !this.ticket.playbooks) {
return;
}
let task = this.ticket.playbooks[playbookID].tasks[taskID]
task.data = this.tdata[playbookID.toString() + "-" + taskID];
API.setTask(this.ticket.id, playbookID, taskID, this.toTaskForm(task)).then((response) => {
let data = this.tdata[playbookID.toString() + "-" + taskID];
API.setTaskData(this.ticket.id, playbookID, taskID, data).then((response) => {
this.$store.dispatch("alertSuccess", { name: "Task saved" });
this.setTicket(response.data);
});
@@ -1181,9 +1164,7 @@ export default Vue.extend({
return;
}
let task = this.ticket.playbooks[playbookID].tasks[taskID]
task.owner = owner
API.setTask(this.ticket.id, playbookID, taskID, this.toTaskForm(task)).then((response) => {
API.setTaskOwner(this.ticket.id, playbookID, taskID, owner).then((response) => {
this.$store.dispatch("alertSuccess", { name: "Owner saved" });
this.setTicket(response.data);
if (response.data.playbooks) {

View File

@@ -49,7 +49,7 @@
<script lang="ts">
import Vue from "vue";
import { NewUserResponse, UserResponse } from "../client";
import { NewUserResponse, UserResponse } from "@/client";
import {API} from "@/services/api";
interface State {