diff --git a/openaev-api/src/main/java/io/openaev/api/inject_result/dto/InjectResultPayloadExecutionOutput.java b/openaev-api/src/main/java/io/openaev/api/inject_result/dto/InjectResultPayloadExecutionOutput.java new file mode 100644 index 00000000000..d2d24f5f859 --- /dev/null +++ b/openaev-api/src/main/java/io/openaev/api/inject_result/dto/InjectResultPayloadExecutionOutput.java @@ -0,0 +1,25 @@ +package io.openaev.api.inject_result.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.openaev.database.model.ExecutionTrace; +import io.openaev.database.model.PayloadCommandBlock; +import jakarta.validation.constraints.NotEmpty; +import java.util.ArrayList; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class InjectResultPayloadExecutionOutput { + + @JsonProperty("payload_command_blocks") + @NotEmpty + private List payloadCommandBlocks = new ArrayList<>(); + + @JsonProperty("execution_execution_traces") + @NotEmpty + private List traces = new ArrayList<>(); +} diff --git a/openaev-api/src/main/java/io/openaev/rest/inject/InjectApi.java b/openaev-api/src/main/java/io/openaev/rest/inject/InjectApi.java index ac253e0855a..7166b27866c 100644 --- a/openaev-api/src/main/java/io/openaev/rest/inject/InjectApi.java +++ b/openaev-api/src/main/java/io/openaev/rest/inject/InjectApi.java @@ -10,7 +10,6 @@ import io.openaev.database.model.*; import io.openaev.database.raw.RawDocument; import io.openaev.database.repository.ExerciseRepository; -import io.openaev.database.repository.GrantRepository; import io.openaev.database.repository.InjectRepository; import io.openaev.database.repository.UserRepository; import io.openaev.database.specification.InjectSpecification; @@ -28,7 +27,6 @@ import io.openaev.rest.inject.service.InjectExportService; import io.openaev.rest.inject.service.InjectService; import io.openaev.rest.payload.form.DetectionRemediationOutput; -import io.openaev.service.InjectImportService; import io.openaev.service.UserService; import io.openaev.service.targets.TargetService; import io.openaev.utils.FilterUtilsJpa; @@ -70,7 +68,6 @@ public class InjectApi extends RestBehavior { private final ExerciseRepository exerciseRepository; private final InjectRepository injectRepository; private final InjectService injectService; - private final InjectImportService injectImportService; private final InjectExecutionService injectExecutionService; private final InjectExportService injectExportService; private final TargetService targetService; @@ -78,7 +75,6 @@ public class InjectApi extends RestBehavior { private final PayloadMapper payloadMapper; private final UserService userService; private final DocumentService documentService; - private final GrantRepository grantRepository; // -- INJECTS -- @@ -499,7 +495,8 @@ public List getInjectTracesFromInjectAndTarget( @RequestParam String injectId, @RequestParam String targetId, @RequestParam TargetType targetType) { - return this.injectService.getInjectTracesFromInjectAndTarget(injectId, targetId, targetType); + return this.injectService.getInjectTracesOutputFromInjectAndTarget( + injectId, targetId, targetType); } @Operation(description = "Get InjectStatus with global execution traces") diff --git a/openaev-api/src/main/java/io/openaev/rest/inject/InjectExecutionResultApi.java b/openaev-api/src/main/java/io/openaev/rest/inject/InjectExecutionResultApi.java new file mode 100644 index 00000000000..0f76c2077c7 --- /dev/null +++ b/openaev-api/src/main/java/io/openaev/rest/inject/InjectExecutionResultApi.java @@ -0,0 +1,35 @@ +package io.openaev.rest.inject; + +import io.openaev.aop.RBAC; +import io.openaev.api.inject_result.dto.InjectResultPayloadExecutionOutput; +import io.openaev.database.model.Action; +import io.openaev.database.model.ResourceType; +import io.openaev.rest.helper.RestBehavior; +import io.openaev.utils.TargetType; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class InjectExecutionResultApi extends RestBehavior { + + public static final String INJECT_EXECUTION_URI = "/api/injects/{injectId}"; + + private final InjectExecutionResultService injectExecutionService; + + @GetMapping(INJECT_EXECUTION_URI + "/payload-result") + @RBAC(resourceId = "#injectId", actionPerformed = Action.READ, resourceType = ResourceType.INJECT) + public InjectResultPayloadExecutionOutput injectExecutionResultPayload( + @PathVariable @NotBlank final String injectId, + @RequestParam @NotBlank final String targetId, + @RequestParam @NotNull final TargetType targetType) { + return this.injectExecutionService.injectExecutionResultPayload(injectId, targetId, targetType); + } +} diff --git a/openaev-api/src/main/java/io/openaev/rest/inject/InjectExecutionResultService.java b/openaev-api/src/main/java/io/openaev/rest/inject/InjectExecutionResultService.java new file mode 100644 index 00000000000..77e368908a5 --- /dev/null +++ b/openaev-api/src/main/java/io/openaev/rest/inject/InjectExecutionResultService.java @@ -0,0 +1,47 @@ +package io.openaev.rest.inject; + +import static io.openaev.database.model.ExecutionTraceAction.EXECUTION; + +import io.openaev.aop.RBAC; +import io.openaev.api.inject_result.dto.InjectResultPayloadExecutionOutput; +import io.openaev.database.model.*; +import io.openaev.rest.inject.service.InjectService; +import io.openaev.rest.inject.service.InjectStatusService; +import io.openaev.utils.TargetType; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +@Slf4j +public class InjectExecutionResultService { + + private final InjectService injectService; + private final InjectStatusService injectStatusService; + + @RBAC(resourceId = "#injectId", actionPerformed = Action.READ, resourceType = ResourceType.INJECT) + public InjectResultPayloadExecutionOutput injectExecutionResultPayload( + @NotBlank final String injectId, + @NotBlank final String targetId, + @NotNull final TargetType targetType) { + InjectStatus injectStatus = this.injectStatusService.findInjectStatusByInjectId(injectId); + InjectResultPayloadExecutionOutput output = new InjectResultPayloadExecutionOutput(); + output.setPayloadCommandBlocks( + Optional.of(injectStatus) + .map(InjectStatus::getPayloadOutput) + .map(StatusPayload::getPayloadCommandBlocks) + .orElse(new ArrayList<>())); + + List traces = + this.injectService.getInjectTracesFromInjectAndTarget(injectId, targetId, targetType); + output.setTraces(traces.stream().filter(t -> EXECUTION.equals(t.getAction())).toList()); + + return output; + } +} diff --git a/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectService.java b/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectService.java index fd5cfa8c5bd..72e4dd81dad 100644 --- a/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectService.java +++ b/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectService.java @@ -929,24 +929,21 @@ public List getOptionsByNameLinkedToFindings( .toList(); } - public List getInjectTracesFromInjectAndTarget( + public List getInjectTracesOutputFromInjectAndTarget( final String injectId, final String targetId, final TargetType targetType) { - switch (targetType) { - case AGENT: - return injectStatusMapper.toExecutionTracesOutput( - this.executionTraceRepository.findByInjectIdAndAgentId(injectId, targetId)); - case ASSETS: - return injectStatusMapper.toExecutionTracesOutput( - this.executionTraceRepository.findByInjectIdAndAssetId(injectId, targetId)); - case TEAMS: - return injectStatusMapper.toExecutionTracesOutput( - this.executionTraceRepository.findByInjectIdAndTeamId(injectId, targetId)); - case PLAYERS: - return injectStatusMapper.toExecutionTracesOutput( - this.executionTraceRepository.findByInjectIdAndPlayerId(injectId, targetId)); - default: - throw new BadRequestException("Target type " + targetType + " is not supported"); - } + return injectStatusMapper.toExecutionTracesOutput( + getInjectTracesFromInjectAndTarget(injectId, targetId, targetType)); + } + + public List getInjectTracesFromInjectAndTarget( + final String injectId, final String targetId, final TargetType targetType) { + return switch (targetType) { + case AGENT -> this.executionTraceRepository.findByInjectIdAndAgentId(injectId, targetId); + case ASSETS -> this.executionTraceRepository.findByInjectIdAndAssetId(injectId, targetId); + case TEAMS -> this.executionTraceRepository.findByInjectIdAndTeamId(injectId, targetId); + case PLAYERS -> this.executionTraceRepository.findByInjectIdAndPlayerId(injectId, targetId); + default -> throw new BadRequestException("Target type " + targetType + " is not supported"); + }; } public InjectStatusOutput getInjectStatusWithGlobalExecutionTraces(String injectId) { diff --git a/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectStatusService.java b/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectStatusService.java index 25f814b4173..f9e58d17b65 100644 --- a/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectStatusService.java +++ b/openaev-api/src/main/java/io/openaev/rest/inject/service/InjectStatusService.java @@ -18,6 +18,7 @@ import io.openaev.utils.InjectUtils; import jakarta.annotation.Nullable; import jakarta.transaction.Transactional; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.time.Instant; import java.util.Collections; @@ -41,6 +42,13 @@ public List findPendingInjectStatusByType(String injectType) { return this.injectStatusRepository.pendingForInjectType(injectType); } + public InjectStatus findInjectStatusByInjectId(@NotBlank final String injectId) { + return this.injectStatusRepository + .findByInjectId(injectId) + .orElseThrow( + () -> new ElementNotFoundException("Inject status not found for :" + injectId)); + } + @Transactional(rollbackOn = Exception.class) public Inject updateInjectStatus(String injectId, InjectUpdateStatusInput input) { Inject inject = injectRepository.findById(injectId).orElseThrow(); diff --git a/openaev-front/package.json b/openaev-front/package.json index e44717ffff7..3d2e075e754 100644 --- a/openaev-front/package.json +++ b/openaev-front/package.json @@ -62,7 +62,7 @@ "react-markdown": "10.1.0", "react-redux": "9.2.0", "react-router": "7.9.5", - "react-syntax-highlighter": "15.6.6", + "react-syntax-highlighter": "16.1.0", "redux": "5.0.1", "redux-thunk": "3.1.0", "remark-flexible-markers": "1.3.1", diff --git a/openaev-front/src/actions/inject_status/inject-status-action.ts b/openaev-front/src/actions/inject_status/inject-status-action.ts new file mode 100644 index 00000000000..4828e3fb0ca --- /dev/null +++ b/openaev-front/src/actions/inject_status/inject-status-action.ts @@ -0,0 +1,13 @@ +import { simpleCall } from '../../utils/Action'; +import type { Inject } from '../../utils/api-types'; +import { INJECT_URI } from '../injects/inject-action'; + +// eslint-disable-next-line import/prefer-default-export +export const fetchInjectExecutionResult = (injectId: Inject['inject_id'], targetId: string = '', targetType: string = '') => { + const params = { + targetId, + targetType, + }; + const uri = `${INJECT_URI}/${injectId}/payload-result`; + return simpleCall(uri, { params }); +}; diff --git a/openaev-front/src/actions/inject_status/useFetchInjectExecutionResult.tsx b/openaev-front/src/actions/inject_status/useFetchInjectExecutionResult.tsx new file mode 100644 index 00000000000..e22ee87d0a4 --- /dev/null +++ b/openaev-front/src/actions/inject_status/useFetchInjectExecutionResult.tsx @@ -0,0 +1,28 @@ +import { useEffect, useState } from 'react'; + +import type { InjectResultPayloadExecutionOutput, InjectTarget } from '../../utils/api-types'; +import { fetchInjectExecutionResult } from './inject-status-action'; + +const useFetchInjectExecutionResult = (injectId: string, target: InjectTarget) => { + const [injectExecutionResult, setInjectExecutionResult] = useState(); + const [loading, setLoading] = useState(false); + const fetch = async () => { + if (!injectId || !target?.target_id || !target.target_type) return; + setLoading(true); + try { + const result = await fetchInjectExecutionResult(injectId, target.target_id, target.target_type); + setInjectExecutionResult(result.data || []); + } finally { + setLoading(false); + } + }; + useEffect(() => { + fetch(); + }, [injectId, target?.target_id, target?.target_type]); + return ({ + injectExecutionResult, + loading, + }); +}; + +export default useFetchInjectExecutionResult; diff --git a/openaev-front/src/actions/injects/inject-action.ts b/openaev-front/src/actions/injects/inject-action.ts index 63c7fa75c49..17d0b88157b 100644 --- a/openaev-front/src/actions/injects/inject-action.ts +++ b/openaev-front/src/actions/injects/inject-action.ts @@ -12,7 +12,7 @@ import { import { MESSAGING$ } from '../../utils/Environment'; import * as schema from '../Schema'; -const INJECT_URI = '/api/injects'; +export const INJECT_URI = '/api/injects'; export const exportInjectSearch = (data: InjectExportFromSearchRequestInput) => { const uri = '/api/injects/search/export'; diff --git a/openaev-front/src/admin/components/atomic_testings/atomic_testing/target_result/TargetResultsDetail.tsx b/openaev-front/src/admin/components/atomic_testings/atomic_testing/target_result/TargetResultsDetail.tsx index 37a2a9d8614..cd94c24adde 100644 --- a/openaev-front/src/admin/components/atomic_testings/atomic_testing/target_result/TargetResultsDetail.tsx +++ b/openaev-front/src/admin/components/atomic_testings/atomic_testing/target_result/TargetResultsDetail.tsx @@ -1,5 +1,5 @@ -import { Tab, Tabs, Typography } from '@mui/material'; -import { type SyntheticEvent, useContext, useEffect, useState } from 'react'; +import { Typography } from '@mui/material'; +import { useContext, useEffect, useState } from 'react'; import { useSearchParams } from 'react-router'; import { makeStyles } from 'tss-react/mui'; @@ -7,16 +7,12 @@ import { fetchTargetResult } from '../../../../../actions/atomic_testings/atomic import Paper from '../../../../../components/common/Paper'; import { useFormatter } from '../../../../../components/i18n'; import type { InjectResultOverviewOutput, InjectTarget } from '../../../../../utils/api-types'; -import { - type ExpectationResultType, - ExpectationType, - type InjectExpectationsStore, -} from '../../../common/injects/expectations/Expectation'; +import { isAgent, isAssetGroups } from '../../../../../utils/target/TargetUtils'; +import { type ExpectationResultType, ExpectationType, type InjectExpectationsStore } from '../../../common/injects/expectations/Expectation'; import ExecutionStatusDetail from '../../../common/injects/status/ExecutionStatusDetail'; -import { - InjectResultOverviewOutputContext, - type InjectResultOverviewOutputContextType, -} from '../../InjectResultOverviewOutputContext'; +import TerminalView from '../../../common/injects/status/traces/TerminalView'; +import TabbedView, { type TabConfig } from '../../../settings/groups/grants/ui/TabbedView'; +import { InjectResultOverviewOutputContext, type InjectResultOverviewOutputContextType } from '../../InjectResultOverviewOutputContext'; import InjectExpectationCard from './InjectExpectationCard'; import TargetResultsReactFlow from './TargetResultsReactFlow'; @@ -28,9 +24,9 @@ interface Props { const useStyles = makeStyles()(theme => ({ container: { display: 'grid', - gridTemplateColumns: '1fr auto 1fr', + gridTemplateColumns: '1fr 1fr 1fr', + justifyItems: 'center', }, - allWidth: { gridColumn: 'span 3' }, paddingTop: { paddingTop: theme.spacing(2) }, gap: { display: 'flex', @@ -42,17 +38,13 @@ const useStyles = makeStyles()(theme => ({ const TargetResultsDetail = ({ inject, target }: Props) => { const { classes } = useStyles(); const { t } = useFormatter(); - const canShowExecutionTab = target.target_type !== 'ASSETS_GROUPS'; const [sortedGroupedTargetResults, setSortedGroupedTargetResults] = useState>({}); const [searchParams, setSearchParams] = useSearchParams(); const openIdParams = searchParams.get('expectation_id'); - const [activeTab, setActiveTab] = useState(0); - const handleTabChange = (_event: SyntheticEvent, newValue: number) => { - setActiveTab(newValue); - }; + const [activeTab, setActiveTab] = useState(null); const { injectResultOverviewOutput, updateInjectResultOverviewOutput } = useContext(InjectResultOverviewOutputContext); @@ -96,71 +88,83 @@ const TargetResultsDetail = ({ inject, target }: Props) => { useEffect(() => { if (!openIdParams || !sortedGroupedTargetResults) return; - const activeTabIndex = Object.values(sortedGroupedTargetResults).findIndex(results => - results.some(r => r.inject_expectation_id === openIdParams), - ); + const activeTabIndex: string = Object.values(sortedGroupedTargetResults) + .map(results => results.find(r => r.inject_expectation_id === openIdParams)) + .filter(res => !!res)[0]?.inject_expectation_type.toString(); - if (activeTabIndex === -1) return; + if (!activeTabIndex) return; setActiveTab(activeTabIndex); searchParams.delete('open'); setSearchParams(searchParams, { replace: true }); }, [openIdParams, sortedGroupedTargetResults]); + const tabs: TabConfig[] = []; + Object.entries(sortedGroupedTargetResults).forEach(([type, expectationResults]) => ( + tabs.push({ + key: type, + label: t(`TYPE_${type}`), + component: ( + expectationResults.map(expectationResult => ( + + )) + ), + }) + )); + if (!isAssetGroups(target)) { + tabs.push({ + key: 'execution', + label: 'Execution', + component: ( + + ), + }); + } + if (isAgent(target)) { + tabs.push({ + key: 'terminal-view', + label: t('Terminal view'), + component: ( + + ), + }); + } + return ( - - {t('Name')} - {t('Type')} - {t('Platform')} - {target.target_name} - {target.target_type} - {target.target_subtype ?? t('N/A')} + +
+ {t('Name')} + {t('Type')} + {t('Platform')} +
+
+ {target.target_name} + {target.target_type} + {target.target_subtype ?? t('N/A')} +
- - {Object.keys(sortedGroupedTargetResults).map((type, index) => ( - - ))} - {canShowExecutionTab && } - - -
- {Object.entries(sortedGroupedTargetResults).length > 0 - && Object.entries(sortedGroupedTargetResults).length > activeTab - && Object.entries(sortedGroupedTargetResults)[activeTab][1].map(expectationResult => ( - - ))} - {(activeTab === Object.keys(sortedGroupedTargetResults).length && canShowExecutionTab) && ( - - )} -
- +
); }; diff --git a/openaev-front/src/admin/components/common/injects/status/traces/TerminalView.tsx b/openaev-front/src/admin/components/common/injects/status/traces/TerminalView.tsx new file mode 100644 index 00000000000..738b482c0b5 --- /dev/null +++ b/openaev-front/src/admin/components/common/injects/status/traces/TerminalView.tsx @@ -0,0 +1,67 @@ +import { Paper } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import { type FunctionComponent } from 'react'; + +import useFetchInjectExecutionResult from '../../../../../../actions/inject_status/useFetchInjectExecutionResult'; +import Empty from '../../../../../../components/Empty'; +import { useFormatter } from '../../../../../../components/i18n'; +import { FONT_FAMILY_CODE } from '../../../../../../components/Theme'; +import type { InjectTarget } from '../../../../../../utils/api-types'; + +interface Props { + injectId: string; + target: InjectTarget; +} + +const TerminalView: FunctionComponent = ({ injectId, target }) => { + const { t } = useFormatter(); + const theme = useTheme(); + const { injectExecutionResult, loading } = useFetchInjectExecutionResult(injectId, target); + + if (!injectExecutionResult || injectExecutionResult?.execution_execution_traces.length === 0) { + return ; + } + + const firstExec = injectExecutionResult?.execution_execution_traces[0].execution_time; + const isDark = theme.palette.mode === 'dark'; + + return ( + <> + {!loading && ( + +
+ {firstExec + ' ' + injectExecutionResult?.payload_command_blocks.map(p => p.command_content).join(' ') + '\n'} + {injectExecutionResult?.execution_execution_traces.map((tr) => { + const parsed = JSON.parse(tr.execution_message); + const stdout = parsed.stdout || ''; + const stderr = parsed.stderr || ''; + + return ( + <> + {tr.execution_time + ' '} + {stdout && ({stdout})} + {stderr && ({stderr})} + + ); + })} +
+
+ )} + + ); +} +; + +export default TerminalView; diff --git a/openaev-front/src/admin/components/settings/groups/grants/ui/TabbedView.tsx b/openaev-front/src/admin/components/settings/groups/grants/ui/TabbedView.tsx index b3dbb37b858..1c931af3f94 100644 --- a/openaev-front/src/admin/components/settings/groups/grants/ui/TabbedView.tsx +++ b/openaev-front/src/admin/components/settings/groups/grants/ui/TabbedView.tsx @@ -1,22 +1,37 @@ -import { type FunctionComponent, type ReactNode } from 'react'; +import { type FunctionComponent, type ReactNode, useEffect } from 'react'; import Tabs, { type TabsEntry } from '../../../../../../components/common/tabs/Tabs'; import useTabs from '../../../../../../components/common/tabs/useTabs'; import TabPanel from './TabPanel'; -interface TabConfig extends TabsEntry { component: ReactNode } +export interface TabConfig extends TabsEntry { component: ReactNode } -interface Props { tabs: TabConfig[] } +interface Props { + tabs: TabConfig[]; + externalCurrentTab?: string | null; + notifyTabChange?: (key: string) => void; +} -const TabbedView: FunctionComponent = ({ tabs }) => { - const { currentTab, handleChangeTab } = useTabs(tabs[0].key); +const TabbedView: FunctionComponent = ({ tabs, externalCurrentTab, notifyTabChange }) => { + const { currentTab, handleChangeTab } = useTabs(tabs[0]?.key); + + useEffect(() => { + if (externalCurrentTab && externalCurrentTab !== currentTab) { + handleChangeTab(externalCurrentTab); + } + }, [externalCurrentTab]); + + const handleChange = (newKey: string) => { + handleChangeTab(newKey); + notifyTabChange?.(newKey); + }; return ( <> handleChangeTab(newValue)} + onChange={handleChange} /> {tabs.map((tab, index) => ( e.key === currentTab)} index={index}> diff --git a/openaev-front/src/components/Theme.ts b/openaev-front/src/components/Theme.ts index fb79a84c34f..39aee5ebc37 100644 --- a/openaev-front/src/components/Theme.ts +++ b/openaev-front/src/components/Theme.ts @@ -69,3 +69,5 @@ export const LabelColorDict = { Green: 'GREEN', Orange: 'ORANGE', } as const; + +export const FONT_FAMILY_CODE = 'Consolas, monaco, monospace'; diff --git a/openaev-front/src/components/ThemeDark.ts b/openaev-front/src/components/ThemeDark.ts index 12aea240b6f..810651c7880 100644 --- a/openaev-front/src/components/ThemeDark.ts +++ b/openaev-front/src/components/ThemeDark.ts @@ -4,7 +4,7 @@ import LogoCollapsed from '../static/images/logo_dark.png'; import LogoText from '../static/images/logo_text_dark.png'; import { hexToRGB } from '../utils/Colors'; import { fileUri } from '../utils/Environment'; -import { type LabelColor, LabelColorDict } from './Theme'; +import { FONT_FAMILY_CODE, type LabelColor, LabelColorDict } from './Theme'; const EE_COLOR = '#00f1bd'; @@ -14,7 +14,6 @@ const THEME_DARK_DEFAULT_SECONDARY = '#00f1bd'; const THEME_DARK_DEFAULT_ACCENT = '#0f1e38'; const THEME_DARK_DEFAULT_PAPER = '#09101e'; const THEME_DARK_DEFAULT_NAV = '#070d19'; -export const BACKGROUND_COLOR_GREY = '#181E27'; const ThemeDark = ( logo: string | null = null, @@ -31,7 +30,10 @@ const ThemeDark = ( borderRadius: 4, palette: { mode: 'dark', - common: { white: '#ffffff' }, + common: { + white: '#ffffff', + black: '#000000', + }, error: { main: '#f44336', dark: '#c62828', @@ -166,12 +168,12 @@ const ThemeDark = ( borderTopRightRadius: 'inherit', }, 'pre': { - fontFamily: 'Consolas, monaco, monospace', + fontFamily: FONT_FAMILY_CODE, color: '#ffffff !important', background: `${accent || THEME_DARK_DEFAULT_ACCENT} !important`, }, 'code': { - fontFamily: 'Consolas, monaco, monospace', + fontFamily: FONT_FAMILY_CODE, color: '#ffffff !important', background: `${accent || '#01478d'} !important`, padding: 3, diff --git a/openaev-front/src/components/ThemeLight.ts b/openaev-front/src/components/ThemeLight.ts index 1f93e59b4f9..41c17aa89e1 100644 --- a/openaev-front/src/components/ThemeLight.ts +++ b/openaev-front/src/components/ThemeLight.ts @@ -4,7 +4,7 @@ import LogoCollapsed from '../static/images/logo_light.png'; import LogoText from '../static/images/logo_text_light.png'; import { hexToRGB } from '../utils/Colors'; import { fileUri } from '../utils/Environment'; -import { type LabelColor, LabelColorDict } from './Theme'; +import { FONT_FAMILY_CODE, type LabelColor, LabelColorDict } from './Theme'; const EE_COLOR = '#0c7e69'; @@ -30,7 +30,10 @@ const ThemeLight = ( borderRadius: 4, palette: { mode: 'light', - common: { white: '#ffffff' }, + common: { + white: '#ffffff', + black: '#000000', + }, error: { main: '#f44336', dark: '#c62828', @@ -165,12 +168,12 @@ const ThemeLight = ( borderTopRightRadius: 'inherit', }, 'pre': { - fontFamily: 'Consolas, monaco, monospace', + fontFamily: FONT_FAMILY_CODE, color: '#000000 !important', background: `${accent || THEME_LIGHT_DEFAULT_ACCENT} !important`, }, 'code': { - fontFamily: 'Consolas, monaco, monospace', + fontFamily: FONT_FAMILY_CODE, color: '#000000 !important', background: `${accent || THEME_LIGHT_DEFAULT_ACCENT} !important`, padding: 3, diff --git a/openaev-front/src/utils/api-types.d.ts b/openaev-front/src/utils/api-types.d.ts index dae173a9fa4..ae9c039a224 100644 --- a/openaev-front/src/utils/api-types.d.ts +++ b/openaev-front/src/utils/api-types.d.ts @@ -3054,6 +3054,11 @@ export interface InjectResultOverviewOutput { injects_tags?: string[]; } +export interface InjectResultPayloadExecutionOutput { + execution_execution_traces: ExecutionTrace[]; + payload_command_blocks: PayloadCommandBlock[]; +} + /** Inject linked to finding */ export interface InjectSimple { /** Inject Id */ diff --git a/openaev-front/src/utils/lang/en.json b/openaev-front/src/utils/lang/en.json index d15d42f159b..aa934693fac 100644 --- a/openaev-front/src/utils/lang/en.json +++ b/openaev-front/src/utils/lang/en.json @@ -1653,6 +1653,7 @@ "techniques": "Techniques", "Telemetry manager": "Telemetry manager", "Template": "Template", + "Terminal view": "Terminal view", "Test": "Test", "Test Details": "Test Details", "Test execution time": "Test execution time", diff --git a/openaev-front/src/utils/lang/es.json b/openaev-front/src/utils/lang/es.json index 4cb3e273b52..ec8824e4ec7 100644 --- a/openaev-front/src/utils/lang/es.json +++ b/openaev-front/src/utils/lang/es.json @@ -1653,6 +1653,7 @@ "techniques": "Técnicas", "Telemetry manager": "Gestor de telemetría", "Template": "Plantilla", + "Terminal view": "Vista del terminal", "Test": "Prueba", "Test Details": "Detalles de la prueba", "Test execution time": "Tiempo de ejecución de la prueba", @@ -1940,7 +1941,7 @@ "You dont have the right access. Please contact your administrator.": "Usted no tiene el acceso correcto. Por favor, contacte con su administrador.", "You have canceled the registration process": "Ha cancelado el proceso de registro", "You have canceled the unregistration process": "Has cancelado la cancelación del registro", - "You just need to try? Get right now": "¿Sólo necesitas probar? Obtener ahora mismo", + "You just need to try ? Get right now {url}.": "¿Sólo tienes que probar? Obtener ahora mismo {url}.", "You must be logged to access this page": "Debes estar registrado para acceder a esta página", "You must set a simulation": "Debe establecer una simulación", "You probably need a public email in your github account": "Probablemente necesites un email público en tu cuenta de github", diff --git a/openaev-front/src/utils/lang/fr.json b/openaev-front/src/utils/lang/fr.json index 730cea24ef6..8ba1f812c4b 100644 --- a/openaev-front/src/utils/lang/fr.json +++ b/openaev-front/src/utils/lang/fr.json @@ -1653,6 +1653,7 @@ "techniques": "Techniques", "Telemetry manager": "Gestionnaire de télémétrie", "Template": "Modèle", + "Terminal view": "Vue du terminal", "Test": "Tester", "Test Details": "Détails du test", "Test execution time": "Heure d'execution du test", diff --git a/openaev-front/src/utils/lang/zh.json b/openaev-front/src/utils/lang/zh.json index 40b904d4a57..acc5cf45486 100644 --- a/openaev-front/src/utils/lang/zh.json +++ b/openaev-front/src/utils/lang/zh.json @@ -1653,6 +1653,7 @@ "techniques": "技术", "Telemetry manager": "遥测管理器", "Template": "模板", + "Terminal view": "终端视图", "Test": "测试", "Test Details": "测试详情", "Test execution time": "測試執行時間", diff --git a/openaev-front/src/utils/target/TargetUtils.ts b/openaev-front/src/utils/target/TargetUtils.ts new file mode 100644 index 00000000000..55e7affc9ae --- /dev/null +++ b/openaev-front/src/utils/target/TargetUtils.ts @@ -0,0 +1,9 @@ +import type { InjectTarget } from '../api-types'; + +export const isAssetGroups = (target: InjectTarget) => { + return target.target_type === 'ASSETS_GROUPS'; +}; + +export const isAgent = (target: InjectTarget) => { + return target.target_type === 'AGENT'; +}; diff --git a/openaev-front/yarn.lock b/openaev-front/yarn.lock index dc970b54662..1506b9c5326 100644 --- a/openaev-front/yarn.lock +++ b/openaev-front/yarn.lock @@ -217,7 +217,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.10.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": +"@babel/runtime@npm:^7.10.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": version: 7.26.10 resolution: "@babel/runtime@npm:7.26.10" dependencies: @@ -4679,13 +4679,6 @@ __metadata: languageName: node linkType: hard -"character-entities-legacy@npm:^1.0.0": - version: 1.1.4 - resolution: "character-entities-legacy@npm:1.1.4" - checksum: 10c0/ea4ca9c29887335eed86d78fc67a640168342b1274da84c097abb0575a253d1265281a5052f9a863979e952bcc267b4ecaaf4fe233a7e1e0d8a47806c65b96c7 - languageName: node - linkType: hard - "character-entities-legacy@npm:^3.0.0": version: 3.0.0 resolution: "character-entities-legacy@npm:3.0.0" @@ -4693,13 +4686,6 @@ __metadata: languageName: node linkType: hard -"character-entities@npm:^1.0.0": - version: 1.2.4 - resolution: "character-entities@npm:1.2.4" - checksum: 10c0/ad015c3d7163563b8a0ee1f587fb0ef305ef344e9fd937f79ca51cccc233786a01d591d989d5bf7b2e66b528ac9efba47f3b1897358324e69932f6d4b25adfe1 - languageName: node - linkType: hard - "character-entities@npm:^2.0.0": version: 2.0.2 resolution: "character-entities@npm:2.0.2" @@ -4707,13 +4693,6 @@ __metadata: languageName: node linkType: hard -"character-reference-invalid@npm:^1.0.0": - version: 1.1.4 - resolution: "character-reference-invalid@npm:1.1.4" - checksum: 10c0/29f05081c5817bd1e975b0bf61e77b60a40f62ad371d0f0ce0fdb48ab922278bc744d1fbe33771dced751887a8403f265ff634542675c8d7375f6ff4811efd0e - languageName: node - linkType: hard - "character-reference-invalid@npm:^2.0.0": version: 2.0.1 resolution: "character-reference-invalid@npm:2.0.1" @@ -4899,13 +4878,6 @@ __metadata: languageName: node linkType: hard -"comma-separated-tokens@npm:^1.0.0": - version: 1.0.8 - resolution: "comma-separated-tokens@npm:1.0.8" - checksum: 10c0/c3bcfeaa6d50313528a006a40bcc0f9576086665c9b48d4b3a76ddd63e7d6174734386c98be1881cbf6ecfc25e1db61cd775a7b896d2ea7a65de28f83a0f9b17 - languageName: node - linkType: hard - "comma-separated-tokens@npm:^2.0.0": version: 2.0.3 resolution: "comma-separated-tokens@npm:2.0.3" @@ -7197,13 +7169,6 @@ __metadata: languageName: node linkType: hard -"hast-util-parse-selector@npm:^2.0.0": - version: 2.2.5 - resolution: "hast-util-parse-selector@npm:2.2.5" - checksum: 10c0/29b7ee77960ded6a99d30c287d922243071cc07b39f2006f203bd08ee54eb8f66bdaa86ef6527477c766e2382d520b60ee4e4087f189888c35d8bcc020173648 - languageName: node - linkType: hard - "hast-util-parse-selector@npm:^3.0.0": version: 3.1.1 resolution: "hast-util-parse-selector@npm:3.1.1" @@ -7341,19 +7306,6 @@ __metadata: languageName: node linkType: hard -"hastscript@npm:^6.0.0": - version: 6.0.0 - resolution: "hastscript@npm:6.0.0" - dependencies: - "@types/hast": "npm:^2.0.0" - comma-separated-tokens: "npm:^1.0.0" - hast-util-parse-selector: "npm:^2.0.0" - property-information: "npm:^5.0.0" - space-separated-tokens: "npm:^1.0.0" - checksum: 10c0/f76d9cf373cb075c8523c8ad52709f09f7e02b7c9d3152b8d35c65c265b9f1878bed6023f215a7d16523921036d40a7da292cb6f4399af9b5eccac2a5a5eb330 - languageName: node - linkType: hard - "hastscript@npm:^7.0.0": version: 7.2.0 resolution: "hastscript@npm:7.2.0" @@ -7756,13 +7708,6 @@ __metadata: languageName: node linkType: hard -"is-alphabetical@npm:^1.0.0": - version: 1.0.4 - resolution: "is-alphabetical@npm:1.0.4" - checksum: 10c0/1505b1de5a1fd74022c05fb21b0e683a8f5229366bac8dc4d34cf6935bcfd104d1125a5e6b083fb778847629f76e5bdac538de5367bdf2b927a1356164e23985 - languageName: node - linkType: hard - "is-alphabetical@npm:^2.0.0": version: 2.0.1 resolution: "is-alphabetical@npm:2.0.1" @@ -7770,16 +7715,6 @@ __metadata: languageName: node linkType: hard -"is-alphanumerical@npm:^1.0.0": - version: 1.0.4 - resolution: "is-alphanumerical@npm:1.0.4" - dependencies: - is-alphabetical: "npm:^1.0.0" - is-decimal: "npm:^1.0.0" - checksum: 10c0/d623abae7130a7015c6bf33d99151d4e7005572fd170b86568ff4de5ae86ac7096608b87dd4a1d4dbbd497e392b6396930ba76c9297a69455909cebb68005905 - languageName: node - linkType: hard - "is-alphanumerical@npm:^2.0.0": version: 2.0.1 resolution: "is-alphanumerical@npm:2.0.1" @@ -7887,13 +7822,6 @@ __metadata: languageName: node linkType: hard -"is-decimal@npm:^1.0.0": - version: 1.0.4 - resolution: "is-decimal@npm:1.0.4" - checksum: 10c0/a4ad53c4c5c4f5a12214e7053b10326711f6a71f0c63ba1314a77bd71df566b778e4ebd29f9fb6815f07a4dc50c3767fb19bd6fc9fa05e601410f1d64ffeac48 - languageName: node - linkType: hard - "is-decimal@npm:^2.0.0": version: 2.0.1 resolution: "is-decimal@npm:2.0.1" @@ -7946,13 +7874,6 @@ __metadata: languageName: node linkType: hard -"is-hexadecimal@npm:^1.0.0": - version: 1.0.4 - resolution: "is-hexadecimal@npm:1.0.4" - checksum: 10c0/ec4c64e5624c0f240922324bc697e166554f09d3ddc7633fc526084502626445d0a871fbd8cae52a9844e83bd0bb414193cc5a66806d7b2867907003fc70c5ea - languageName: node - linkType: hard - "is-hexadecimal@npm:^2.0.0": version: 2.0.1 resolution: "is-hexadecimal@npm:2.0.1" @@ -9921,7 +9842,7 @@ __metadata: react-markdown: "npm:10.1.0" react-redux: "npm:9.2.0" react-router: "npm:7.9.5" - react-syntax-highlighter: "npm:15.6.6" + react-syntax-highlighter: "npm:16.1.0" redux: "npm:5.0.1" redux-thunk: "npm:3.1.0" remark-flexible-markers: "npm:1.3.1" @@ -10069,20 +9990,6 @@ __metadata: languageName: node linkType: hard -"parse-entities@npm:^2.0.0": - version: 2.0.0 - resolution: "parse-entities@npm:2.0.0" - dependencies: - character-entities: "npm:^1.0.0" - character-entities-legacy: "npm:^1.0.0" - character-reference-invalid: "npm:^1.0.0" - is-alphanumerical: "npm:^1.0.0" - is-decimal: "npm:^1.0.0" - is-hexadecimal: "npm:^1.0.0" - checksum: 10c0/f85a22c0ea406ff26b53fdc28641f01cc36fa49eb2e3135f02693286c89ef0bcefc2262d99b3688e20aac2a14fd10b75c518583e875c1b9fe3d1f937795e0854 - languageName: node - linkType: hard - "parse-entities@npm:^4.0.0": version: 4.0.2 resolution: "parse-entities@npm:4.0.2" @@ -10336,13 +10243,6 @@ __metadata: languageName: node linkType: hard -"prismjs@npm:~1.27.0": - version: 1.27.0 - resolution: "prismjs@npm:1.27.0" - checksum: 10c0/841cbf53e837a42df9155c5ce1be52c4a0a8967ac916b52a27d066181a3578186c634e52d06d0547fb62b65c486b99b95f826dd54966619f9721b884f486b498 - languageName: node - linkType: hard - "proc-log@npm:^5.0.0": version: 5.0.0 resolution: "proc-log@npm:5.0.0" @@ -10371,15 +10271,6 @@ __metadata: languageName: node linkType: hard -"property-information@npm:^5.0.0": - version: 5.6.0 - resolution: "property-information@npm:5.6.0" - dependencies: - xtend: "npm:^4.0.0" - checksum: 10c0/d54b77c31dc13bb6819559080b2c67d37d94be7dc271f404f139a16a57aa96fcc0b3ad806d4a5baef9e031744853e4afe3df2e37275aacb1f78079bbb652c5af - languageName: node - linkType: hard - "property-information@npm:^6.0.0": version: 6.5.0 resolution: "property-information@npm:6.5.0" @@ -10777,19 +10668,19 @@ __metadata: languageName: node linkType: hard -"react-syntax-highlighter@npm:15.6.6": - version: 15.6.6 - resolution: "react-syntax-highlighter@npm:15.6.6" +"react-syntax-highlighter@npm:16.1.0": + version: 16.1.0 + resolution: "react-syntax-highlighter@npm:16.1.0" dependencies: - "@babel/runtime": "npm:^7.3.1" + "@babel/runtime": "npm:^7.28.4" highlight.js: "npm:^10.4.1" highlightjs-vue: "npm:^1.0.0" lowlight: "npm:^1.17.0" prismjs: "npm:^1.30.0" - refractor: "npm:^3.6.0" + refractor: "npm:^5.0.0" peerDependencies: react: ">= 0.14.0" - checksum: 10c0/894f8b7c79ed40866c0fc542ad0a2040128a8c7e6e6decfd06ef092d8af9c63788ecdd911ea9b2b433e361a4a33a14f721bcec81fd59f1e7394442ade4e7ea46 + checksum: 10c0/0c07a569a3390c6bf5fd383bf4b6eca03cd4421623859f7b776547128550534b91ad3d767e3f21f2f0e1ff17b380804e3f3af5aff42b2cd646af9b0c26c6d758 languageName: node linkType: hard @@ -10874,17 +10765,6 @@ __metadata: languageName: node linkType: hard -"refractor@npm:^3.6.0": - version: 3.6.0 - resolution: "refractor@npm:3.6.0" - dependencies: - hastscript: "npm:^6.0.0" - parse-entities: "npm:^2.0.0" - prismjs: "npm:~1.27.0" - checksum: 10c0/63ab62393c8c2fd7108c2ea1eff721c0ad2a1a6eee60fdd1b47f4bb25cf298667dc97d041405b3e718b0817da12b37a86ed07ebee5bd2ca6405611f1bae456db - languageName: node - linkType: hard - "refractor@npm:^4.8.0": version: 4.9.0 resolution: "refractor@npm:4.9.0" @@ -10897,6 +10777,18 @@ __metadata: languageName: node linkType: hard +"refractor@npm:^5.0.0": + version: 5.0.0 + resolution: "refractor@npm:5.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/prismjs": "npm:^1.0.0" + hastscript: "npm:^9.0.0" + parse-entities: "npm:^4.0.0" + checksum: 10c0/7b69a3b525f88de4adae3467f30ff7e80513fa53e65d85656f169ded16e314f471dfa936ccc545ae604e178ee7a108cbd94e6144a5bd541ed8d20626cc75e7b8 + languageName: node + linkType: hard + "reftools@npm:^1.1.9": version: 1.1.9 resolution: "reftools@npm:1.1.9" @@ -11745,13 +11637,6 @@ __metadata: languageName: node linkType: hard -"space-separated-tokens@npm:^1.0.0": - version: 1.1.5 - resolution: "space-separated-tokens@npm:1.1.5" - checksum: 10c0/3ee0a6905f89e1ffdfe474124b1ade9fe97276a377a0b01350bc079b6ec566eb5b219e26064cc5b7f3899c05bde51ffbc9154290b96eaf82916a1e2c2c13ead9 - languageName: node - linkType: hard - "space-separated-tokens@npm:^2.0.0": version: 2.0.2 resolution: "space-separated-tokens@npm:2.0.2" @@ -13223,13 +13108,6 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.0": - version: 4.0.2 - resolution: "xtend@npm:4.0.2" - checksum: 10c0/366ae4783eec6100f8a02dff02ac907bf29f9a00b82ac0264b4d8b832ead18306797e283cf19de776538babfdcb2101375ec5646b59f08c52128ac4ab812ed0e - languageName: node - linkType: hard - "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8"