Skip to content

Commit 49d0c92

Browse files
Feature/272 manual step api implementation (#275)
Co-authored-by: Maarten de Kruijf <[email protected]>
1 parent e960dbc commit 49d0c92

File tree

18 files changed

+2106
-83
lines changed

18 files changed

+2106
-83
lines changed

docs/content/en/docs/core-components/api-manual.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ We will use HTTP status codes https://en.wikipedia.org/wiki/List_of_HTTP_status_
1515

1616
```plantuml
1717
@startuml
18-
protocol Reporter {
18+
protocol Manual {
1919
GET /manual
20+
GET /manual/{execution-id}/{step-id}
2021
POST /manual/continue
2122
}
2223
@enduml
@@ -33,7 +34,7 @@ Get all pending manual actions objects that are currently waiting in SOARCA.
3334
None
3435

3536
##### Response
36-
200/OK with payload list of:
37+
200/OK with body a list of:
3738

3839

3940

@@ -45,8 +46,8 @@ None
4546
|step_id |UUID |string |The id of the step executed by the execution
4647
|description |description of the step|string |The description from the workflow step
4748
|command |command |string |The command for the agent either command
48-
|command_is_base64 |true \| false |bool |Indicate the command is in base 64
49-
|targets |cacao agent-target |dictionary |Map of [cacao agent-target](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256509) with the target(s) of this command
49+
|command_is_base64 |true \| false |bool |Indicates if the command is in Base64
50+
|target |cacao agent-target |object |Map of [cacao agent-target](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256509) with the target(s) of this command
5051
|out_args |cacao variables |dictionary |Map of [cacao variables](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256555) handled in the step out args with current values and definitions
5152

5253

@@ -97,7 +98,7 @@ Get pending manual actions objects that are currently waiting in SOARCA for spec
9798
None
9899

99100
##### Response
100-
200/OK with payload:
101+
200/OK with body:
101102

102103

103104

@@ -109,7 +110,7 @@ None
109110
|step_id |UUID |string |The id of the step executed by the execution
110111
|description |description of the step|string |The description from the workflow step
111112
|command |command |string |The command for the agent either command
112-
|command_is_base64 |true \| false |bool |Indicate the command is in base 64
113+
|command_is_base64 |true \| false |bool |Indicates if the command is in Base64
113114
|targets |cacao agent-target |dictionary |Map of [cacao agent-target](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256509) with the target(s) of this command
114115
|out_args |cacao variables |dictionary |Map of [cacao variables](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256555) handled in the step out args with current values and definitions
115116

@@ -154,7 +155,7 @@ None
154155
General error
155156

156157
#### POST `/manual/continue`
157-
Respond to manual command pending in SOARCA, if out_args are defined they must be filled in and returned in the payload body. Only value is required in the response of the variable. You can however return the entire object. Of the object does not match the original out_arg the call we be considered as failed.
158+
Respond to manual command pending in SOARCA, if out_args are defined they must be filled in and returned in the payload body. Only value is required in the response of the variable. You can however return the entire object. If the object does not match the original out_arg, the call we be considered as failed.
158159

159160
##### Call payload
160161
|field |content |type | description |
@@ -163,9 +164,8 @@ Respond to manual command pending in SOARCA, if out_args are defined they must b
163164
|execution_id |UUID |string |The id of the execution
164165
|playbook_id |UUID |string |The id of the CACAO playbook executed by the execution
165166
|step_id |UUID |string |The id of the step executed by the execution
166-
|response_status |enum |string |Can be either `success` or `failed`
167-
|response_out_args |cacao variables |dictionary |Map of [cacao variables](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256555) handled in the step out args with current values and definitions
168-
167+
|response_status |enum |string |`success` indicates successfull fulfilment of the manual request. `failure` indicates failed satisfaction of the request
168+
|response_out_args |cacao variables |dictionary |Map of cacao variables names to cacao variable struct. Only name, type, and value are mandatory
169169

170170

171171
```plantuml
@@ -176,15 +176,15 @@ Respond to manual command pending in SOARCA, if out_args are defined they must b
176176
"execution_id" : "<execution-id>",
177177
"playbook_id" : "<playbook-id>",
178178
"step_id" : "<step-id>",
179-
"response_status" : "success | failed",
179+
"response_status" : "success | failure",
180180
"response_out_args": {
181181
"<variable-name-1>" : {
182-
"type": "<type>",
182+
"type": "<variable-type>",
183183
"name": "<variable-name>",
184-
"description": "<description>",
185184
"value": "<value>",
186-
"constant": "<true/false>",
187-
"external": "<true/false>"
185+
"description": "<description> (ignored)",
186+
"constant": "<true/false> (ignored)",
187+
"external": "<true/false> (ignored)"
188188
}
189189
}
190190
}
@@ -193,7 +193,8 @@ Respond to manual command pending in SOARCA, if out_args are defined they must b
193193
```
194194

195195
##### Response
196-
200/OK with payload:
196+
200/OK with payload:
197+
Generic execution information
197198

198199
##### Error
199200
400/BAD REQUEST with payload:

docs/content/en/docs/core-components/modules.md

Lines changed: 183 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,20 +322,199 @@ This example will start an operation that executes the ability with ID `36eecb80
322322
```
323323

324324
### Manual capability
325-
This capability executes [manual Commands](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256491) and provides them through the [SOARCA api](/docs/core-components/api-manual).
325+
This capability executes [manual Commands](https://docs.oasis-open.org/cacao/security-playbooks/v2.0/cs01/security-playbooks-v2.0-cs01.html#_Toc152256491) and provides them natively through the [SOARCA api](/docs/core-components/api-manual), though other integrations are possible.
326326

327327

328-
<!-- The manual capability will allow an operator to interact with a playbook. It could allow one to perform a manual step that could not be automated, enter a variable to the playbook execution or a combination of these operations.
328+
The manual capability will allow an operator to interact with a playbook. It could allow one to perform a manual step that could not be automated, enter a variable to the playbook execution or a combination of these operations.
329329

330-
The main way to interact with the manual step is through SOARCA's [manual api](/docs/core-components/api-manual). The manual step should provide a timeout SOARCA will by default use a timeout of 10 minutes. If a timeout occurs the step is considered as failed. -->
330+
The manual step should provide a timeout. SOARCA will by default use a timeout of 10 minutes. If a timeout occurs, the step is considered as failed.
331331

332+
#### Manual capability architecture
332333

334+
In essence, executing a manual command involves the following actions:
335+
1. A message, the `command` of a manual command, is posted *somewhere*, *somehow*, together with the variables of which values is expected to be assigned or updated (if any).
336+
2. The playbook execution stops, waiting for *something* to respond to the message with the variables values.
337+
3. Once something replies, the variables are streamed inside the playbook execution and handled accordingly.
333338

339+
It should be possible to post a manual command message anywhere and in any way, and allow anything to respond back. Hence, SOARCA adopts a flexible architecture to accomodate different ways of manual *interactions*. Below a view of the architecture.
334340

341+
When a playbook execution hits an Action step with a manual command, the *ManualCapability* will queue the instruction into the *CapabilityInteraction* module. The module does essentially three things:
342+
1. it stores the status of the manual command, and implements the SOARCA API interactions with the manual command.
343+
2. If manual integrations are defined for the SOARCA instance, the *CapabilityInteraction* module notifies the manual integration modules, so that they can handle the manual command in turn.
344+
3. It waits for the manual command to be satisfied either via SOARCA APIs, or via manual integrations. The first to respond amongst the two, resolves the manual command. The resolution of the command may or may not assign new values to variables in the playbook. Finally the *CapabilityInteraction* module replies to the *ManualCommand* module.
345+
346+
Ultimately the *ManualCapability* then completes its execution, having eventually updated the values for the variables in the outArgs of the command. Timeouts or errors are handled opportunely.
347+
348+
```plantuml
349+
@startuml
350+
set separator ::
351+
352+
class ManualCommand
353+
354+
protocol ManualAPI {
355+
GET /manual
356+
GET /manual/{exec-id}/{step-id}
357+
POST /manual/continue
358+
}
359+
360+
interface ICapability{
361+
Execute()
362+
}
363+
364+
interface ICapabilityInteraction{
365+
Queue(command InteractionCommand, manualComms ManualCapabilityCommunication)
366+
}
367+
368+
interface IInteracionStorage{
369+
GetPendingCommands() []CommandData
370+
GetPendingCommand(execution.metadata) CommandData
371+
PostContinue(execution.metadata) ExecutionInfo
372+
}
373+
374+
interface IInteractionIntegrationNotifier {
375+
Notify(command InteractionIntegrationCommand, channel manualCapabilityCommunication.Channel)
376+
}
377+
378+
class Interaction {
379+
notifiers []IInteractionIntegrationNotifier
380+
storage map[executionId]map[stepId]InteractionStorageEntry
381+
}
382+
class ThirdPartyManualIntegration
383+
384+
385+
ManualCommand .up.|> ICapability
386+
ManualCommand -down-> ICapabilityInteraction
387+
Interaction .up.|> ICapabilityInteraction
388+
Interaction .up.|> IInteracionStorage
389+
390+
ManualAPI -down-> IInteracionStorage
391+
392+
Interaction -right-> IInteractionIntegrationNotifier
393+
ThirdPartyManualIntegration .up.|> IInteractionIntegrationNotifier
394+
395+
396+
```
397+
398+
The default and internally-supported way to interact with the manual step is through SOARCA's [manual api](/docs/core-components/api-manual).
399+
Besides SOARCA's [manual api](/docs/core-components/api-manual), SOARCA is designed to allow for configuration of additional ways that a manual command should be executed. In particular, there can be *one* manual integration (besides the native manual APIs) per running SOARCA instance.
400+
Integration's code should implement the *IInteractionIntegrationNotifier* interface, returning the result of the manual command execution in form of an `InteractionIntegrationResponse` object, into the respective channel.
401+
402+
The diagram below displays in some detail the way the manual interactions components work.
403+
404+
```plantuml
405+
@startuml
406+
control "ManualCommand" as manual
407+
control "Interaction" as interaction
408+
control "ManualAPI" as api
409+
control "ThirdPartyManualIntegration" as 3ptool
410+
participant "Integration" as integration
411+
412+
-> manual : ...manual command
413+
manual -> interaction : Queue(command, capabilityChannel, timeoutContext)
414+
manual -> manual : idle wait on capabilityChannel
415+
activate manual
416+
417+
activate interaction
418+
interaction -> interaction : save pending manual command
419+
interaction ->> 3ptool : Notify(command, capabilityChannel, timeoutContext)
420+
3ptool <--> integration : custom handling command posting
421+
deactivate interaction
422+
423+
alt Command Response
424+
425+
group Native ManualAPI flow
426+
api -> interaction : GetPendingCommands()
427+
activate interaction
428+
activate api
429+
api -> interaction : GetPendingCommand(execution.metadata)
430+
api -> interaction : PostContinue(ManualOutArgsUpdate)
431+
interaction -> interaction : build InteractionResponse
432+
deactivate api
433+
interaction --> manual : capabilityChannel <- InteractionResponse
434+
manual ->> interaction : timeoutContext.Cancel() event
435+
interaction -> interaction : de-register pending command
436+
deactivate interaction
437+
manual ->> 3ptool : timeoutContext.Deadline() event
438+
activate 3ptool
439+
3ptool <--> integration : custom handling command completed
440+
deactivate manual
441+
<- manual : ...continue execution
442+
deactivate 3ptool
443+
deactivate integration
444+
end
445+
else
446+
group Third Party Integration flow
447+
integration --> 3ptool : custom handling command response
448+
activate manual
449+
activate integration
450+
deactivate integration
451+
activate 3ptool
452+
3ptool -> 3ptool : build InteractionResponse
453+
3ptool --> manual : capabilityChannel <- InteractionResponse
454+
deactivate 3ptool
455+
manual ->> interaction : timeoutContext.Cancel() event
456+
activate interaction
457+
interaction -> interaction : de-register pending command
458+
deactivate interaction
459+
manual ->> 3ptool : timeoutContext.Deadline() event
460+
activate 3ptool
461+
3ptool <--> integration : custom handling command completed
462+
deactivate 3ptool
463+
activate integration
464+
deactivate integration
465+
<- manual : ...continue execution
466+
deactivate manual
467+
deactivate integration
468+
end
469+
end
470+
471+
@enduml
472+
```
473+
474+
Note that whoever resolves the manual command first, whether via the manualAPI, or a third party integration, then the command results are returned to the workflow execution, and the manual command is removed from the pending list. Hence, if a manual command is resolved e.g. via the manual integration, a postContinue API call for that same command will not go through, as the command will have been resolved already, and hence removed from the registry of pending manual commands.
475+
476+
The diagram below shows instead what happens when a timeout occurs for the manual command.
477+
478+
```plantuml
479+
@startuml
480+
control "ManualCommand" as manual
481+
control "Interaction" as interaction
482+
control "ManualAPI" as api
483+
control "ThirdPartyManualIntegration" as 3ptool
484+
participant "Integration" as integration
485+
486+
-> manual : ...manual command
487+
manual -> interaction : Queue(command, capabilityChannel, timeoutContext)
488+
manual -> manual : idle wait on capabilityChannel
489+
activate manual
490+
491+
activate interaction
492+
interaction -> interaction : save pending manual command
493+
interaction ->> 3ptool : Notify(command, capabilityChannel, timeoutContext)
494+
3ptool --> integration : custom handling command posting
495+
deactivate interaction
496+
497+
group Command execution times out
498+
manual -> manual : timeoutContext.Deadline()
499+
manual ->> interaction : timeoutContext.Deadline() event
500+
manual ->> 3ptool : timeoutContext.Deadline() event
501+
3ptool --> integration : custom handling command timed-out view
502+
activate interaction
503+
interaction -> interaction : de-register pending command
504+
<- manual : ...continue execution
505+
deactivate manual
506+
...
507+
api -> interaction : GetPendingCommand(execution.metadata)
508+
interaction -> api : no pending command (404)
509+
end
510+
511+
512+
@enduml
513+
```
335514

336515
#### Success and failure
337516

338-
In SOARCA the manual step is considered successful if a response is made through the [manual api](/docs/core-components/api-manual). The manual command can specify a timeout but if none is specified SOARCA will use a default timeout of 10 minutes. If a timeout occurs the step is considered as failed and SOARCA will return an error to the decomposer.
517+
In SOARCA the manual step is considered successful if a response is made through the [manual api](/docs/core-components/api-manual), or an integration. The manual command can specify a timeout, but if none is specified SOARCA will use a default timeout of 10 minutes. If a timeout occurs the step is considered as failed and SOARCA will return an error to the decomposer.
339518

340519
#### Variables
341520

examples/manual-playbook.json

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"type": "playbook",
3+
"spec_version": "cacao-2.0",
4+
"id": "playbook--fe65ef7b-e8b1-4ed9-ba60-3c380ae5ab28",
5+
"name": "Example manual",
6+
"description": "This playbook is to demonstrate the manual command definition",
7+
"playbook_types": [
8+
"notification"
9+
],
10+
"created_by": "identity--ac3c0258-7a81-46e7-a2ae-d34b6d06cc54",
11+
"created": "2025-01-21T14:14:23.263Z",
12+
"modified": "2025-01-21T14:14:23.263Z",
13+
"revoked": false,
14+
"valid_from": "2023-11-20T15:56:00.123Z",
15+
"valid_until": "2123-11-20T15:56:00.123Z",
16+
"priority": 1,
17+
"severity": 1,
18+
"impact": 1,
19+
"labels": [
20+
"soarca",
21+
"manual"
22+
],
23+
"external_references": [
24+
{
25+
"description": "TNO COSSAS"
26+
}
27+
],
28+
"workflow_start": "start--9e7d62b2-88ac-4656-94e1-dbd4413ba008",
29+
"workflow_exception": "end--a6f0b81e-affb-4bca-b4f6-a2d5af908958",
30+
"workflow": {
31+
"start--9e7d62b2-88ac-4656-94e1-dbd4413ba008": {
32+
"name": "Start example flow for manual command",
33+
"on_completion": "action--eb9372d4-d524-49fc-bf24-be26ea084779",
34+
"type": "start"
35+
},
36+
"action--eb9372d4-d524-49fc-bf24-be26ea084779": {
37+
"name": "manual",
38+
"description": "Instruction to the operator to be executed manually",
39+
"step_variables": {
40+
"__hyperspeed_ready__": {
41+
"type": "string",
42+
"description": "set value to true or false when the request is completed",
43+
"constant": false,
44+
"external": false
45+
}
46+
},
47+
"on_completion": "end--a6f0b81e-affb-4bca-b4f6-a2d5af908958",
48+
"type": "action",
49+
"commands": [
50+
{
51+
"type": "manual",
52+
"command": "prepare Falcon for hyperspeed jump"
53+
}
54+
],
55+
"agent": "soarca-manual-capability--7b0e98db-fa93-42aa-8511-e871c65131b1",
56+
"targets": [
57+
"individual--9d1f6217-34d5-435c-b29a-6a1af6b664d9"
58+
],
59+
"out_args": [
60+
"__hyperspeed_ready__"
61+
]
62+
},
63+
"end--a6f0b81e-affb-4bca-b4f6-a2d5af908958": {
64+
"name": "End Flow",
65+
"type": "end"
66+
}
67+
},
68+
"agent_definitions": {
69+
"soarca--00040001-1000-1000-a000-000100010001": {
70+
"type": "soarca",
71+
"name": "soarca-manual-capability"
72+
},
73+
"soarca-manual-capability--7b0e98db-fa93-42aa-8511-e871c65131b1": {
74+
"type": "soarca-manual-capability",
75+
"name": "soarca-manual-capability",
76+
"description": "SOARCA's manual command handler"
77+
}
78+
},
79+
"target_definitions": {
80+
"individual--9d1f6217-34d5-435c-b29a-6a1af6b664d9": {
81+
"type": "individual",
82+
"name": "Luke Skywalker",
83+
"description": "Darth Vader's son",
84+
"location": {
85+
"name": "Somewhere in a galaxy far far away"
86+
}
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)