Skip to content

Commit ce283c0

Browse files
committed
feat[backend,frontend](incident-response): add shell selection for Windows agents, fix flow agent loading, enforce alert name in triggers, and rename default to dedicated agent
1 parent 9073eec commit ce283c0

13 files changed

Lines changed: 84 additions & 20 deletions

File tree

backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public class UtmAlertResponseRule implements Serializable {
6464
@Column(name = "last_modified_date")
6565
private Instant lastModifiedDate;
6666

67+
@Size(max = 20)
68+
@Column(name = "rule_shell", length = 20)
69+
private String ruleShell;
70+
6771
@Column(name = "system_owner", nullable = false)
6872
private Boolean systemOwner;
6973

@@ -92,6 +96,7 @@ public UtmAlertResponseRule(UtmAlertResponseRuleDTO dto) {
9296
this.ruleActive = dto.getActive();
9397
this.agentPlatform = dto.getAgentPlatform();
9498
this.defaultAgent = dto.getDefaultAgent();
99+
this.ruleShell = dto.getShell();
95100
this.systemOwner = dto.getSystemOwner();
96101
if (!CollectionUtils.isEmpty(dto.getExcludedAgents()))
97102
this.excludedAgents = String.join(",", dto.getExcludedAgents());

backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,9 @@ public void executeRuleCommands() {
283283
if (agent.getStatus().equals(AgentStatusEnum.ONLINE)) {
284284
String reason = "The incident response automation executed this command because it was accomplished the conditions of the rule with ID: " + cmd.getRuleId();
285285
final StringBuilder results = new StringBuilder();
286+
String shell = cmd.getRule() != null ? cmd.getRule().getRuleShell() : null;
286287
incidentResponseCommandService.sendCommand(String.valueOf(agent.getId()), utmIncidentVariableService.replaceVariablesInCommand(cmd.getCommand()), "INCIDENT_RESPONSE_AUTOMATION",
287-
cmd.getRuleId().toString(), reason, Constants.SYSTEM_ACCOUNT, new StreamObserver<>() {
288+
cmd.getRuleId().toString(), reason, Constants.SYSTEM_ACCOUNT, shell, new StreamObserver<>() {
288289
@Override
289290
public void onNext(CommandResult commandResult) {
290291
results.append(commandResult.getResult());

backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public class UtmAlertResponseRuleDTO {
4646
@Size(max = 500)
4747
private String defaultAgent;
4848

49+
@Size(max = 20)
50+
private String shell;
51+
4952
private List<String> excludedAgents = new ArrayList<>();
5053

5154
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@@ -76,6 +79,7 @@ public UtmAlertResponseRuleDTO(UtmAlertResponseRule rule) {
7679
this.active = rule.getRuleActive();
7780
this.agentPlatform = rule.getAgentPlatform();
7881
this.defaultAgent = rule.getDefaultAgent();
82+
this.shell = rule.getRuleShell();
7983
if (StringUtils.hasText(rule.getExcludedAgents())) {
8084
this.excludedAgents.addAll(Arrays.asList(rule.getExcludedAgents().split(",")));
8185
}

backend/src/main/java/com/park/utmstack/service/incident_response/grpc_impl/IncidentResponseCommandService.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,22 @@ public void sendCommand(String agentId,
2222
String originId,
2323
String reason,
2424
String executedBy,
25+
String shell,
2526
StreamObserver<CommandResult> responseObserver) {
26-
// Get the stub
27-
// Create the UtmCommand with the provided agent key and command
2827

29-
UtmCommand utmCommand = UtmCommand.newBuilder()
28+
UtmCommand.Builder builder = UtmCommand.newBuilder()
3029
.setAgentId(agentId)
3130
.setCommand(command)
3231
.setOriginId(originId)
3332
.setOriginType(originType)
3433
.setReason(reason)
35-
.setExecutedBy(executedBy)
36-
.build();
34+
.setExecutedBy(executedBy);
35+
36+
if (shell != null && !shell.isEmpty()) {
37+
builder.setShell(shell);
38+
}
39+
40+
UtmCommand utmCommand = builder.build();
3741

3842
// Send command using the bidirectional stream
3943
StreamObserver<UtmCommand> requestObserver = nonBlockingStub.processCommand(responseObserver);

backend/src/main/java/com/park/utmstack/web/rest/incident_response/UTMIncidentCommandWebsocket.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public void processCommand(@NotNull String command, @DestinationVariable @NotNul
6161
String commandVar = utmIncidentVariableService.replaceVariablesInCommand(commandVM.getCommand());
6262

6363
incidentResponseCommandService.sendCommand(String.valueOf(agentDTO.getId()), commandVar, commandVM.getOriginType(),
64-
commandVM.getOriginId(), commandVM.getReason(), executedBy, new StreamObserver<>() {
64+
commandVM.getOriginId(), commandVM.getReason(), executedBy, commandVM.getShell(), new StreamObserver<>() {
6565
@Override
6666
public void onNext(CommandResult value) {
6767
String output = utmIncidentVariableService.replaceSecretVariableValuesWithPlaceholders(value.getResult());

backend/src/main/java/com/park/utmstack/web/rest/vm/CommandVM.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public class CommandVM {
1212
@NotBlank
1313
String reason;
1414

15+
String shell;
16+
1517
public String getCommand() {
1618
return command;
1719
}
@@ -43,4 +45,12 @@ public String getReason() {
4345
public void setReason(String reason) {
4446
this.reason = reason;
4547
}
48+
49+
public String getShell() {
50+
return shell;
51+
}
52+
53+
public void setShell(String shell) {
54+
this.shell = shell;
55+
}
4656
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<databaseChangeLog
3+
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
6+
7+
<changeSet id="20260409001" author="Yorjander">
8+
<addColumn tableName="utm_alert_response_rule">
9+
<column name="rule_shell" type="varchar(20)" defaultValue="cmd"/>
10+
</addColumn>
11+
</changeSet>
12+
</databaseChangeLog>

backend/src/main/resources/config/liquibase/master.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,4 +589,6 @@
589589

590590
<include file="/config/liquibase/changelog/20260408002_seed_module_id_in_utm_data_types.xml" relativeToChangelogFile="false"/>
591591

592+
<include file="/config/liquibase/changelog/20260409001_add_shell_to_alert_response_rule.xml" relativeToChangelogFile="false"/>
593+
592594
</databaseChangeLog>

frontend/src/app/incident-response/shared/component/action-builder/action-builder.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ <h4 class="panel-title m-0">Flow Actions</h4>
5656

5757
<div class="col-6">
5858
<label class="pb-1" [for]="group.get('agentType').value ? 'default' : 'exclude'">
59-
{{ group.get('agentType').value ? 'Default agent' : 'Exclude agents' }}
59+
{{ group.get('agentType').value ? 'Dedicated agent' : 'Exclude agents' }}
6060
</label>
6161

6262
<!-- Default agent select -->

frontend/src/app/incident-response/shared/component/ir-create-rule/ir-create-rule.component.html

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
class="w-30"
117117
[loadingText]="'Loading alert fields...'"
118118
[loading]="!alertFields"
119+
[disabled]="i === 0"
119120
bindLabel="label"
120121
bindValue="field"
121122
formControlName="field"
@@ -139,15 +140,16 @@
139140
formControlName="value"
140141
id="values">
141142
</ng-select>
142-
<i class="icon-cross2 cursor-pointer ml-3" ngbTooltip="Delete condition"
143+
<i *ngIf="i > 0" class="icon-cross2 cursor-pointer ml-3" ngbTooltip="Delete condition"
143144
placement="left"
144145
(click)="removeRuleCondition(i)"></i>
146+
<i *ngIf="i === 0" class="icon-lock2 ml-3 text-muted" ngbTooltip="Alert name is required"></i>
145147
</div>
146148
<div class="d-flex justify-content-between pr-4">
147149
<div>
148-
<span *ngIf="ruleConditions.length === 0 || !ruleConditions.valid"
150+
<span *ngIf="!ruleConditions.valid"
149151
class="text-danger-300 font-size-base">
150-
You must set at least one trigger condition
152+
You must select an alert name and complete all trigger conditions
151153
</span>
152154
</div>
153155
<button class="btn utm-button btn-success align-self-end" (click)="addRuleCondition()">
@@ -175,7 +177,7 @@
175177
<div class="d-flex mt-3 flex-column">
176178
<div class="alert alert-info alert-styled-right mb-2 info-dismissible">
177179
<span class="font-weight-semibold">Info! </span>
178-
<span>Select the agent handling strategy for the automation. If <strong>not active</strong>, commands will run on specified platform agents if the trigger conditions and dataSource field value of the alert match. Alternatively, choose a <strong>default agent</strong> to run the automation if no other agent matches the criteria. If this option is <strong>active</strong>, commands will run only on specified platform agents if the trigger conditions and dataSource field value of the alert match, if not, the <strong>automation won't be executed</strong>.</span>
180+
<span>Select the agent handling strategy for the automation. If <strong>not active</strong>, commands will run on specified platform agents if the trigger conditions and dataSource field value of the alert match. Alternatively, choose a <strong>dedicated agent</strong> to run the automation if no other agent matches the criteria. If this option is <strong>active</strong>, commands will run only on specified platform agents if the trigger conditions and dataSource field value of the alert match, if not, the <strong>automation won't be executed</strong>.</span>
179181
</div>
180182
<app-utm-toggle (toggleChange)="onChangeToggle($event)"
181183
[active]="formRule.get('agentType').value"
@@ -208,7 +210,7 @@
208210
</div>
209211
<div *ngIf="formRule.get('agentType').value" class="d-flex mt-2 flex-column">
210212
<div class="col-6 p-0">
211-
<label class="pb-1" for="exclude">Default agent</label>
213+
<label class="pb-1" for="exclude">Dedicated agent</label>
212214
<ng-select [clearable]="false"
213215
[items]="agents"
214216
[placeholder]="'Select agent'"
@@ -235,6 +237,13 @@
235237
</div>
236238
</div>
237239
<div *ngIf="step===3" class="configure-step mt-3 mb-3">
240+
<div *ngIf="isWindows()" class="form-group mb-3">
241+
<label class="pb-1">Shell</label>
242+
<select formControlName="shell" class="form-control w-25">
243+
<option value="cmd">cmd</option>
244+
<option value="powershell">powershell</option>
245+
</select>
246+
</div>
238247
<span class="font-size-lg mb-2"> <i
239248
class="icon-keyboard"></i> Press <b>TAB</b> to use alert fields in the command</span>
240249
<div class="window">

0 commit comments

Comments
 (0)