Skip to content

Commit

Permalink
correct manual documentation and playbook example
Browse files Browse the repository at this point in the history
  • Loading branch information
lucamrgs committed Jan 21, 2025
1 parent 8bc5d10 commit 699577b
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 31 deletions.
123 changes: 93 additions & 30 deletions docs/content/en/docs/core-components/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,18 +274,18 @@ The manual step should provide a timeout. SOARCA will by default use a timeout o
#### Manual capability architecture

In essence, executing a manual command involves the following actions:
1. A message, the `command` of a manual command, is posted *somewhere*, *somehow*, together with the variables expected to be filled.
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).
2. The playbook execution stops, waiting for *something* to respond to the message with the variables values.
3. The variables are streamed inside the playbook execution and handled accordingly.
3. Once something replies, the variables are streamed inside the playbook execution and handled accordingly.

Because the *somewhere* and *somehow* for posting a message can vary, and the *something* that replies can vary too, SOARCA adopts a flexible architecture to accomodate different ways of manual *interactions*. Below a view of the architecture.
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.

When a playbook execution hits an Action step with a Manual command, the manual command will queue the instruction into the *CapabilityInteraction* module. The module does essentially three things:
1. it stores the status of the manual command, and handles the SOARCA API interactions with the manual command.
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:
1. it stores the status of the manual command, and implements the SOARCA API interactions with the manual command.
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.
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.

Ultimately the *ManualCommand* then completes its execution, having eventually updated the values for the variables in the outArgs of the command. Timeouts or errors are handled opportunely.
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.

```plantuml
@startuml
Expand All @@ -310,11 +310,11 @@ interface ICapabilityInteraction{
interface IInteracionStorage{
GetPendingCommands() []CommandData
GetPendingCommand(execution.metadata) CommandData
PostContinue(execution.metadata) StatusCode
PostContinue(execution.metadata) ExecutionInfo
}
interface IInteractionIntegrationNotifier {
Notify(command InteractionIntegrationCommand, channel chan InteractionIntegrationResponse)
Notify(command InteractionIntegrationCommand, channel manualCapabilityCommunication.Channel)
}
class Interaction {
Expand Down Expand Up @@ -349,40 +349,103 @@ control "ManualCommand" as manual
control "Interaction" as interaction
control "ManualAPI" as api
control "ThirdPartyManualIntegration" as 3ptool
participant "Integration" as integration
manual -> interaction : Queue(command, capabilityChannel)
-> manual : ...manual command
manual -> interaction : Queue(command, capabilityChannel, timeoutContext)
manual -> manual : idle wait on capabilityChannel
activate interaction
interaction -> interaction : save manual command status
alt Third Party Integration flow
interaction ->> 3ptool : async Notify(interactionCommand, integrationChannel)
activate 3ptool
interaction ->> interaction : async wait on integrationChannel
3ptool <--> Integration : command posting and handling
3ptool -> 3ptool : post InteractionIntegrationResponse on channel
3ptool --> interaction : integrationChannel <- InteractionIntegrationResponse
interaction --> manual : capabilityChannel <- InteractionResponse
deactivate 3ptool
else Native ManualAPI flow
interaction ->> interaction : async wait on integrationChannel
api -> interaction : GetPendingCommands()
api -> interaction : GetPendingCommand(execution.metadata)
api -> interaction : PostContinue(InteractionResponse)
interaction --> manual : capabilityChannel <- InteractionResponse
end
activate manual
activate interaction
interaction -> interaction : save pending manual command
interaction ->> 3ptool : Notify(command, capabilityChannel, timeoutContext)
3ptool --> integration : custom handling command posting
deactivate interaction
alt Command Response
group Native ManualAPI flow
api -> interaction : GetPendingCommands()
activate interaction
activate api
api -> interaction : GetPendingCommand(execution.metadata)
api -> interaction : PostContinue(ManualOutArgsUpdate)
interaction -> interaction : build InteractionResponse
deactivate api
interaction --> manual : capabilityChannel <- InteractionResponse
manual ->> interaction : timeoutContext.Cancel() event
manual -->> 3ptool : timeoutContext.Deadline() event
3ptool --> integration : custom handling command timed-out view
interaction -> interaction : de-register pending command
deactivate interaction
deactivate manual
end
else
group Third Party Integration flow
activate manual
activate integration
integration --> 3ptool : custom handling command response
deactivate integration
activate 3ptool
3ptool -> 3ptool : build InteractionResponse
3ptool --> manual : capabilityChannel <- InteractionResponse
deactivate 3ptool
manual ->> interaction : timeoutContext.Cancel() event
activate interaction
interaction -> interaction : de-register pending command
deactivate interaction
deactivate 3ptool
deactivate manual
end
end
@enduml
```

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.

