Skip to content

Commit 193aedc

Browse files
committed
[backend/frontend] feat: add terminal view on inject result (#3525)
1 parent 35befa4 commit 193aedc

File tree

23 files changed

+388
-249
lines changed

23 files changed

+388
-249
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.openaev.api.inject_result.dto;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import io.openaev.database.model.ExecutionTrace;
5+
import io.openaev.database.model.PayloadCommandBlock;
6+
import jakarta.validation.constraints.NotEmpty;
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import lombok.AllArgsConstructor;
10+
import lombok.Data;
11+
import lombok.NoArgsConstructor;
12+
13+
@Data
14+
@AllArgsConstructor
15+
@NoArgsConstructor
16+
public class InjectResultPayloadExecutionOutput {
17+
18+
@JsonProperty("payload_command_blocks")
19+
@NotEmpty
20+
private List<PayloadCommandBlock> payloadCommandBlocks = new ArrayList<>();
21+
22+
@JsonProperty("execution_execution_traces")
23+
@NotEmpty
24+
private List<ExecutionTrace> traces = new ArrayList<>();
25+
}

openaev-api/src/main/java/io/openaev/rest/inject/InjectApi.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import io.openaev.database.model.*;
1111
import io.openaev.database.raw.RawDocument;
1212
import io.openaev.database.repository.ExerciseRepository;
13-
import io.openaev.database.repository.GrantRepository;
1413
import io.openaev.database.repository.InjectRepository;
1514
import io.openaev.database.repository.UserRepository;
1615
import io.openaev.database.specification.InjectSpecification;
@@ -28,7 +27,6 @@
2827
import io.openaev.rest.inject.service.InjectExportService;
2928
import io.openaev.rest.inject.service.InjectService;
3029
import io.openaev.rest.payload.form.DetectionRemediationOutput;
31-
import io.openaev.service.InjectImportService;
3230
import io.openaev.service.UserService;
3331
import io.openaev.service.targets.TargetService;
3432
import io.openaev.utils.FilterUtilsJpa;
@@ -70,15 +68,13 @@ public class InjectApi extends RestBehavior {
7068
private final ExerciseRepository exerciseRepository;
7169
private final InjectRepository injectRepository;
7270
private final InjectService injectService;
73-
private final InjectImportService injectImportService;
7471
private final InjectExecutionService injectExecutionService;
7572
private final InjectExportService injectExportService;
7673
private final TargetService targetService;
7774
private final UserRepository userRepository;
7875
private final PayloadMapper payloadMapper;
7976
private final UserService userService;
8077
private final DocumentService documentService;
81-
private final GrantRepository grantRepository;
8278

8379
// -- INJECTS --
8480

@@ -499,7 +495,8 @@ public List<ExecutionTraceOutput> getInjectTracesFromInjectAndTarget(
499495
@RequestParam String injectId,
500496
@RequestParam String targetId,
501497
@RequestParam TargetType targetType) {
502-
return this.injectService.getInjectTracesFromInjectAndTarget(injectId, targetId, targetType);
498+
return this.injectService.getInjectTracesOutputFromInjectAndTarget(
499+
injectId, targetId, targetType);
503500
}
504501

505502
@Operation(description = "Get InjectStatus with global execution traces")
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.openaev.rest.inject;
2+
3+
import io.openaev.aop.RBAC;
4+
import io.openaev.api.inject_result.dto.InjectResultPayloadExecutionOutput;
5+
import io.openaev.database.model.Action;
6+
import io.openaev.database.model.ResourceType;
7+
import io.openaev.rest.helper.RestBehavior;
8+
import io.openaev.utils.TargetType;
9+
import jakarta.validation.constraints.NotBlank;
10+
import jakarta.validation.constraints.NotNull;
11+
import lombok.RequiredArgsConstructor;
12+
import lombok.extern.slf4j.Slf4j;
13+
import org.springframework.web.bind.annotation.GetMapping;
14+
import org.springframework.web.bind.annotation.PathVariable;
15+
import org.springframework.web.bind.annotation.RequestParam;
16+
import org.springframework.web.bind.annotation.RestController;
17+
18+
@Slf4j
19+
@RestController
20+
@RequiredArgsConstructor
21+
public class InjectExecutionResultApi extends RestBehavior {
22+
23+
public static final String INJECT_EXECUTION_URI = "/api/injects/{injectId}";
24+
25+
private final InjectExecutionResultService injectExecutionService;
26+
27+
@GetMapping(INJECT_EXECUTION_URI + "/payload_result")
28+
@RBAC(resourceId = "#injectId", actionPerformed = Action.READ, resourceType = ResourceType.INJECT)
29+
public InjectResultPayloadExecutionOutput injectExecutionResultPayload(
30+
@PathVariable @NotBlank final String injectId,
31+
@RequestParam @NotBlank final String targetId,
32+
@RequestParam @NotNull final TargetType targetType) {
33+
return this.injectExecutionService.injectExecutionResultPayload(injectId, targetId, targetType);
34+
}
35+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.openaev.rest.inject;
2+
3+
import static io.openaev.database.model.ExecutionTraceAction.EXECUTION;
4+
5+
import io.openaev.aop.RBAC;
6+
import io.openaev.api.inject_result.dto.InjectResultPayloadExecutionOutput;
7+
import io.openaev.database.model.*;
8+
import io.openaev.rest.inject.service.InjectService;
9+
import io.openaev.rest.inject.service.InjectStatusService;
10+
import io.openaev.utils.TargetType;
11+
import jakarta.validation.constraints.NotBlank;
12+
import jakarta.validation.constraints.NotNull;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
import java.util.Optional;
16+
import lombok.RequiredArgsConstructor;
17+
import lombok.extern.slf4j.Slf4j;
18+
import org.springframework.stereotype.Service;
19+
20+
@RequiredArgsConstructor
21+
@Service
22+
@Slf4j
23+
public class InjectExecutionResultService {
24+
25+
private final InjectService injectService;
26+
private final InjectStatusService injectStatusService;
27+
28+
@RBAC(resourceId = "#injectId", actionPerformed = Action.READ, resourceType = ResourceType.INJECT)
29+
public InjectResultPayloadExecutionOutput injectExecutionResultPayload(
30+
@NotBlank final String injectId,
31+
@NotBlank final String targetId,
32+
@NotNull final TargetType targetType) {
33+
InjectStatus injectStatus = this.injectStatusService.findInjectStatusByInjectId(injectId);
34+
InjectResultPayloadExecutionOutput output = new InjectResultPayloadExecutionOutput();
35+
output.setPayloadCommandBlocks(
36+
Optional.of(injectStatus)
37+
.map(InjectStatus::getPayloadOutput)
38+
.map(StatusPayload::getPayloadCommandBlocks)
39+
.orElse(new ArrayList<>()));
40+
41+
List<ExecutionTrace> traces =
42+
this.injectService.getInjectTracesFromInjectAndTarget(injectId, targetId, targetType);
43+
output.setTraces(traces.stream().filter(t -> EXECUTION.equals(t.getAction())).toList());
44+
45+
return output;
46+
}
47+
}

openaev-api/src/main/java/io/openaev/rest/inject/service/InjectService.java

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -929,24 +929,21 @@ public List<FilterUtilsJpa.Option> getOptionsByNameLinkedToFindings(
929929
.toList();
930930
}
931931

932-
public List<ExecutionTraceOutput> getInjectTracesFromInjectAndTarget(
932+
public List<ExecutionTraceOutput> getInjectTracesOutputFromInjectAndTarget(
933933
final String injectId, final String targetId, final TargetType targetType) {
934-
switch (targetType) {
935-
case AGENT:
936-
return injectStatusMapper.toExecutionTracesOutput(
937-
this.executionTraceRepository.findByInjectIdAndAgentId(injectId, targetId));
938-
case ASSETS:
939-
return injectStatusMapper.toExecutionTracesOutput(
940-
this.executionTraceRepository.findByInjectIdAndAssetId(injectId, targetId));
941-
case TEAMS:
942-
return injectStatusMapper.toExecutionTracesOutput(
943-
this.executionTraceRepository.findByInjectIdAndTeamId(injectId, targetId));
944-
case PLAYERS:
945-
return injectStatusMapper.toExecutionTracesOutput(
946-
this.executionTraceRepository.findByInjectIdAndPlayerId(injectId, targetId));
947-
default:
948-
throw new BadRequestException("Target type " + targetType + " is not supported");
949-
}
934+
return injectStatusMapper.toExecutionTracesOutput(
935+
getInjectTracesFromInjectAndTarget(injectId, targetId, targetType));
936+
}
937+
938+
public List<ExecutionTrace> getInjectTracesFromInjectAndTarget(
939+
final String injectId, final String targetId, final TargetType targetType) {
940+
return switch (targetType) {
941+
case AGENT -> this.executionTraceRepository.findByInjectIdAndAgentId(injectId, targetId);
942+
case ASSETS -> this.executionTraceRepository.findByInjectIdAndAssetId(injectId, targetId);
943+
case TEAMS -> this.executionTraceRepository.findByInjectIdAndTeamId(injectId, targetId);
944+
case PLAYERS -> this.executionTraceRepository.findByInjectIdAndPlayerId(injectId, targetId);
945+
default -> throw new BadRequestException("Target type " + targetType + " is not supported");
946+
};
950947
}
951948

952949
public InjectStatusOutput getInjectStatusWithGlobalExecutionTraces(String injectId) {

openaev-api/src/main/java/io/openaev/rest/inject/service/InjectStatusService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.openaev.utils.InjectUtils;
1919
import jakarta.annotation.Nullable;
2020
import jakarta.transaction.Transactional;
21+
import jakarta.validation.constraints.NotBlank;
2122
import jakarta.validation.constraints.NotNull;
2223
import java.time.Instant;
2324
import java.util.Collections;
@@ -41,6 +42,13 @@ public List<InjectStatus> findPendingInjectStatusByType(String injectType) {
4142
return this.injectStatusRepository.pendingForInjectType(injectType);
4243
}
4344

45+
public InjectStatus findInjectStatusByInjectId(@NotBlank final String injectId) {
46+
return this.injectStatusRepository
47+
.findByInjectId(injectId)
48+
.orElseThrow(
49+
() -> new ElementNotFoundException("Inject status not found for :" + injectId));
50+
}
51+
4452
@Transactional(rollbackOn = Exception.class)
4553
public Inject updateInjectStatus(String injectId, InjectUpdateStatusInput input) {
4654
Inject inject = injectRepository.findById(injectId).orElseThrow();

openaev-front/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"react-markdown": "10.1.0",
6363
"react-redux": "9.2.0",
6464
"react-router": "7.9.5",
65-
"react-syntax-highlighter": "15.6.6",
65+
"react-syntax-highlighter": "16.1.0",
6666
"redux": "5.0.1",
6767
"redux-thunk": "3.1.0",
6868
"remark-flexible-markers": "1.3.1",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { simpleCall } from '../../utils/Action';
2+
import type { Inject } from '../../utils/api-types';
3+
import { INJECT_URI } from '../injects/inject-action';
4+
5+
// eslint-disable-next-line import/prefer-default-export
6+
export const fetchInjectExecutionResult = (injectId: Inject['inject_id'], targetId: string = '', targetType: string = '') => {
7+
const params = {
8+
targetId,
9+
targetType,
10+
};
11+
const uri = `${INJECT_URI}/${injectId}/payload_result`;
12+
return simpleCall(uri, { params });
13+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import type { InjectExecutionPayloadOutput, InjectTarget } from '../../utils/api-types';
4+
import { fetchInjectExecutionResult } from './inject-status-action';
5+
6+
const useFetchInjectExecutionResult = (injectId: string, target: InjectTarget) => {
7+
const [injectExecutionResult, setInjectExecutionResult] = useState<InjectExecutionPayloadOutput>();
8+
const [loading, setLoading] = useState(false);
9+
const fetch = async () => {
10+
if (!injectId || !target?.target_id || !target.target_type) return;
11+
setLoading(true);
12+
try {
13+
const result = await fetchInjectExecutionResult(injectId, target.target_id, target.target_type);
14+
setInjectExecutionResult(result.data || []);
15+
} finally {
16+
setLoading(false);
17+
}
18+
};
19+
useEffect(() => {
20+
fetch();
21+
}, [injectId, target?.target_id, target?.target_type]);
22+
return ({
23+
injectExecutionResult,
24+
loading,
25+
});
26+
};
27+
28+
export default useFetchInjectExecutionResult;

openaev-front/src/actions/injects/inject-action.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { MESSAGING$ } from '../../utils/Environment';
1313
import * as schema from '../Schema';
1414

15-
const INJECT_URI = '/api/injects';
15+
export const INJECT_URI = '/api/injects';
1616

1717
export const exportInjectSearch = (data: InjectExportFromSearchRequestInput) => {
1818
const uri = '/api/injects/search/export';

0 commit comments

Comments
 (0)