The diagram below shows instead what happens when a timeout occurs for the manual command.

```plantuml
@startuml
control "ManualCommand" as manual
control "Interaction" as interaction
control "ManualAPI" as api
control "ThirdPartyManualIntegration" as 3ptool
participant "Integration" as integration
-> manual : ...manual command
manual -> interaction : Queue(command, capabilityChannel, timeoutContext)
manual -> manual : idle wait on capabilityChannel
activate manual
activate interaction
interaction -> interaction : save pending manual command
interaction ->> 3ptool : Notify(command, capabilityChannel, timeoutContext)
3ptool --> integration : custom handling command posting
deactivate interaction
group Command execution times out
manual -> manual : timeoutContext.Deadline()
manual -->> interaction : timeoutContext.Deadline() event
manual -->> 3ptool : timeoutContext.Deadline() event
3ptool --> integration : custom handling command timed-out view
activate interaction
interaction -> interaction : de-register pending command
<- manual : ...continue execution
deactivate manual
...
api -> interaction : GetPendingCommand(execution.metadata)
interaction -> api : no pending command (404)
end
@enduml
```

#### Success and failure

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.
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.

#### Variables

Expand Down
89 changes: 89 additions & 0 deletions examples/manual-playbook.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"type": "playbook",
"spec_version": "cacao-2.0",
"id": "playbook--fe65ef7b-e8b1-4ed9-ba60-3c380ae5ab28",
"name": "Example manual",
"description": "This playbook is to demonstrate the manual command definition",
"playbook_types": [
"notification"
],
"created_by": "identity--ac3c0258-7a81-46e7-a2ae-d34b6d06cc54",
"created": "2025-01-21T14:14:23.263Z",
"modified": "2025-01-21T14:14:23.263Z",
"revoked": false,
"valid_from": "2023-11-20T15:56:00.123Z",
"valid_until": "2123-11-20T15:56:00.123Z",
"priority": 1,
"severity": 1,
"impact": 1,
"labels": [
"soarca",
"manual"
],
"external_references": [
{
"description": "TNO COSSAS"
}
],
"workflow_start": "start--9e7d62b2-88ac-4656-94e1-dbd4413ba008",
"workflow_exception": "end--a6f0b81e-affb-4bca-b4f6-a2d5af908958",
"workflow": {
"start--9e7d62b2-88ac-4656-94e1-dbd4413ba008": {
"name": "Start example flow for manual command",
"on_completion": "action--eb9372d4-d524-49fc-bf24-be26ea084779",
"type": "start"
},
"action--eb9372d4-d524-49fc-bf24-be26ea084779": {
"name": "manual",
"description": "Instruction to the operator to be executed manually",
"step_variables": {
"__hyperspeed_ready__": {
"type": "string",
"description": "set value to true or false when the request is completed",
"constant": false,
"external": false
}
},
"on_completion": "end--a6f0b81e-affb-4bca-b4f6-a2d5af908958",
"type": "action",
"commands": [
{
"type": "manual",
"command": "prepare Falcon for hyperspeed jump"
}
],
"agent": "soarca-manual-capability--7b0e98db-fa93-42aa-8511-e871c65131b1",
"targets": [
"individual--9d1f6217-34d5-435c-b29a-6a1af6b664d9"
],
"out_args": [
"__hyperspeed_ready__"
]
},
"end--a6f0b81e-affb-4bca-b4f6-a2d5af908958": {
"name": "End Flow",
"type": "end"
}
},
"agent_definitions": {
"soarca--00040001-1000-1000-a000-000100010001": {
"type": "soarca",
"name": "soarca-manual-capability"
},
"soarca-manual-capability--7b0e98db-fa93-42aa-8511-e871c65131b1": {
"type": "soarca-manual-capability",
"name": "soarca-manual-capability",
"description": "SOARCA's manual command handler"
}
},
"target_definitions": {
"individual--9d1f6217-34d5-435c-b29a-6a1af6b664d9": {
"type": "individual",
"name": "Luke Skywalker",
"description": "Darth Vader's son",
"location": {
"name": "Somewhere in a galaxy far far away"
}
}
}
}
2 changes: 1 addition & 1 deletion test/integration/api/routes/manual_api/manual_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func TestPostContinueCalled(t *testing.T) {
mock_interaction_storage.AssertExpectations(t)
}

func TestPostContinueFailsOnNonMatchingOutArg(t *testing.T) {
func TestPostContinueFailsOnNonMatchingOutArgNames(t *testing.T) {
mock_interaction_storage := mock_interaction_storage.MockInteractionStorage{}
manualApiHandler := manual_api.NewManualHandler(&mock_interaction_storage)

Expand Down

0 comments on commit 699577b

Please sign in to comment.