diff --git a/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/service/ExpectationsExpirationManagerService.java b/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/service/ExpectationsExpirationManagerService.java index 476256d357..220376c518 100644 --- a/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/service/ExpectationsExpirationManagerService.java +++ b/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/service/ExpectationsExpirationManagerService.java @@ -40,19 +40,16 @@ private void computeExpectations(@NotNull final List expectat List expectationAssets = expectations.stream().toList(); expectationAssets.forEach((expectation) -> { Long userExpirationTime = expectation.getExpirationTime(); - if (userExpirationTime != null) { - // Maximum time for detection - if (isExpired(expectation, Math.toIntExact(userExpirationTime / 60))) { - String result = computeFailedMessage(expectation.getType()); - this.injectExpectationService.computeExpectation( - expectation, - this.config.getId(), - "collector", - PRODUCT_NAME, - result, - false - ); - } + if (isExpired(expectation, Math.toIntExact(userExpirationTime / 60))) { + String result = computeFailedMessage(expectation.getType()); + this.injectExpectationService.computeExpectation( + expectation, + this.config.getId(), + "collector", + PRODUCT_NAME, + result, + false + ); } }); @@ -62,19 +59,16 @@ private void computeExpectationsForAssets(@NotNull final List List expectationAssets = expectations.stream().filter(e -> e.getAsset() != null).toList(); expectationAssets.forEach((expectation) -> { Long userExpirationTime = expectation.getExpirationTime(); - if (userExpirationTime != null) { - // Maximum time for detection - if (isExpired(expectation, Math.toIntExact(userExpirationTime / 60))) { - String result = computeFailedMessage(expectation.getType()); - this.injectExpectationService.computeExpectation( - expectation, - this.config.getId(), - "collector", - PRODUCT_NAME, - result, - false - ); - } + if (isExpired(expectation, Math.toIntExact(userExpirationTime / 60))) { + String result = computeFailedMessage(expectation.getType()); + this.injectExpectationService.computeExpectation( + expectation, + this.config.getId(), + "collector", + PRODUCT_NAME, + result, + false + ); } }); diff --git a/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java index 62d4a2939a..11f4d77cc0 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/caldera/CalderaExecutor.java @@ -47,269 +47,328 @@ @RequiredArgsConstructor @Log public class CalderaExecutor extends Injector { - private final int RETRY_NUMBER = 20; - private final CalderaInjectorConfig config; - private final CalderaInjectorService calderaService; - private final EndpointService endpointService; - private final AssetGroupService assetGroupService; - private final InjectRepository injectRepository; + private final int RETRY_NUMBER = 20; - @Override - @Transactional - public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) throws Exception { - CalderaInjectContent content = contentConvert(injection, CalderaInjectContent.class); - String obfuscator = content.getObfuscator() != null ? content.getObfuscator() : "base64"; - Inject inject = this.injectRepository.findById(injection.getInjection().getInject().getId()).orElseThrow(); + private final CalderaInjectorConfig config; + private final CalderaInjectorService calderaService; + private final EndpointService endpointService; + private final AssetGroupService assetGroupService; + private final InjectRepository injectRepository; - Map assets = this.resolveAllAssets(injection); - // Execute inject for all assets - if (assets.isEmpty()) { - execution.addTrace(traceError("Found 0 asset to execute the ability on (likely this inject does not have any target or the targeted asset is inactive and has been purged)")); - } + @Override + @Transactional + public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) + throws Exception { + CalderaInjectContent content = contentConvert(injection, CalderaInjectContent.class); + String obfuscator = content.getObfuscator() != null ? content.getObfuscator() : "base64"; + Inject inject = this.injectRepository.findById(injection.getInjection().getInject().getId()).orElseThrow(); - List asyncIds = new ArrayList<>(); - List expectations = new ArrayList<>(); - List> additionalFields = new ArrayList<>(); + Map assets = this.resolveAllAssets(injection); + // Execute inject for all assets + if (assets.isEmpty()) { + execution.addTrace(traceError( + "Found 0 asset to execute the ability on (likely this inject does not have any target or the targeted asset is inactive and has been purged)")); + } - inject.getInjectorContract().ifPresentOrElse(injectorContract -> { - ObjectNode rawContent = injection.getInjection().getInject().getContent(); - ObjectNode contractContent = injectorContract.getConvertedContent(); - List contractTextFields = StreamSupport.stream(contractContent.get("fields").spliterator(), false) - .filter(contractElement -> contractElement.get("type").asText().equals("text")) - .toList(); + List asyncIds = new ArrayList<>(); + List expectations = new ArrayList<>(); + List> additionalFields = new ArrayList<>(); - if (!contractTextFields.isEmpty()) { - contractTextFields.forEach(jsonField -> { - String key = jsonField.get("key").asText(); - if (rawContent.get(key) != null) { - Map additionalField = new HashMap<>(); - additionalField.put("trait", key); - additionalField.put("value", rawContent.get(key).asText()); - additionalFields.add(additionalField); - } - }); - } + inject.getInjectorContract().ifPresentOrElse(injectorContract -> { + ObjectNode rawContent = injection.getInjection().getInject().getContent(); + ObjectNode contractContent = injectorContract.getConvertedContent(); + List contractTextFields = StreamSupport.stream(contractContent.get("fields").spliterator(), false) + .filter(contractElement -> contractElement.get("type").asText().equals("text")) + .toList(); - String contract; - if (injectorContract.getPayload() != null) { - // This is a payload, need to create the ability on the fly - List abilities = calderaService.abilities().stream().filter(ability -> ability.getName().equals(injectorContract.getPayload().getId())).toList(); - if (!abilities.isEmpty()) { - calderaService.deleteAbility(abilities.getFirst()); - } - Ability abilityToExecute = calderaService.createAbility(injectorContract.getPayload()); - contract = abilityToExecute.getAbility_id(); - } else { - contract = injectorContract.getId(); + if (!contractTextFields.isEmpty()) { + contractTextFields.forEach(jsonField -> { + String key = jsonField.get("key").asText(); + if (rawContent.get(key) != null) { + Map additionalField = new HashMap<>(); + additionalField.put("trait", key); + additionalField.put("value", rawContent.get(key).asText()); + additionalFields.add(additionalField); + } + }); + } + + String contract; + if (injectorContract.getPayload() != null) { + // This is a payload, need to create the ability on the fly + List abilities = calderaService.abilities().stream() + .filter(ability -> ability.getName().equals(injectorContract.getPayload().getId())).toList(); + if (!abilities.isEmpty()) { + calderaService.deleteAbility(abilities.getFirst()); } - assets.forEach((asset, aBoolean) -> { - try { - Endpoint executionEndpoint = this.findAndRegisterAssetForExecution(injection.getInjection().getInject(), asset); - if (executionEndpoint != null) { - if (Arrays.stream(injectorContract.getPlatforms()).anyMatch(s -> s.equals(executionEndpoint.getPlatform()))) { - String result = this.calderaService.exploit(obfuscator, executionEndpoint.getExternalReference(), contract, additionalFields); - if (result.contains("complete")) { - ExploitResult exploitResult = this.calderaService.exploitResult(executionEndpoint.getExternalReference(), contract); - asyncIds.add(exploitResult.getLinkId()); - execution.addTrace(traceInfo(EXECUTION_TYPE_COMMAND, exploitResult.getCommand())); - // Compute expectations - boolean isInGroup = assets.get(executionEndpoint.getParent()); - List injectExpectationSignatures = new ArrayList<>(); - if (injectorContract.getPayload() != null) { - switch (injectorContract.getPayload().getType()) { - case "Command": - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_PROCESS_NAME).value(executionEndpoint.getProcessName()).build()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_COMMAND_LINE).value(exploitResult.getCommand()).build()); - break; - case "Executable": - Executable payloadExecutable = (Executable) Hibernate.unproxy(injectorContract.getPayload()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME).value(payloadExecutable.getExecutableFile().getName()).build()); - // TODO File hash - break; - case "FileDrop": - FileDrop payloadFileDrop = (FileDrop) Hibernate.unproxy(injectorContract.getPayload()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME).value(payloadFileDrop.getFileDropFile().getName()).build()); - // TODO File hash - break; - case "DnsResolution": - DnsResolution payloadDnsResolution = (DnsResolution) Hibernate.unproxy(injectorContract.getPayload()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_HOSTNAME).value(payloadDnsResolution.getHostname().split("\\r?\\n")[0]).build()); - break; - default: - throw new UnsupportedOperationException("Payload type " + injectorContract.getPayload().getType() + " is not supported"); - } - } else { - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_PROCESS_NAME).value(executionEndpoint.getProcessName()).build()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_COMMAND_LINE).value(exploitResult.getCommand()).build()); - } - computeExpectationsForAsset(expectations, content, executionEndpoint.getParent(), isInGroup, injectExpectationSignatures); - execution.addTrace(traceInfo("Caldera executed the ability on asset " + asset.getName() + " using " + executionEndpoint.getProcessName() + " (paw: " + executionEndpoint.getExternalReference() + ", linkID: " + exploitResult.getLinkId() + ")")); - } else { - execution.addTrace(traceError("Caldera failed to execute the ability on asset " + asset.getName() + " (" + result + ")")); - } - } else { - execution.addTrace(traceError("Caldera failed to execute ability on asset " + asset.getName() + " (platform is not compatible: " + executionEndpoint.getPlatform().name() + ")")); - } + Ability abilityToExecute = calderaService.createAbility(injectorContract.getPayload()); + contract = abilityToExecute.getAbility_id(); + } else { + contract = injectorContract.getId(); + } + assets.forEach((asset, aBoolean) -> { + try { + Endpoint executionEndpoint = this.findAndRegisterAssetForExecution(injection.getInjection().getInject(), + asset); + if (executionEndpoint != null) { + if (Arrays.stream(injectorContract.getPlatforms()) + .anyMatch(s -> s.equals(executionEndpoint.getPlatform()))) { + String result = this.calderaService.exploit(obfuscator, executionEndpoint.getExternalReference(), + contract, additionalFields); + if (result.contains("complete")) { + ExploitResult exploitResult = this.calderaService.exploitResult( + executionEndpoint.getExternalReference(), contract); + asyncIds.add(exploitResult.getLinkId()); + execution.addTrace(traceInfo(EXECUTION_TYPE_COMMAND, exploitResult.getCommand())); + // Compute expectations + boolean isInGroup = assets.get(executionEndpoint.getParent()); + List injectExpectationSignatures = new ArrayList<>(); + if (injectorContract.getPayload() != null) { + switch (injectorContract.getPayload().getType()) { + case "Command": + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_PROCESS_NAME) + .value(executionEndpoint.getProcessName()).build()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_COMMAND_LINE) + .value(exploitResult.getCommand()).build()); + break; + case "Executable": + Executable payloadExecutable = (Executable) Hibernate.unproxy(injectorContract.getPayload()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME) + .value(payloadExecutable.getExecutableFile().getName()).build()); + // TODO File hash + break; + case "FileDrop": + FileDrop payloadFileDrop = (FileDrop) Hibernate.unproxy(injectorContract.getPayload()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME) + .value(payloadFileDrop.getFileDropFile().getName()).build()); + // TODO File hash + break; + case "DnsResolution": + DnsResolution payloadDnsResolution = (DnsResolution) Hibernate.unproxy( + injectorContract.getPayload()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_HOSTNAME) + .value(payloadDnsResolution.getHostname().split("\\r?\\n")[0]).build()); + break; + default: + throw new UnsupportedOperationException( + "Payload type " + injectorContract.getPayload().getType() + " is not supported"); + } } else { - execution.addTrace(traceError("Caldera failed to execute the ability on asset " + asset.getName() + " (temporary injector not spawned correctly)")); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_PROCESS_NAME) + .value(executionEndpoint.getProcessName()).build()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_COMMAND_LINE) + .value(exploitResult.getCommand()).build()); } - } catch (Exception e) { - execution.addTrace(traceError("Caldera failed to execute the ability on asset " + asset.getName() + " (" + e.getMessage() + ")")); - log.severe(Arrays.toString(e.getStackTrace())); + computeExpectationsForAsset(expectations, content, executionEndpoint.getParent(), isInGroup, + injectExpectationSignatures); + execution.addTrace(traceInfo("Caldera executed the ability on asset " + asset.getName() + " using " + + executionEndpoint.getProcessName() + " (paw: " + executionEndpoint.getExternalReference() + + ", linkID: " + exploitResult.getLinkId() + ")")); + } else { + execution.addTrace(traceError( + "Caldera failed to execute the ability on asset " + asset.getName() + " (" + result + ")")); + } + } else { + execution.addTrace(traceError( + "Caldera failed to execute ability on asset " + asset.getName() + " (platform is not compatible: " + + executionEndpoint.getPlatform().name() + ")")); } - }); + } else { + execution.addTrace(traceError("Caldera failed to execute the ability on asset " + asset.getName() + + " (temporary injector not spawned correctly)")); + } + } catch (Exception e) { + execution.addTrace(traceError( + "Caldera failed to execute the ability on asset " + asset.getName() + " (" + e.getMessage() + ")")); + log.severe(Arrays.toString(e.getStackTrace())); + } + }); }, - ()->execution.addTrace(traceError("Inject does not have a contract"))); + () -> execution.addTrace(traceError("Inject does not have a contract"))); - if (asyncIds.isEmpty()) { - throw new UnsupportedOperationException("Caldera failed to execute the ability due to above errors"); - } - - List assetGroups = injection.getAssetGroups(); - assetGroups.forEach((assetGroup -> computeExpectationsForAssetGroup(expectations, content, assetGroup, new ArrayList<>()))); - String message = "Caldera executed the ability on " + asyncIds.size() + " asset(s)"; - execution.addTrace(traceInfo(message, asyncIds)); - return new ExecutionProcess(true, expectations); + if (asyncIds.isEmpty()) { + throw new UnsupportedOperationException("Caldera failed to execute the ability due to above errors"); } - @Override - public InjectStatusCommandLine getCommandsLines(String externalId) { - InjectStatusCommandLine commandLine = new InjectStatusCommandLine(); - Set contents = new HashSet<>(); - Set cleanCommands = new HashSet<>(); - Ability ability = calderaService.findAbilityById(externalId); - if(ability != null) { - ability.getExecutors().forEach(executor -> { - if(executor.getCommand() != null && !executor.getCommand().isBlank()) { - contents.add(executor.getCommand()); - } - if(executor.getCleanup() != null && !executor.getCleanup().isEmpty()) { - cleanCommands.addAll(executor.getCleanup()); - } - }); + List assetGroups = injection.getAssetGroups(); + assetGroups.forEach( + (assetGroup -> computeExpectationsForAssetGroup(expectations, content, assetGroup, new ArrayList<>()))); + String message = "Caldera executed the ability on " + asyncIds.size() + " asset(s)"; + execution.addTrace(traceInfo(message, asyncIds)); + return new ExecutionProcess(true, expectations); + } + + @Override + public InjectStatusCommandLine getCommandsLines(String externalId) { + InjectStatusCommandLine commandLine = new InjectStatusCommandLine(); + Set contents = new HashSet<>(); + Set cleanCommands = new HashSet<>(); + Ability ability = calderaService.findAbilityById(externalId); + if (ability != null) { + ability.getExecutors().forEach(executor -> { + if (executor.getCommand() != null && !executor.getCommand().isBlank()) { + contents.add(executor.getCommand()); } - commandLine.setExternalId(externalId); - commandLine.setContent(contents.stream().toList()); - commandLine.setCleanupCommand(cleanCommands.stream().toList()); - return commandLine; + if (executor.getCleanup() != null && !executor.getCleanup().isEmpty()) { + cleanCommands.addAll(executor.getCleanup()); + } + }); } + commandLine.setExternalId(externalId); + commandLine.setContent(contents.stream().toList()); + commandLine.setCleanupCommand(cleanCommands.stream().toList()); + return commandLine; + } - // -- PRIVATE -- + // -- PRIVATE -- - private Map resolveAllAssets(@NotNull final ExecutableInject inject) { - Map assets = new HashMap<>(); - inject.getAssets().forEach((asset -> { - assets.put(asset, false); - })); - inject.getAssetGroups().forEach((assetGroup -> { - List assetsFromGroup = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - // Verify asset validity - assetsFromGroup.forEach((asset) -> { - assets.put(asset, true); - }); - })); - return assets; - } + private Map resolveAllAssets(@NotNull final ExecutableInject inject) { + Map assets = new HashMap<>(); + inject.getAssets().forEach((asset -> { + assets.put(asset, false); + })); + inject.getAssetGroups().forEach((assetGroup -> { + List assetsFromGroup = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + // Verify asset validity + assetsFromGroup.forEach((asset) -> { + assets.put(asset, true); + }); + })); + return assets; + } - private Endpoint findAndRegisterAssetForExecution(@NotNull final Inject inject, @NotNull final Asset asset) throws InterruptedException { - Endpoint endpointForExecution = null; - if (!asset.getType().equals("Endpoint")) { - log.log(Level.SEVERE, "Caldera failed to execute ability on the asset because type is not supported: " + asset.getType()); - return null; - } - log.log(Level.INFO, "Trying to find an available executor for " + asset.getName()); - Endpoint assetEndpoint = (Endpoint) Hibernate.unproxy(asset); - for (int i = 0; i < RETRY_NUMBER; i++) { - // Find an executor agent matching the asset - log.log(Level.INFO, "Listing agents..."); - List agents = this.calderaService.agents().stream().filter(agent -> - agent.getExe_name().contains("implant") - && (now().toEpochMilli() - Time.toInstant(agent.getCreated()).toEpochMilli()) < Asset.ACTIVE_THRESHOLD - && (agent.getHost().equals(assetEndpoint.getHostname()) || agent.getHost().split("\\.")[0].equals(assetEndpoint.getHostname().split("\\.")[0])) - && Arrays.stream(assetEndpoint.getIps()).anyMatch(s -> Arrays.stream(agent.getHost_ip_addrs()).toList().contains(s)) - ).toList(); - log.log(Level.INFO, "List return with " + agents.size() + " agents"); - if (!agents.isEmpty()) { - for (Agent agent : agents) { - // Check in the database if not exist - Optional resolvedExistingEndpoint = this.endpointService.findByExternalReference(agent.getPaw()); - if (resolvedExistingEndpoint.isEmpty()) { - log.log(Level.INFO, "Agent found and not present in the database, creating it..."); - Endpoint newEndpoint = new Endpoint(); - newEndpoint.setInject(inject); - newEndpoint.setParent(asset); - newEndpoint.setName(assetEndpoint.getName()); - newEndpoint.setIps(assetEndpoint.getIps()); - newEndpoint.setHostname(assetEndpoint.getHostname()); - newEndpoint.setPlatform(assetEndpoint.getPlatform()); - newEndpoint.setArch(assetEndpoint.getArch()); - newEndpoint.setExternalReference(agent.getPaw()); - newEndpoint.setExecutor(assetEndpoint.getExecutor()); - newEndpoint.setProcessName(agent.getExe_name()); - endpointForExecution = this.endpointService.createEndpoint(newEndpoint); - break; - } - } - } - if (endpointForExecution != null) { - break; - } - Thread.sleep(5000); + private Endpoint findAndRegisterAssetForExecution(@NotNull final Inject inject, @NotNull final Asset asset) + throws InterruptedException { + Endpoint endpointForExecution = null; + if (!asset.getType().equals("Endpoint")) { + log.log(Level.SEVERE, + "Caldera failed to execute ability on the asset because type is not supported: " + asset.getType()); + return null; + } + log.log(Level.INFO, "Trying to find an available executor for " + asset.getName()); + Endpoint assetEndpoint = (Endpoint) Hibernate.unproxy(asset); + for (int i = 0; i < RETRY_NUMBER; i++) { + // Find an executor agent matching the asset + log.log(Level.INFO, "Listing agents..."); + List agents = this.calderaService.agents().stream().filter(agent -> + agent.getExe_name().contains("implant") + && (now().toEpochMilli() - Time.toInstant(agent.getCreated()).toEpochMilli()) < Asset.ACTIVE_THRESHOLD + && (agent.getHost().equals(assetEndpoint.getHostname()) || agent.getHost().split("\\.")[0].equals( + assetEndpoint.getHostname().split("\\.")[0])) + && Arrays.stream(assetEndpoint.getIps()) + .anyMatch(s -> Arrays.stream(agent.getHost_ip_addrs()).toList().contains(s)) + ).toList(); + log.log(Level.INFO, "List return with " + agents.size() + " agents"); + if (!agents.isEmpty()) { + for (Agent agent : agents) { + // Check in the database if not exist + Optional resolvedExistingEndpoint = this.endpointService.findByExternalReference(agent.getPaw()); + if (resolvedExistingEndpoint.isEmpty()) { + log.log(Level.INFO, "Agent found and not present in the database, creating it..."); + Endpoint newEndpoint = new Endpoint(); + newEndpoint.setInject(inject); + newEndpoint.setParent(asset); + newEndpoint.setName(assetEndpoint.getName()); + newEndpoint.setIps(assetEndpoint.getIps()); + newEndpoint.setHostname(assetEndpoint.getHostname()); + newEndpoint.setPlatform(assetEndpoint.getPlatform()); + newEndpoint.setArch(assetEndpoint.getArch()); + newEndpoint.setExternalReference(agent.getPaw()); + newEndpoint.setExecutor(assetEndpoint.getExecutor()); + newEndpoint.setProcessName(agent.getExe_name()); + endpointForExecution = this.endpointService.createEndpoint(newEndpoint); + break; + } } - return endpointForExecution; + } + if (endpointForExecution != null) { + break; + } + Thread.sleep(5000); } + return endpointForExecution; + } - /** - * In case of direct asset, we have an individual expectation for the asset - */ - private void computeExpectationsForAsset(@NotNull final List expectations, @NotNull final CalderaInjectContent content, @NotNull final Asset asset, final boolean expectationGroup, final List injectExpectationSignatures) { - if (!content.getExpectations().isEmpty()) { - expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { - case PREVENTION -> - Stream.of(preventionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), asset, expectationGroup, injectExpectationSignatures)); // expectationGroup usefully in front-end - case DETECTION -> - Stream.of(detectionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), asset, expectationGroup, injectExpectationSignatures)); - case MANUAL -> - Stream.of(manualExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), asset, expectationGroup)); - default -> Stream.of(); - }).toList()); - } + /** + * In case of direct asset, we have an individual expectation for the asset + */ + private void computeExpectationsForAsset(@NotNull final List expectations, + @NotNull final CalderaInjectContent content, @NotNull final Asset asset, final boolean expectationGroup, + final List injectExpectationSignatures) { + if (!content.getExpectations().isEmpty()) { + expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { + case PREVENTION -> Stream.of( + preventionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), + asset, expectationGroup, expectation.getExpirationTime(), + injectExpectationSignatures)); // expectationGroup usefully in front-end + case DETECTION -> Stream.of( + detectionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), + asset, expectationGroup, expectation.getExpirationTime(), injectExpectationSignatures)); + case MANUAL -> Stream.of( + manualExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), + asset, expectation.getExpirationTime(), expectationGroup)); + default -> Stream.of(); + }).toList()); } + } - /** - * In case of asset group if expectation group -> we have an expectation for the group and one for each asset if not - * expectation group -> we have an individual expectation for each asset - */ - private void computeExpectationsForAssetGroup(@NotNull final List expectations, @NotNull final CalderaInjectContent content, @NotNull final AssetGroup assetGroup, final List injectExpectationSignatures) { - if (!content.getExpectations().isEmpty()) { - expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { - case PREVENTION -> { - // Verify that at least one asset in the group has been executed - List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - if (assets.stream().anyMatch((asset) -> expectations.stream().filter(e -> EXPECTATION_TYPE.PREVENTION == e.type()).anyMatch((e) -> ((PreventionExpectation) e).getAsset() != null && ((PreventionExpectation) e).getAsset().getId().equals(asset.getId())))) { - yield Stream.of(preventionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), injectExpectationSignatures)); - } - yield Stream.of(); - } - case DETECTION -> { - // Verify that at least one asset in the group has been executed - List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - if (assets.stream().anyMatch((asset) -> expectations.stream().filter(e -> EXPECTATION_TYPE.DETECTION == e.type()).anyMatch((e) -> ((DetectionExpectation) e).getAsset() != null && ((DetectionExpectation) e).getAsset().getId().equals(asset.getId())))) { - yield Stream.of(detectionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), injectExpectationSignatures)); - } - yield Stream.of(); - } - case MANUAL -> { - // Verify that at least one asset in the group has been executed - List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - if (assets.stream().anyMatch((asset) -> expectations.stream().filter(e -> EXPECTATION_TYPE.MANUAL == e.type()).anyMatch((e) -> ((ManualExpectation) e).getAsset() != null && ((ManualExpectation) e).getAsset().getId().equals(asset.getId())))) { - yield Stream.of(manualExpectationForAssetGroup(expectation.getScore(), expectation.getName(), expectation.getDescription(), assetGroup, expectation.isExpectationGroup())); - } - yield Stream.of(); - } - default -> Stream.of(); - }).toList()); + /** + * In case of asset group if expectation group -> we have an expectation for the group and one for each asset if not + * expectation group -> we have an individual expectation for each asset + */ + private void computeExpectationsForAssetGroup(@NotNull final List expectations, + @NotNull final CalderaInjectContent content, @NotNull final AssetGroup assetGroup, + final List injectExpectationSignatures) { + if (!content.getExpectations().isEmpty()) { + expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { + case PREVENTION -> { + // Verify that at least one asset in the group has been executed + List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + if (assets.stream().anyMatch( + (asset) -> expectations.stream().filter(e -> EXPECTATION_TYPE.PREVENTION == e.type()).anyMatch( + (e) -> ((PreventionExpectation) e).getAsset() != null && ((PreventionExpectation) e).getAsset() + .getId().equals(asset.getId())))) { + yield Stream.of(preventionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), + expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), + expectation.getExpirationTime(), injectExpectationSignatures)); + } + yield Stream.of(); + } + case DETECTION -> { + // Verify that at least one asset in the group has been executed + List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + if (assets.stream().anyMatch( + (asset) -> expectations.stream().filter(e -> EXPECTATION_TYPE.DETECTION == e.type()).anyMatch( + (e) -> ((DetectionExpectation) e).getAsset() != null && ((DetectionExpectation) e).getAsset().getId() + .equals(asset.getId())))) { + yield Stream.of(detectionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), + expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), + expectation.getExpirationTime(), injectExpectationSignatures)); + } + yield Stream.of(); + } + case MANUAL -> { + // Verify that at least one asset in the group has been executed + List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + if (assets.stream().anyMatch((asset) -> expectations.stream().filter(e -> EXPECTATION_TYPE.MANUAL == e.type()) + .anyMatch((e) -> ((ManualExpectation) e).getAsset() != null && ((ManualExpectation) e).getAsset().getId() + .equals(asset.getId())))) { + yield Stream.of(manualExpectationForAssetGroup(expectation.getScore(), expectation.getName(), + expectation.getDescription(), assetGroup, expectation.getExpirationTime(), + expectation.isExpectationGroup())); + } + yield Stream.of(); } + default -> Stream.of(); + }).toList()); } + } } diff --git a/openbas-api/src/main/java/io/openbas/injectors/challenge/ChallengeExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/challenge/ChallengeExecutor.java index 1c7b7c2eb6..8bd81eeed4 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/challenge/ChallengeExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/challenge/ChallengeExecutor.java @@ -32,99 +32,99 @@ @Component(ChallengeContract.TYPE) public class ChallengeExecutor extends Injector { - @Resource - private OpenBASConfig openBASConfig; + @Resource + private OpenBASConfig openBASConfig; - private ChallengeRepository challengeRepository; + private ChallengeRepository challengeRepository; - private EmailService emailService; + private EmailService emailService; - @Value("${openbas.mail.imap.enabled}") - private boolean imapEnabled; + @Value("${openbas.mail.imap.enabled}") + private boolean imapEnabled; - @Autowired - public void setChallengeRepository(ChallengeRepository challengeRepository) { - this.challengeRepository = challengeRepository; - } + @Autowired + public void setChallengeRepository(ChallengeRepository challengeRepository) { + this.challengeRepository = challengeRepository; + } - @Autowired - public void setEmailService(EmailService emailService) { - this.emailService = emailService; - } + @Autowired + public void setEmailService(EmailService emailService) { + this.emailService = emailService; + } - private String buildChallengeUri(ExecutionContext context, Exercise exercise, Challenge challenge) { - String userId = context.getUser().getId(); - String challengeId = challenge.getId(); - String exerciseId = exercise.getId(); - return openBASConfig.getBaseUrl() + "/challenges/" + exerciseId + "?user=" + userId + "&challenge=" + challengeId; - } + private String buildChallengeUri(ExecutionContext context, Exercise exercise, Challenge challenge) { + String userId = context.getUser().getId(); + String challengeId = challenge.getId(); + String exerciseId = exercise.getId(); + return openBASConfig.getBaseUrl() + "/challenges/" + exerciseId + "?user=" + userId + "&challenge=" + challengeId; + } - @Override - public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) { - try { - ChallengeContent content = contentConvert(injection, ChallengeContent.class); - List challenges = fromIterable(challengeRepository.findAllById(content.getChallenges())); - String contract = injection - .getInjection() - .getInject() - .getInjectorContract() - .map(InjectorContract::getId) - .orElseThrow(() -> new UnsupportedOperationException("Inject does not have a contract")); + @Override + public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) { + try { + ChallengeContent content = contentConvert(injection, ChallengeContent.class); + List challenges = fromIterable(challengeRepository.findAllById(content.getChallenges())); + String contract = injection + .getInjection() + .getInject() + .getInjectorContract() + .map(InjectorContract::getId) + .orElseThrow(() -> new UnsupportedOperationException("Inject does not have a contract")); - if (contract.equals(CHALLENGE_PUBLISH)) { - // Challenge publishing is only linked to execution date of this inject. - String challengeNames = challenges.stream().map(Challenge::getName).collect(Collectors.joining(",")); - String publishedMessage = "Challenges (" + challengeNames + ") marked as published"; - execution.addTrace(traceSuccess(publishedMessage)); - // Send the publication message. - Exercise exercise = injection.getInjection().getExercise(); - String from = exercise.getFrom(); - List replyTos = exercise.getReplyTos(); - List users = injection.getUsers(); - List documents = injection.getInjection().getInject().getDocuments().stream() - .filter(InjectDocument::isAttached).map(InjectDocument::getDocument).toList(); - List attachments = resolveAttachments(execution, injection, documents); - String message = content.buildMessage(injection, imapEnabled); - boolean encrypted = content.isEncrypted(); - users.forEach(userInjectContext -> { - try { - // Put the challenges variables in the injection context - List challengeVariables = challenges.stream() - .map(challenge -> new ChallengeVariable(challenge.getId(), challenge.getName(), - buildChallengeUri(userInjectContext, exercise, challenge))) - .toList(); - userInjectContext.put("challenges", challengeVariables); - // Send the email. - emailService.sendEmail(execution, userInjectContext, from, replyTos, content.getInReplyTo(), encrypted, - content.getSubject(), message, attachments); - } catch (Exception e) { - execution.addTrace(traceError(e.getMessage())); - } - }); - // Return expectations - List expectations = new ArrayList<>(); - if (!content.getExpectations().isEmpty()) { - expectations.addAll( - content.getExpectations() - .stream() - .flatMap((entry) -> switch (entry.getType()) { - case MANUAL -> Stream.of( - (Expectation) new ManualExpectation(entry.getScore(), entry.getName(), entry.getDescription(), entry.isExpectationGroup()) - ); - case CHALLENGE -> challenges.stream() - .map(challenge -> (Expectation) new ChallengeExpectation(entry.getScore(), challenge, entry.isExpectationGroup())); - default -> Stream.of(); - }) - .toList() - ); - } - return new ExecutionProcess(false, expectations); - } else { - throw new UnsupportedOperationException("Unknown contract " + contract); - } - } catch (Exception e) { + if (contract.equals(CHALLENGE_PUBLISH)) { + // Challenge publishing is only linked to execution date of this inject. + String challengeNames = challenges.stream().map(Challenge::getName).collect(Collectors.joining(",")); + String publishedMessage = "Challenges (" + challengeNames + ") marked as published"; + execution.addTrace(traceSuccess(publishedMessage)); + // Send the publication message. + Exercise exercise = injection.getInjection().getExercise(); + String from = exercise.getFrom(); + List replyTos = exercise.getReplyTos(); + List users = injection.getUsers(); + List documents = injection.getInjection().getInject().getDocuments().stream() + .filter(InjectDocument::isAttached).map(InjectDocument::getDocument).toList(); + List attachments = resolveAttachments(execution, injection, documents); + String message = content.buildMessage(injection, imapEnabled); + boolean encrypted = content.isEncrypted(); + users.forEach(userInjectContext -> { + try { + // Put the challenges variables in the injection context + List challengeVariables = challenges.stream() + .map(challenge -> new ChallengeVariable(challenge.getId(), challenge.getName(), + buildChallengeUri(userInjectContext, exercise, challenge))) + .toList(); + userInjectContext.put("challenges", challengeVariables); + // Send the email. + emailService.sendEmail(execution, userInjectContext, from, replyTos, content.getInReplyTo(), encrypted, + content.getSubject(), message, attachments); + } catch (Exception e) { execution.addTrace(traceError(e.getMessage())); + } + }); + // Return expectations + List expectations = new ArrayList<>(); + if (!content.getExpectations().isEmpty()) { + expectations.addAll( + content.getExpectations() + .stream() + .flatMap((entry) -> switch (entry.getType()) { + case MANUAL -> Stream.of( + (Expectation) new ManualExpectation(entry) + ); + case CHALLENGE -> challenges.stream() + .map(challenge -> (Expectation) new ChallengeExpectation(entry, challenge)); + default -> Stream.of(); + }) + .toList() + ); } - return new ExecutionProcess(false, List.of()); + return new ExecutionProcess(false, expectations); + } else { + throw new UnsupportedOperationException("Unknown contract " + contract); + } + } catch (Exception e) { + execution.addTrace(traceError(e.getMessage())); } + return new ExecutionProcess(false, List.of()); + } } diff --git a/openbas-api/src/main/java/io/openbas/injectors/channel/ChannelExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/channel/ChannelExecutor.java index 5aab36bc42..aa700c10d3 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/channel/ChannelExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/channel/ChannelExecutor.java @@ -118,10 +118,10 @@ public ExecutionProcess process(@NotNull final Execution execution, @NotNull fin .stream() .flatMap((entry) -> switch (entry.getType()) { case MANUAL -> Stream.of( - (Expectation) new ManualExpectation(entry.getScore(), entry.getName(), entry.getDescription(), entry.isExpectationGroup()) + (Expectation) new ManualExpectation(entry) ); case ARTICLE -> articles.stream() - .map(article -> (Expectation) new ChannelExpectation(entry.getScore(), article, entry.isExpectationGroup())); + .map(article -> (Expectation) new ChannelExpectation(entry, article)); default -> Stream.of(); }) .toList() diff --git a/openbas-api/src/main/java/io/openbas/injectors/email/EmailExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/email/EmailExecutor.java index dcc202ee77..248f40d909 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/email/EmailExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/email/EmailExecutor.java @@ -38,7 +38,8 @@ public void setEmailService(EmailService emailService) { this.emailService = emailService; } - private void sendMulti(Execution execution, List users, String from, List replyTos, String inReplyTo, + private void sendMulti(Execution execution, List users, String from, List replyTos, + String inReplyTo, String subject, String message, List attachments) { try { emailService.sendEmail(execution, users, from, replyTos, inReplyTo, subject, message, attachments); @@ -47,11 +48,13 @@ private void sendMulti(Execution execution, List users, String } } - private void sendSingle(Execution execution, List users, String from, List replyTos, String inReplyTo, + private void sendSingle(Execution execution, List users, String from, List replyTos, + String inReplyTo, boolean mustBeEncrypted, String subject, String message, List attachments) { users.forEach(user -> { try { - emailService.sendEmail(execution, user, from, replyTos, inReplyTo, mustBeEncrypted, subject, message, attachments); + emailService.sendEmail(execution, user, from, replyTos, inReplyTo, mustBeEncrypted, subject, message, + attachments); } catch (Exception e) { execution.addTrace(traceError(e.getMessage())); } @@ -59,7 +62,8 @@ private void sendSingle(Execution execution, List users, Strin } @Override - public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) throws Exception { + public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) + throws Exception { Inject inject = injection.getInjection().getInject(); EmailContent content = contentConvert(injection, EmailContent.class); List documents = inject.getDocuments().stream().filter(InjectDocument::isAttached) @@ -78,18 +82,20 @@ public ExecutionProcess process(@NotNull final Execution execution, @NotNull fin String from = exercise != null ? exercise.getFrom() : this.openBASConfig.getDefaultMailer(); List replyTos = exercise != null ? exercise.getReplyTos() : List.of(this.openBASConfig.getDefaultReplyTo()); //noinspection SwitchStatementWithTooFewBranches - switch (inject.getInjectorContract().map(InjectorContract::getId).orElseThrow(() -> new UnsupportedOperationException("Inject does not have a contract"))) { + switch (inject.getInjectorContract().map(InjectorContract::getId) + .orElseThrow(() -> new UnsupportedOperationException("Inject does not have a contract"))) { case EMAIL_GLOBAL -> sendMulti(execution, users, from, replyTos, inReplyTo, subject, message, attachments); - default -> sendSingle(execution, users, from, replyTos, inReplyTo, mustBeEncrypted, subject, message, attachments); + default -> + sendSingle(execution, users, from, replyTos, inReplyTo, mustBeEncrypted, subject, message, attachments); } List expectations = content.getExpectations() - .stream() - .flatMap((entry) -> switch (entry.getType()) { - case MANUAL -> - Stream.of((Expectation) new ManualExpectation(entry.getScore(), entry.getName(), entry.getDescription(), entry.isExpectationGroup())); - default -> Stream.of(); - }) - .toList(); + .stream() + .flatMap((entry) -> switch (entry.getType()) { + case MANUAL -> Stream.of( + (Expectation) new ManualExpectation(entry)); + default -> Stream.of(); + }) + .toList(); return new ExecutionProcess(false, expectations); } } diff --git a/openbas-api/src/main/java/io/openbas/injectors/openbas/OpenBASImplantExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/openbas/OpenBASImplantExecutor.java index 9f51c5f4e2..bdcdf18e2c 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/openbas/OpenBASImplantExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/openbas/OpenBASImplantExecutor.java @@ -38,161 +38,200 @@ @Log public class OpenBASImplantExecutor extends Injector { - private final AssetGroupService assetGroupService; - private final InjectRepository injectRepository; - private final InjectStatusRepository injectStatusRepository; + private final AssetGroupService assetGroupService; + private final InjectRepository injectRepository; + private final InjectStatusRepository injectStatusRepository; - private Map resolveAllAssets(@NotNull final ExecutableInject inject) { - Map assets = new HashMap<>(); - inject.getAssets().forEach((asset -> { - assets.put(asset, false); - })); - inject.getAssetGroups().forEach((assetGroup -> { - List assetsFromGroup = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - // Verify asset validity - assetsFromGroup.forEach((asset) -> { - assets.put(asset, true); - }); - })); - return assets; - } + private Map resolveAllAssets(@NotNull final ExecutableInject inject) { + Map assets = new HashMap<>(); + inject.getAssets().forEach((asset -> { + assets.put(asset, false); + })); + inject.getAssetGroups().forEach((assetGroup -> { + List assetsFromGroup = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + // Verify asset validity + assetsFromGroup.forEach((asset) -> { + assets.put(asset, true); + }); + })); + return assets; + } - /** - * In case of direct asset, we have an individual expectation for the asset - */ - private void computeExpectationsForAsset(@NotNull final List expectations, @NotNull final OpenBASImplantInjectContent content, @NotNull final Asset asset, final boolean expectationGroup, final List injectExpectationSignatures) { - if (!content.getExpectations().isEmpty()) { - expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { - case PREVENTION -> - Stream.of(preventionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), asset, expectationGroup, injectExpectationSignatures)); // expectationGroup usefully in front-end - case DETECTION -> - Stream.of(detectionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), asset, expectationGroup, injectExpectationSignatures)); - case MANUAL -> - Stream.of(manualExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), asset, expectationGroup)); - default -> Stream.of(); - }).toList()); - } + /** + * In case of direct asset, we have an individual expectation for the asset + */ + private void computeExpectationsForAsset(@NotNull final List expectations, + @NotNull final OpenBASImplantInjectContent content, @NotNull final Asset asset, final boolean expectationGroup, + final List injectExpectationSignatures) { + if (!content.getExpectations().isEmpty()) { + expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { + case PREVENTION -> Stream.of( + preventionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), + asset, expectationGroup, expectation.getExpirationTime(), + injectExpectationSignatures)); // expectationGroup usefully in front-end + case DETECTION -> Stream.of( + detectionExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), + asset, expectationGroup, expectation.getExpirationTime(), injectExpectationSignatures)); + case MANUAL -> Stream.of( + manualExpectationForAsset(expectation.getScore(), expectation.getName(), expectation.getDescription(), + asset, expectation.getExpirationTime(), expectationGroup)); + default -> Stream.of(); + }).toList()); } + } - /** - * In case of asset group if expectation group -> we have an expectation for the group and one for each asset if not - * expectation group -> we have an individual expectation for each asset - */ - private void computeExpectationsForAssetGroup(@NotNull final List expectations, @NotNull final OpenBASImplantInjectContent content, @NotNull final AssetGroup assetGroup, final List injectExpectationSignatures) { - if (!content.getExpectations().isEmpty()) { - expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { - case PREVENTION -> { - // Verify that at least one asset in the group has been executed - List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - if (assets.stream().anyMatch((asset) -> expectations.stream().filter(e -> InjectExpectation.EXPECTATION_TYPE.PREVENTION == e.type()).anyMatch((e) -> ((PreventionExpectation) e).getAsset() != null && ((PreventionExpectation) e).getAsset().getId().equals(asset.getId())))) { - yield Stream.of(preventionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), injectExpectationSignatures)); - } - yield Stream.of(); - } - case DETECTION -> { - // Verify that at least one asset in the group has been executed - List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - if (assets.stream().anyMatch((asset) -> expectations.stream().filter(e -> InjectExpectation.EXPECTATION_TYPE.DETECTION == e.type()).anyMatch((e) -> ((DetectionExpectation) e).getAsset() != null && ((DetectionExpectation) e).getAsset().getId().equals(asset.getId())))) { - yield Stream.of(detectionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), injectExpectationSignatures)); - } - yield Stream.of(); - } - case MANUAL -> { - // Verify that at least one asset in the group has been executed - List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); - if (assets.stream().anyMatch((asset) -> expectations.stream().filter(e -> InjectExpectation.EXPECTATION_TYPE.MANUAL == e.type()).anyMatch((e) -> ((ManualExpectation) e).getAsset() != null && ((ManualExpectation) e).getAsset().getId().equals(asset.getId())))) { - yield Stream.of(manualExpectationForAssetGroup(expectation.getScore(), expectation.getName(), expectation.getDescription(), assetGroup, expectation.isExpectationGroup())); - } - yield Stream.of(); - } - default -> Stream.of(); - }).toList()); + /** + * In case of asset group if expectation group -> we have an expectation for the group and one for each asset if not + * expectation group -> we have an individual expectation for each asset + */ + private void computeExpectationsForAssetGroup(@NotNull final List expectations, + @NotNull final OpenBASImplantInjectContent content, @NotNull final AssetGroup assetGroup, + final List injectExpectationSignatures) { + if (!content.getExpectations().isEmpty()) { + expectations.addAll(content.getExpectations().stream().flatMap((expectation) -> switch (expectation.getType()) { + case PREVENTION -> { + // Verify that at least one asset in the group has been executed + List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + if (assets.stream().anyMatch( + (asset) -> expectations.stream().filter(e -> InjectExpectation.EXPECTATION_TYPE.PREVENTION == e.type()) + .anyMatch( + (e) -> ((PreventionExpectation) e).getAsset() != null && ((PreventionExpectation) e).getAsset() + .getId().equals(asset.getId())))) { + yield Stream.of(preventionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), + expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), + expectation.getExpirationTime(), injectExpectationSignatures)); + } + yield Stream.of(); + } + case DETECTION -> { + // Verify that at least one asset in the group has been executed + List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + if (assets.stream().anyMatch( + (asset) -> expectations.stream().filter(e -> InjectExpectation.EXPECTATION_TYPE.DETECTION == e.type()) + .anyMatch( + (e) -> ((DetectionExpectation) e).getAsset() != null && ((DetectionExpectation) e).getAsset() + .getId().equals(asset.getId())))) { + yield Stream.of(detectionExpectationForAssetGroup(expectation.getScore(), expectation.getName(), + expectation.getDescription(), assetGroup, expectation.isExpectationGroup(), + expectation.getExpirationTime(), injectExpectationSignatures)); + } + yield Stream.of(); + } + case MANUAL -> { + // Verify that at least one asset in the group has been executed + List assets = this.assetGroupService.assetsFromAssetGroup(assetGroup.getId()); + if (assets.stream().anyMatch( + (asset) -> expectations.stream().filter(e -> InjectExpectation.EXPECTATION_TYPE.MANUAL == e.type()) + .anyMatch( + (e) -> ((ManualExpectation) e).getAsset() != null && ((ManualExpectation) e).getAsset().getId() + .equals(asset.getId())))) { + yield Stream.of(manualExpectationForAssetGroup(expectation.getScore(), expectation.getName(), + expectation.getDescription(), assetGroup, expectation.getExpirationTime(), + expectation.isExpectationGroup())); + } + yield Stream.of(); } + default -> Stream.of(); + }).toList()); } + } - @Override - public ExecutionProcess process(Execution execution, ExecutableInject injection) throws Exception { - Inject inject = this.injectRepository.findById(injection.getInjection().getInject().getId()).orElseThrow(); - Map assets = this.resolveAllAssets(injection); + @Override + public ExecutionProcess process(Execution execution, ExecutableInject injection) throws Exception { + Inject inject = this.injectRepository.findById(injection.getInjection().getInject().getId()).orElseThrow(); + Map assets = this.resolveAllAssets(injection); - // Check assets target - if (assets.isEmpty()) { - execution.addTrace(traceError("Found 0 asset to execute the ability on (likely this inject does not have any target or the targeted asset is inactive and has been purged)")); - } + // Check assets target + if (assets.isEmpty()) { + execution.addTrace(traceError( + "Found 0 asset to execute the ability on (likely this inject does not have any target or the targeted asset is inactive and has been purged)")); + } - // Compute expectations - OpenBASImplantInjectContent content = contentConvert(injection, OpenBASImplantInjectContent.class); + // Compute expectations + OpenBASImplantInjectContent content = contentConvert(injection, OpenBASImplantInjectContent.class); - List expectations = new ArrayList<>(); - assets.forEach((asset, isInGroup) -> { - List injectExpectationSignatures = new ArrayList<>(); + List expectations = new ArrayList<>(); + assets.forEach((asset, isInGroup) -> { + List injectExpectationSignatures = new ArrayList<>(); - inject.getInjectorContract().ifPresent(injectorContract -> { - if (injectorContract.getPayload() != null) { - // Put the correct number in inject status - int totalActionsCount = 0; - switch (injectorContract.getPayload().getType()) { - case "Command": - Command payloadCommand = (Command) Hibernate.unproxy(injectorContract.getPayload()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_PROCESS_NAME).value("obas-implant-" + inject.getId()).build()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_COMMAND_LINE).value(payloadCommand.getContent()).build()); - totalActionsCount = totalActionsCount + 1; - if (payloadCommand.getPrerequisites() != null) { - totalActionsCount = totalActionsCount + payloadCommand.getPrerequisites().size(); - } - if (payloadCommand.getCleanupCommand() != null) { - totalActionsCount = totalActionsCount + 1; - } - break; - case "Executable": - Executable payloadExecutable = (Executable) Hibernate.unproxy(injectorContract.getPayload()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME).value(payloadExecutable.getExecutableFile().getName()).build()); - totalActionsCount = totalActionsCount + 2; - if (payloadExecutable.getPrerequisites() != null) { - totalActionsCount = totalActionsCount + payloadExecutable.getPrerequisites().size(); - } - if (payloadExecutable.getCleanupCommand() != null) { - totalActionsCount = totalActionsCount + 1; - } - // TODO File hash - break; - case "FileDrop": - FileDrop payloadFileDrop = (FileDrop) Hibernate.unproxy(injectorContract.getPayload()); - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME).value(payloadFileDrop.getFileDropFile().getName()).build()); - totalActionsCount = totalActionsCount + 1; - if (payloadFileDrop.getPrerequisites() != null) { - totalActionsCount = totalActionsCount + payloadFileDrop.getPrerequisites().size(); - } - if (payloadFileDrop.getCleanupCommand() != null) { - totalActionsCount = totalActionsCount + 1; - } - // TODO File hash - break; - case "DnsResolution": - DnsResolution payloadDnsResolution = (DnsResolution) Hibernate.unproxy(injectorContract.getPayload()); - // TODO this is only generating the signature for the first hostname - // Problem is: we are not supporting multiple signatures of the same type with "AND" parameters, and this can be in multiple alerts downstream in security platforms - // Tech pain to refine - injectExpectationSignatures.add(InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_HOSTNAME).value(payloadDnsResolution.getHostname().split("\\r?\\n")[0]).build()); - totalActionsCount = totalActionsCount + payloadDnsResolution.getHostname().split("\\r?\\n").length; - if (payloadDnsResolution.getPrerequisites() != null) { - totalActionsCount = totalActionsCount + payloadDnsResolution.getPrerequisites().size(); - } - if (payloadDnsResolution.getCleanupCommand() != null) { - totalActionsCount = totalActionsCount + 1; - } - break; - default: - throw new UnsupportedOperationException("Payload type " + injectorContract.getPayload().getType() + " is not supported"); - } - execution.setExpectedCount(totalActionsCount); - } - }); - computeExpectationsForAsset(expectations, content, asset, isInGroup, injectExpectationSignatures); - }); + inject.getInjectorContract().ifPresent(injectorContract -> { + if (injectorContract.getPayload() != null) { + // Put the correct number in inject status + int totalActionsCount = 0; + switch (injectorContract.getPayload().getType()) { + case "Command": + Command payloadCommand = (Command) Hibernate.unproxy(injectorContract.getPayload()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_PROCESS_NAME) + .value("obas-implant-" + inject.getId()).build()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_COMMAND_LINE) + .value(payloadCommand.getContent()).build()); + totalActionsCount = totalActionsCount + 1; + if (payloadCommand.getPrerequisites() != null) { + totalActionsCount = totalActionsCount + payloadCommand.getPrerequisites().size(); + } + if (payloadCommand.getCleanupCommand() != null) { + totalActionsCount = totalActionsCount + 1; + } + break; + case "Executable": + Executable payloadExecutable = (Executable) Hibernate.unproxy(injectorContract.getPayload()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME) + .value(payloadExecutable.getExecutableFile().getName()).build()); + totalActionsCount = totalActionsCount + 2; + if (payloadExecutable.getPrerequisites() != null) { + totalActionsCount = totalActionsCount + payloadExecutable.getPrerequisites().size(); + } + if (payloadExecutable.getCleanupCommand() != null) { + totalActionsCount = totalActionsCount + 1; + } + // TODO File hash + break; + case "FileDrop": + FileDrop payloadFileDrop = (FileDrop) Hibernate.unproxy(injectorContract.getPayload()); + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_FILE_NAME) + .value(payloadFileDrop.getFileDropFile().getName()).build()); + totalActionsCount = totalActionsCount + 1; + if (payloadFileDrop.getPrerequisites() != null) { + totalActionsCount = totalActionsCount + payloadFileDrop.getPrerequisites().size(); + } + if (payloadFileDrop.getCleanupCommand() != null) { + totalActionsCount = totalActionsCount + 1; + } + // TODO File hash + break; + case "DnsResolution": + DnsResolution payloadDnsResolution = (DnsResolution) Hibernate.unproxy(injectorContract.getPayload()); + // TODO this is only generating the signature for the first hostname + // Problem is: we are not supporting multiple signatures of the same type with "AND" parameters, and this can be in multiple alerts downstream in security platforms + // Tech pain to refine + injectExpectationSignatures.add( + InjectExpectationSignature.builder().type(EXPECTATION_SIGNATURE_TYPE_HOSTNAME) + .value(payloadDnsResolution.getHostname().split("\\r?\\n")[0]).build()); + totalActionsCount = totalActionsCount + payloadDnsResolution.getHostname().split("\\r?\\n").length; + if (payloadDnsResolution.getPrerequisites() != null) { + totalActionsCount = totalActionsCount + payloadDnsResolution.getPrerequisites().size(); + } + if (payloadDnsResolution.getCleanupCommand() != null) { + totalActionsCount = totalActionsCount + 1; + } + break; + default: + throw new UnsupportedOperationException( + "Payload type " + injectorContract.getPayload().getType() + " is not supported"); + } + execution.setExpectedCount(totalActionsCount); + } + }); + computeExpectationsForAsset(expectations, content, asset, isInGroup, injectExpectationSignatures); + }); - List assetGroups = injection.getAssetGroups(); - assetGroups.forEach((assetGroup -> computeExpectationsForAssetGroup(expectations, content, assetGroup, new ArrayList<>()))); - return new ExecutionProcess(true, expectations); - } + List assetGroups = injection.getAssetGroups(); + assetGroups.forEach( + (assetGroup -> computeExpectationsForAssetGroup(expectations, content, assetGroup, new ArrayList<>()))); + return new ExecutionProcess(true, expectations); + } } diff --git a/openbas-api/src/main/java/io/openbas/injectors/opencti/OpenCTIExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/opencti/OpenCTIExecutor.java index 8cc96b176a..158833e610 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/opencti/OpenCTIExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/opencti/OpenCTIExecutor.java @@ -45,7 +45,8 @@ private void createReport(Execution execution, String name, String description, } @Override - public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) throws Exception { + public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) + throws Exception { Inject inject = injection.getInjection().getInject(); CaseContent content = contentConvert(injection, CaseContent.class); List documents = inject.getDocuments().stream().filter(InjectDocument::isAttached) @@ -62,13 +63,12 @@ public ExecutionProcess process(@NotNull final Execution execution, @NotNull fin }); List expectations = content.getExpectations() - .stream() - .flatMap((entry) -> switch (entry.getType()) { - case MANUAL -> - Stream.of((Expectation) new ManualExpectation(entry.getScore(), entry.getName(), entry.getDescription(), entry.isExpectationGroup())); - default -> Stream.of(); - }) - .toList(); + .stream() + .flatMap((entry) -> switch (entry.getType()) { + case MANUAL -> Stream.of((Expectation) new ManualExpectation(entry)); + default -> Stream.of(); + }) + .toList(); return new ExecutionProcess(false, expectations); } } diff --git a/openbas-api/src/main/java/io/openbas/injectors/ovh/OvhSmsExecutor.java b/openbas-api/src/main/java/io/openbas/injectors/ovh/OvhSmsExecutor.java index 1faf2ee3e7..f22e9af12a 100644 --- a/openbas-api/src/main/java/io/openbas/injectors/ovh/OvhSmsExecutor.java +++ b/openbas-api/src/main/java/io/openbas/injectors/ovh/OvhSmsExecutor.java @@ -31,7 +31,8 @@ public class OvhSmsExecutor extends Injector { private final OvhSmsService smsService; @Override - public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) throws Exception { + public ExecutionProcess process(@NotNull final Execution execution, @NotNull final ExecutableInject injection) + throws Exception { Inject inject = injection.getInjection().getInject(); OvhSmsContent content = contentConvert(injection, OvhSmsContent.class); String smsMessage = content.buildMessage(inject.getFooter(), inject.getHeader()); @@ -63,13 +64,12 @@ public ExecutionProcess process(@NotNull final Execution execution, @NotNull fin }); if (isSmsSent.get()) { List expectations = content.getExpectations() - .stream() - .flatMap(entry -> switch (entry.getType()) { - case MANUAL -> - Stream.of((Expectation) new ManualExpectation(entry.getScore(), entry.getName(), entry.getDescription(), entry.isExpectationGroup())); - default -> Stream.of(); - }) - .toList(); + .stream() + .flatMap(entry -> switch (entry.getType()) { + case MANUAL -> Stream.of((Expectation) new ManualExpectation(entry)); + default -> Stream.of(); + }) + .toList(); return new ExecutionProcess(false, expectations); } return new ExecutionProcess(false, Collections.emptyList()); diff --git a/openbas-api/src/main/java/io/openbas/migration/V3_40__Add_column_expiration_time_expectations.java b/openbas-api/src/main/java/io/openbas/migration/V3_41__Add_column_expiration_time_expectations.java similarity index 95% rename from openbas-api/src/main/java/io/openbas/migration/V3_40__Add_column_expiration_time_expectations.java rename to openbas-api/src/main/java/io/openbas/migration/V3_41__Add_column_expiration_time_expectations.java index 0472813e00..ee525c6ccb 100644 --- a/openbas-api/src/main/java/io/openbas/migration/V3_40__Add_column_expiration_time_expectations.java +++ b/openbas-api/src/main/java/io/openbas/migration/V3_41__Add_column_expiration_time_expectations.java @@ -6,7 +6,7 @@ import java.sql.Connection; import java.sql.Statement; -public class V3_40__Add_column_expiration_time_expectations extends BaseJavaMigration { +public class V3_41__Add_column_expiration_time_expectations extends BaseJavaMigration { @Override public void migrate(Context context) throws Exception { diff --git a/openbas-framework/src/main/java/io/openbas/execution/Injector.java b/openbas-framework/src/main/java/io/openbas/execution/Injector.java index 44dc6bbdaa..562d25d919 100644 --- a/openbas-framework/src/main/java/io/openbas/execution/Injector.java +++ b/openbas-framework/src/main/java/io/openbas/execution/Injector.java @@ -30,227 +30,232 @@ public abstract class Injector { - @Resource - protected ObjectMapper mapper; - private FileService fileService; - private DocumentRepository documentRepository; - private InjectExpectationRepository injectExpectationRepository; + @Resource + protected ObjectMapper mapper; + private FileService fileService; + private DocumentRepository documentRepository; + private InjectExpectationRepository injectExpectationRepository; - @Autowired - public void setInjectExpectationRepository(InjectExpectationRepository injectExpectationRepository) { - this.injectExpectationRepository = injectExpectationRepository; - } + @Autowired + public void setInjectExpectationRepository(InjectExpectationRepository injectExpectationRepository) { + this.injectExpectationRepository = injectExpectationRepository; + } - @Autowired - public void setDocumentRepository(DocumentRepository documentRepository) { - this.documentRepository = documentRepository; - } + @Autowired + public void setDocumentRepository(DocumentRepository documentRepository) { + this.documentRepository = documentRepository; + } - @Autowired - public void setFileService(FileService fileService) { - this.fileService = fileService; - } + @Autowired + public void setFileService(FileService fileService) { + this.fileService = fileService; + } - public abstract ExecutionProcess process(Execution execution, ExecutableInject injection) throws Exception; + public abstract ExecutionProcess process(Execution execution, ExecutableInject injection) throws Exception; - public InjectStatusCommandLine getCommandsLines(String externalId) { - return null; - } + public InjectStatusCommandLine getCommandsLines(String externalId) { + return null; + } - private InjectExpectation expectationConverter( - @NotNull final ExecutableInject executableInject, - Expectation expectation) { - InjectExpectation expectationExecution = new InjectExpectation(); - return this.expectationConverter(expectationExecution, executableInject, expectation); - } + private InjectExpectation expectationConverter( + @NotNull final ExecutableInject executableInject, + Expectation expectation) { + InjectExpectation expectationExecution = new InjectExpectation(); + return this.expectationConverter(expectationExecution, executableInject, expectation); + } - private InjectExpectation expectationConverter( - @NotNull final Team team, - @NotNull final ExecutableInject executableInject, - Expectation expectation) { - InjectExpectation expectationExecution = new InjectExpectation(); - expectationExecution.setTeam(team); - return this.expectationConverter(expectationExecution, executableInject, expectation); - } + private InjectExpectation expectationConverter( + @NotNull final Team team, + @NotNull final ExecutableInject executableInject, + Expectation expectation) { + InjectExpectation expectationExecution = new InjectExpectation(); + expectationExecution.setTeam(team); + return this.expectationConverter(expectationExecution, executableInject, expectation); + } - private InjectExpectation expectationConverter( - @NotNull final Team team, - @NotNull final User user, - @NotNull final ExecutableInject executableInject, - Expectation expectation) { - InjectExpectation expectationExecution = new InjectExpectation(); - expectationExecution.setTeam(team); - expectationExecution.setUser(user); - return this.expectationConverter(expectationExecution, executableInject, expectation); - } + private InjectExpectation expectationConverter( + @NotNull final Team team, + @NotNull final User user, + @NotNull final ExecutableInject executableInject, + Expectation expectation) { + InjectExpectation expectationExecution = new InjectExpectation(); + expectationExecution.setTeam(team); + expectationExecution.setUser(user); + return this.expectationConverter(expectationExecution, executableInject, expectation); + } - private InjectExpectation expectationConverter( - @NotNull InjectExpectation expectationExecution, - @NotNull final ExecutableInject executableInject, - @NotNull final Expectation expectation) { - expectationExecution.setExercise(executableInject.getInjection().getExercise()); - expectationExecution.setInject(executableInject.getInjection().getInject()); - expectationExecution.setExpectedScore(expectation.getScore()); - expectationExecution.setExpectationGroup(expectation.isExpectationGroup()); - switch (expectation.type()) { - case ARTICLE -> { - expectationExecution.setName(expectation.getName()); - expectationExecution.setArticle(((ChannelExpectation) expectation).getArticle()); - } - case CHALLENGE -> { - expectationExecution.setName(expectation.getName()); - expectationExecution.setChallenge(((ChallengeExpectation) expectation).getChallenge()); - } - case DOCUMENT -> expectationExecution.setType(EXPECTATION_TYPE.DOCUMENT); - case TEXT -> expectationExecution.setType(EXPECTATION_TYPE.TEXT); - case DETECTION -> { - DetectionExpectation detectionExpectation = (DetectionExpectation) expectation; - expectationExecution.setName(detectionExpectation.getName()); - expectationExecution.setDetection(detectionExpectation.getAsset(), detectionExpectation.getAssetGroup()); - expectationExecution.setSignatures(detectionExpectation.getInjectExpectationSignatures()); - } - case PREVENTION -> { - PreventionExpectation preventionExpectation = (PreventionExpectation) expectation; - expectationExecution.setName(preventionExpectation.getName()); - expectationExecution.setPrevention(preventionExpectation.getAsset(), preventionExpectation.getAssetGroup()); - expectationExecution.setSignatures(preventionExpectation.getInjectExpectationSignatures()); - } - case MANUAL -> { - ManualExpectation manualExpectation = (ManualExpectation) expectation; - expectationExecution.setName(((ManualExpectation) expectation).getName()); - expectationExecution.setManual(manualExpectation.getAsset(), manualExpectation.getAssetGroup()); - expectationExecution.setDescription(((ManualExpectation) expectation).getDescription()); - } - default -> throw new IllegalStateException("Unexpected value: " + expectation); - } - return expectationExecution; + private InjectExpectation expectationConverter( + @NotNull InjectExpectation expectationExecution, + @NotNull final ExecutableInject executableInject, + @NotNull final Expectation expectation) { + expectationExecution.setExercise(executableInject.getInjection().getExercise()); + expectationExecution.setInject(executableInject.getInjection().getInject()); + expectationExecution.setExpectedScore(expectation.getScore()); + expectationExecution.setExpectationGroup(expectation.isExpectationGroup()); + expectationExecution.setExpirationTime(expectation.getExpirationTime()); + switch (expectation.type()) { + case ARTICLE -> { + expectationExecution.setName(expectation.getName()); + expectationExecution.setArticle(((ChannelExpectation) expectation).getArticle()); + } + case CHALLENGE -> { + expectationExecution.setName(expectation.getName()); + expectationExecution.setChallenge(((ChallengeExpectation) expectation).getChallenge()); + } + case DOCUMENT -> expectationExecution.setType(EXPECTATION_TYPE.DOCUMENT); + case TEXT -> expectationExecution.setType(EXPECTATION_TYPE.TEXT); + case DETECTION -> { + DetectionExpectation detectionExpectation = (DetectionExpectation) expectation; + expectationExecution.setName(detectionExpectation.getName()); + expectationExecution.setDetection(detectionExpectation.getAsset(), detectionExpectation.getAssetGroup()); + expectationExecution.setSignatures(detectionExpectation.getInjectExpectationSignatures()); + } + case PREVENTION -> { + PreventionExpectation preventionExpectation = (PreventionExpectation) expectation; + expectationExecution.setName(preventionExpectation.getName()); + expectationExecution.setPrevention(preventionExpectation.getAsset(), preventionExpectation.getAssetGroup()); + expectationExecution.setSignatures(preventionExpectation.getInjectExpectationSignatures()); + } + case MANUAL -> { + ManualExpectation manualExpectation = (ManualExpectation) expectation; + expectationExecution.setName(((ManualExpectation) expectation).getName()); + expectationExecution.setManual(manualExpectation.getAsset(), manualExpectation.getAssetGroup()); + expectationExecution.setDescription(((ManualExpectation) expectation).getDescription()); + } + default -> throw new IllegalStateException("Unexpected value: " + expectation); } + return expectationExecution; + } - @Transactional - public Execution execute(ExecutableInject executableInject) { - Execution execution = new Execution(executableInject.isRuntime()); - try { - boolean isScheduledInject = !executableInject.isDirect(); - boolean isAtomicTesting = executableInject.getInjection().getInject().isAtomicTesting(); - // If empty content, inject must be rejected - if (executableInject.getInjection().getInject().getContent() == null) { - throw new UnsupportedOperationException("Inject is empty"); - } - // If inject is too old, reject the execution - if (isScheduledInject && !isInInjectableRange(executableInject.getInjection())) { - throw new UnsupportedOperationException("Inject is now too old for execution"); - } - // Process the execution - ExecutionProcess executionProcess = process(execution, executableInject); - execution.setAsync(executionProcess.isAsync()); - List expectations = executionProcess.getExpectations(); - // Create the expectations - List teams = executableInject.getTeams(); - List assets = executableInject.getAssets(); - List assetGroups = executableInject.getAssetGroups(); - if ((isScheduledInject || isAtomicTesting) && !expectations.isEmpty()) { - if (!teams.isEmpty()) { - List injectExpectationsByTeam; + @Transactional + public Execution execute(ExecutableInject executableInject) { + Execution execution = new Execution(executableInject.isRuntime()); + try { + boolean isScheduledInject = !executableInject.isDirect(); + boolean isAtomicTesting = executableInject.getInjection().getInject().isAtomicTesting(); + // If empty content, inject must be rejected + if (executableInject.getInjection().getInject().getContent() == null) { + throw new UnsupportedOperationException("Inject is empty"); + } + // If inject is too old, reject the execution + if (isScheduledInject && !isInInjectableRange(executableInject.getInjection())) { + throw new UnsupportedOperationException("Inject is now too old for execution"); + } + // Process the execution + ExecutionProcess executionProcess = process(execution, executableInject); + execution.setAsync(executionProcess.isAsync()); + List expectations = executionProcess.getExpectations(); + // Create the expectations + List teams = executableInject.getTeams(); + List assets = executableInject.getAssets(); + List assetGroups = executableInject.getAssetGroups(); + if ((isScheduledInject || isAtomicTesting) && !expectations.isEmpty()) { + if (!teams.isEmpty()) { + List injectExpectationsByTeam; - List injectExpectationsByUserAndTeam; - // If atomicTesting, We create expectation for every player and every team - if (isAtomicTesting) { - injectExpectationsByTeam = teams.stream() - .flatMap(team -> expectations.stream() - .map(expectation -> expectationConverter(team, executableInject, expectation))) - .collect(Collectors.toList()); + List injectExpectationsByUserAndTeam; + // If atomicTesting, We create expectation for every player and every team + if (isAtomicTesting) { + injectExpectationsByTeam = teams.stream() + .flatMap(team -> expectations.stream() + .map(expectation -> expectationConverter(team, executableInject, expectation))) + .collect(Collectors.toList()); - injectExpectationsByUserAndTeam = teams.stream() - .flatMap(team -> team.getUsers().stream() - .flatMap(user -> expectations.stream() - .map(expectation -> expectationConverter(team, user, executableInject, expectation)))) - .toList(); - } else { - // Create expectations for every enabled player in every team - injectExpectationsByUserAndTeam = teams.stream() - .flatMap(team -> team.getExerciseTeamUsers().stream() - .filter(exerciseTeamUser -> exerciseTeamUser.getExercise().getId().equals(executableInject.getInjection().getExercise().getId())) - .flatMap(exerciseTeamUser -> expectations.stream() - .map(expectation -> expectationConverter(team, exerciseTeamUser.getUser(), executableInject, expectation)))) - .toList(); + injectExpectationsByUserAndTeam = teams.stream() + .flatMap(team -> team.getUsers().stream() + .flatMap(user -> expectations.stream() + .map(expectation -> expectationConverter(team, user, executableInject, expectation)))) + .toList(); + } else { + // Create expectations for every enabled player in every team + injectExpectationsByUserAndTeam = teams.stream() + .flatMap(team -> team.getExerciseTeamUsers().stream() + .filter(exerciseTeamUser -> exerciseTeamUser.getExercise().getId() + .equals(executableInject.getInjection().getExercise().getId())) + .flatMap(exerciseTeamUser -> expectations.stream() + .map(expectation -> expectationConverter(team, exerciseTeamUser.getUser(), executableInject, + expectation)))) + .toList(); - // Create a set of teams that have at least one enabled player - Set teamsWithEnabledPlayers = injectExpectationsByUserAndTeam.stream() - .map(InjectExpectation::getTeam) - .collect(Collectors.toSet()); + // Create a set of teams that have at least one enabled player + Set teamsWithEnabledPlayers = injectExpectationsByUserAndTeam.stream() + .map(InjectExpectation::getTeam) + .collect(Collectors.toSet()); - // Add only the expectations where the team has at least one enabled player - injectExpectationsByTeam = teamsWithEnabledPlayers.stream() - .flatMap(team -> expectations.stream() - .map(expectation -> expectationConverter(team, executableInject, expectation))) - .collect(Collectors.toList()); - } + // Add only the expectations where the team has at least one enabled player + injectExpectationsByTeam = teamsWithEnabledPlayers.stream() + .flatMap(team -> expectations.stream() + .map(expectation -> expectationConverter(team, executableInject, expectation))) + .collect(Collectors.toList()); + } - injectExpectationsByTeam.addAll(injectExpectationsByUserAndTeam); - this.injectExpectationRepository.saveAll(injectExpectationsByTeam); - } else if (!assets.isEmpty() || !assetGroups.isEmpty()) { - List injectExpectations = expectations.stream() - .map(expectation -> expectationConverter(executableInject, expectation)) - .toList(); - this.injectExpectationRepository.saveAll(injectExpectations); - } - } - } catch (Exception e) { - execution.addTrace(traceError(e.getMessage())); - } finally { - execution.stop(); + injectExpectationsByTeam.addAll(injectExpectationsByUserAndTeam); + this.injectExpectationRepository.saveAll(injectExpectationsByTeam); + } else if (!assets.isEmpty() || !assetGroups.isEmpty()) { + List injectExpectations = expectations.stream() + .map(expectation -> expectationConverter(executableInject, expectation)) + .toList(); + this.injectExpectationRepository.saveAll(injectExpectations); } - return execution; + } + } catch (Exception e) { + execution.addTrace(traceError(e.getMessage())); + } finally { + execution.stop(); } + return execution; + } - public Execution executeInjection(ExecutableInject executableInject) { - return execute(executableInject); - } + public Execution executeInjection(ExecutableInject executableInject) { + return execute(executableInject); + } - // region utils - private boolean isInInjectableRange(Injection injection) { - Instant now = Instant.now(); - Instant start = now.minus(Duration.parse("PT1H")); - Instant injectWhen = injection.getDate().orElseThrow(); - return injectWhen.isAfter(start) && injectWhen.isBefore(now); - } + // region utils + private boolean isInInjectableRange(Injection injection) { + Instant now = Instant.now(); + Instant start = now.minus(Duration.parse("PT1H")); + Instant injectWhen = injection.getDate().orElseThrow(); + return injectWhen.isAfter(start) && injectWhen.isBefore(now); + } - public T contentConvert(@NotNull final ExecutableInject injection, @NotNull final Class converter) throws Exception { - Inject inject = injection.getInjection().getInject(); - ObjectNode content = inject.getContent(); - return this.mapper.treeToValue(content, converter); - } + public T contentConvert(@NotNull final ExecutableInject injection, @NotNull final Class converter) + throws Exception { + Inject inject = injection.getInjection().getInject(); + ObjectNode content = inject.getContent(); + return this.mapper.treeToValue(content, converter); + } - public List resolveAttachments(Execution execution, ExecutableInject injection, List documents) { - List resolved = new ArrayList<>(); - // Add attachments from direct configuration - injection.getDirectAttachments().forEach(doc -> { - try { - byte[] content = IOUtils.toByteArray(doc.getInputStream()); - resolved.add(new DataAttachment(doc.getName(), doc.getOriginalFilename(), content, doc.getContentType())); - } catch (Exception e) { - String message = "Error getting direct attachment " + doc.getName(); - execution.addTrace(traceError(message)); - } - }); - // Add attachments from configuration - documents.forEach(attachment -> { - String documentId = attachment.getId(); - Optional askedDocument = documentRepository.findById(documentId); - try { - Document doc = askedDocument.orElseThrow(); - InputStream fileInputStream = fileService.getFile(doc).orElseThrow(); - byte[] content = IOUtils.toByteArray(fileInputStream); - resolved.add(new DataAttachment(documentId, doc.getName(), content, doc.getType())); - } catch (Exception e) { - // Can't fetch the attachments, ignore - String docInfo = askedDocument.map(Document::getName).orElse(documentId); - String message = "Error getting doc attachment " + docInfo; - execution.addTrace(traceError(message)); - } - }); - return resolved; - } - // endregion + public List resolveAttachments(Execution execution, ExecutableInject injection, + List documents) { + List resolved = new ArrayList<>(); + // Add attachments from direct configuration + injection.getDirectAttachments().forEach(doc -> { + try { + byte[] content = IOUtils.toByteArray(doc.getInputStream()); + resolved.add(new DataAttachment(doc.getName(), doc.getOriginalFilename(), content, doc.getContentType())); + } catch (Exception e) { + String message = "Error getting direct attachment " + doc.getName(); + execution.addTrace(traceError(message)); + } + }); + // Add attachments from configuration + documents.forEach(attachment -> { + String documentId = attachment.getId(); + Optional askedDocument = documentRepository.findById(documentId); + try { + Document doc = askedDocument.orElseThrow(); + InputStream fileInputStream = fileService.getFile(doc).orElseThrow(); + byte[] content = IOUtils.toByteArray(fileInputStream); + resolved.add(new DataAttachment(documentId, doc.getName(), content, doc.getType())); + } catch (Exception e) { + // Can't fetch the attachments, ignore + String docInfo = askedDocument.map(Document::getName).orElse(documentId); + String message = "Error getting doc attachment " + docInfo; + execution.addTrace(traceError(message)); + } + }); + return resolved; + } + // endregion } diff --git a/openbas-framework/src/main/java/io/openbas/model/Expectation.java b/openbas-framework/src/main/java/io/openbas/model/Expectation.java index 326a8e29fe..770c3ee470 100644 --- a/openbas-framework/src/main/java/io/openbas/model/Expectation.java +++ b/openbas-framework/src/main/java/io/openbas/model/Expectation.java @@ -4,10 +4,15 @@ public interface Expectation { - EXPECTATION_TYPE type(); - Double getScore(); - default boolean isExpectationGroup() { - return false; - } - String getName(); + EXPECTATION_TYPE type(); + + Double getScore(); + + default boolean isExpectationGroup() { + return false; + } + + String getName(); + + Long getExpirationTime(); } diff --git a/openbas-framework/src/main/java/io/openbas/model/expectation/ChallengeExpectation.java b/openbas-framework/src/main/java/io/openbas/model/expectation/ChallengeExpectation.java index 9024e25176..df7b275c36 100644 --- a/openbas-framework/src/main/java/io/openbas/model/expectation/ChallengeExpectation.java +++ b/openbas-framework/src/main/java/io/openbas/model/expectation/ChallengeExpectation.java @@ -16,17 +16,14 @@ public class ChallengeExpectation implements Expectation { private Challenge challenge; private boolean expectationGroup; private String name; + private Long expirationTime; - public ChallengeExpectation(Double score, Challenge challenge) { - setScore(Objects.requireNonNullElse(score, 100.0)); - setChallenge(challenge); - } - - public ChallengeExpectation(Double score, Challenge challenge, boolean expectationGroup) { - setScore(Objects.requireNonNullElse(score, 100.0)); + public ChallengeExpectation(io.openbas.model.inject.form.Expectation expectation, Challenge challenge) { + setScore(Objects.requireNonNullElse(expectation.getScore(), 100.0)); setChallenge(challenge); setName(challenge.getName()); - setExpectationGroup(expectationGroup); + setExpectationGroup(expectation.isExpectationGroup()); + setExpirationTime(expectation.getExpirationTime()); } @Override diff --git a/openbas-framework/src/main/java/io/openbas/model/expectation/ChannelExpectation.java b/openbas-framework/src/main/java/io/openbas/model/expectation/ChannelExpectation.java index 120df00a78..8f7776b7be 100644 --- a/openbas-framework/src/main/java/io/openbas/model/expectation/ChannelExpectation.java +++ b/openbas-framework/src/main/java/io/openbas/model/expectation/ChannelExpectation.java @@ -16,17 +16,20 @@ public class ChannelExpectation implements Expectation { private Article article; private boolean expectationGroup; private String name; + private Long expirationTime; public ChannelExpectation(Double score, Article article) { setScore(Objects.requireNonNullElse(score, 100.0)); setArticle(article); } - public ChannelExpectation(Double score, Article article, boolean expectationGroup) { + + public ChannelExpectation(io.openbas.model.inject.form.Expectation expectation, Article article) { setScore(Objects.requireNonNullElse(score, 100.0)); setArticle(article); setName(article.getName()); - setExpectationGroup(expectationGroup); + setExpectationGroup(expectation.isExpectationGroup()); + setExpirationTime(expectation.getExpirationTime()); } @Override diff --git a/openbas-framework/src/main/java/io/openbas/model/expectation/DetectionExpectation.java b/openbas-framework/src/main/java/io/openbas/model/expectation/DetectionExpectation.java index a6cb815273..98ad040547 100644 --- a/openbas-framework/src/main/java/io/openbas/model/expectation/DetectionExpectation.java +++ b/openbas-framework/src/main/java/io/openbas/model/expectation/DetectionExpectation.java @@ -20,56 +20,61 @@ @Setter public class DetectionExpectation implements Expectation { - private Double score; - private String name; - private String description; - private Asset asset; - private AssetGroup assetGroup; - private boolean expectationGroup; - private List injectExpectationSignatures; + private Double score; + private String name; + private String description; + private Asset asset; + private AssetGroup assetGroup; + private boolean expectationGroup; + private Long expirationTime; + private List injectExpectationSignatures; - private DetectionExpectation() { - } + private DetectionExpectation() { + } - @Override - public InjectExpectation.EXPECTATION_TYPE type() { - return DETECTION; - } + @Override + public InjectExpectation.EXPECTATION_TYPE type() { + return DETECTION; + } - public static DetectionExpectation detectionExpectationForAsset( - @Nullable final Double score, - @NotBlank final String name, - final String description, - @NotNull final Asset asset, - final boolean expectationGroup, - final List expectationSignatures - ) { - DetectionExpectation detectionExpectation = new DetectionExpectation(); - detectionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); - detectionExpectation.setName(name); - detectionExpectation.setDescription(description); - detectionExpectation.setAsset(asset); - detectionExpectation.setExpectationGroup(expectationGroup); - detectionExpectation.setInjectExpectationSignatures(expectationSignatures); - return detectionExpectation; - } + public static DetectionExpectation detectionExpectationForAsset( + @Nullable final Double score, + @NotBlank final String name, + final String description, + @NotNull final Asset asset, + final boolean expectationGroup, + final Long expirationTime, + final List expectationSignatures + ) { + DetectionExpectation detectionExpectation = new DetectionExpectation(); + detectionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); + detectionExpectation.setName(name); + detectionExpectation.setDescription(description); + detectionExpectation.setAsset(asset); + detectionExpectation.setExpectationGroup(expectationGroup); + detectionExpectation.setExpirationTime(expirationTime); + detectionExpectation.setInjectExpectationSignatures(expectationSignatures); + return detectionExpectation; + } - public static DetectionExpectation detectionExpectationForAssetGroup( - @Nullable final Double score, - @NotBlank final String name, - final String description, - @NotNull final AssetGroup assetGroup, - final boolean expectationGroup, - final List expectationSignatures - ) { - DetectionExpectation detectionExpectation = new DetectionExpectation(); - detectionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); - detectionExpectation.setName(name); - detectionExpectation.setDescription(description); - detectionExpectation.setAssetGroup(assetGroup); - detectionExpectation.setExpectationGroup(expectationGroup); - detectionExpectation.setInjectExpectationSignatures(expectationSignatures); - return detectionExpectation; - } + public static DetectionExpectation detectionExpectationForAssetGroup( + @Nullable final Double score, + @NotBlank final String name, + final String description, + @NotNull final AssetGroup assetGroup, + final boolean expectationGroup, + final Long expirationTime, + final List expectationSignatures + ) { + DetectionExpectation detectionExpectation = new DetectionExpectation(); + detectionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); + detectionExpectation.setName(name); + detectionExpectation.setDescription(description); + detectionExpectation.setAssetGroup(assetGroup); + detectionExpectation.setExpectationGroup(expectationGroup); + detectionExpectation.setExpirationTime(expirationTime); + detectionExpectation.setInjectExpectationSignatures(expectationSignatures); + return detectionExpectation; + } } diff --git a/openbas-framework/src/main/java/io/openbas/model/expectation/ManualExpectation.java b/openbas-framework/src/main/java/io/openbas/model/expectation/ManualExpectation.java index 1cc631c45a..23f75c9130 100644 --- a/openbas-framework/src/main/java/io/openbas/model/expectation/ManualExpectation.java +++ b/openbas-framework/src/main/java/io/openbas/model/expectation/ManualExpectation.java @@ -25,6 +25,7 @@ public class ManualExpectation implements Expectation { private Asset asset; private AssetGroup assetGroup; private boolean expectationGroup; + private Long expirationTime; public ManualExpectation() { } @@ -33,47 +34,46 @@ public ManualExpectation(final Double score) { this.score = Objects.requireNonNullElse(score, 100.0); } - public ManualExpectation(final Double score, @NotBlank final String name, final String description) { - this(score); - this.name = name; - this.description = description; - } - - public ManualExpectation(final Double score, @NotBlank final String name, final String description, final boolean expectationGroup) { - this(score); - this.name = name; - this.description = description; - this.expectationGroup = expectationGroup; + public ManualExpectation(io.openbas.model.inject.form.Expectation expectation) { + this(expectation.getScore()); + this.name = expectation.getName(); + this.description = expectation.getDescription(); + this.expectationGroup = expectation.isExpectationGroup(); + this.expirationTime = expectation.getExpirationTime(); } public static ManualExpectation manualExpectationForAsset( - @Nullable final Double score, - @NotBlank final String name, - final String description, - @NotNull final Asset asset, - final boolean expectationGroup + @Nullable final Double score, + @NotBlank final String name, + final String description, + @NotNull final Asset asset, + final Long expirationTime, + final boolean expectationGroup ) { ManualExpectation manualExpectation = new ManualExpectation(); manualExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); manualExpectation.setName(name); manualExpectation.setDescription(description); manualExpectation.setAsset(asset); + manualExpectation.setExpirationTime(expirationTime); manualExpectation.setExpectationGroup(expectationGroup); return manualExpectation; } public static ManualExpectation manualExpectationForAssetGroup( - @Nullable final Double score, - @NotBlank final String name, - final String description, - @NotNull final AssetGroup assetGroup, - final boolean expectationGroup + @Nullable final Double score, + @NotBlank final String name, + final String description, + @NotNull final AssetGroup assetGroup, + final Long expirationTime, + final boolean expectationGroup ) { ManualExpectation manualExpectation = new ManualExpectation(); manualExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); manualExpectation.setName(name); manualExpectation.setDescription(description); manualExpectation.setAssetGroup(assetGroup); + manualExpectation.setExpirationTime(expirationTime); manualExpectation.setExpectationGroup(expectationGroup); return manualExpectation; } diff --git a/openbas-framework/src/main/java/io/openbas/model/expectation/PreventionExpectation.java b/openbas-framework/src/main/java/io/openbas/model/expectation/PreventionExpectation.java index 355ca70a3d..13d75df542 100644 --- a/openbas-framework/src/main/java/io/openbas/model/expectation/PreventionExpectation.java +++ b/openbas-framework/src/main/java/io/openbas/model/expectation/PreventionExpectation.java @@ -20,56 +20,61 @@ @Setter public class PreventionExpectation implements Expectation { - private Double score; - private String name; - private String description; - private Asset asset; - private AssetGroup assetGroup; - private boolean expectationGroup; - private List injectExpectationSignatures; + private Double score; + private String name; + private String description; + private Asset asset; + private AssetGroup assetGroup; + private boolean expectationGroup; + private Long expirationTime; + private List injectExpectationSignatures; - private PreventionExpectation() { - } + private PreventionExpectation() { + } - @Override - public EXPECTATION_TYPE type() { - return PREVENTION; - } + @Override + public EXPECTATION_TYPE type() { + return PREVENTION; + } - public static PreventionExpectation preventionExpectationForAsset( - @Nullable final Double score, - @NotBlank final String name, - final String description, - @NotNull final Asset asset, - final boolean expectationGroup, - final List expectationSignatures - ) { - PreventionExpectation preventionExpectation = new PreventionExpectation(); - preventionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); - preventionExpectation.setName(name); - preventionExpectation.setDescription(description); - preventionExpectation.setAsset(asset); - preventionExpectation.setExpectationGroup(expectationGroup); - preventionExpectation.setInjectExpectationSignatures(expectationSignatures); - return preventionExpectation; - } + public static PreventionExpectation preventionExpectationForAsset( + @Nullable final Double score, + @NotBlank final String name, + final String description, + @NotNull final Asset asset, + final boolean expectationGroup, + final Long expirationTime, + final List expectationSignatures + ) { + PreventionExpectation preventionExpectation = new PreventionExpectation(); + preventionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); + preventionExpectation.setName(name); + preventionExpectation.setDescription(description); + preventionExpectation.setAsset(asset); + preventionExpectation.setExpectationGroup(expectationGroup); + preventionExpectation.setExpirationTime(expirationTime); + preventionExpectation.setInjectExpectationSignatures(expectationSignatures); + return preventionExpectation; + } - public static PreventionExpectation preventionExpectationForAssetGroup( - @Nullable final Double score, - @NotBlank final String name, - final String description, - @NotNull final AssetGroup assetGroup, - final boolean expectationGroup, - final List expectationSignatures - ) { - PreventionExpectation preventionExpectation = new PreventionExpectation(); - preventionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); - preventionExpectation.setName(name); - preventionExpectation.setDescription(description); - preventionExpectation.setAssetGroup(assetGroup); - preventionExpectation.setExpectationGroup(expectationGroup); - preventionExpectation.setInjectExpectationSignatures(expectationSignatures); - return preventionExpectation; - } + public static PreventionExpectation preventionExpectationForAssetGroup( + @Nullable final Double score, + @NotBlank final String name, + final String description, + @NotNull final AssetGroup assetGroup, + final boolean expectationGroup, + final Long expirationTime, + final List expectationSignatures + ) { + PreventionExpectation preventionExpectation = new PreventionExpectation(); + preventionExpectation.setScore(Objects.requireNonNullElse(score, 100.0)); + preventionExpectation.setName(name); + preventionExpectation.setDescription(description); + preventionExpectation.setAssetGroup(assetGroup); + preventionExpectation.setExpectationGroup(expectationGroup); + preventionExpectation.setExpirationTime(expirationTime); + preventionExpectation.setInjectExpectationSignatures(expectationSignatures); + return preventionExpectation; + } } diff --git a/openbas-framework/src/main/java/io/openbas/model/inject/form/Expectation.java b/openbas-framework/src/main/java/io/openbas/model/inject/form/Expectation.java index ce1801671b..39f77bfdf4 100644 --- a/openbas-framework/src/main/java/io/openbas/model/inject/form/Expectation.java +++ b/openbas-framework/src/main/java/io/openbas/model/inject/form/Expectation.java @@ -22,4 +22,7 @@ public class Expectation { @JsonProperty("expectation_expectation_group") private boolean expectationGroup; + @JsonProperty("expectation_expiration_time") + private Long expirationTime; + } diff --git a/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx b/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx index 0a09be2f2b..e9c2f6b5c9 100644 --- a/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx +++ b/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx @@ -44,6 +44,7 @@ import { useAppDispatch } from '../../../../utils/hooks'; import type { InjectExpectationStore } from '../../../../actions/injects/Inject'; import { NodeResultStep } from './types/nodes/NodeResultStep'; import { isTechnicalExpectation } from '../../common/injects/expectations/ExpectationUtils'; +import { splitDuration } from '../../../../utils/Time'; interface Steptarget { label: string; @@ -86,6 +87,18 @@ const useStyles = makeStyles((theme) => ({ height: '20px', padding: '0 4px', }, + duration: { + fontSize: 12, + lineHeight: '12px', + height: 20, + float: 'left', + marginRight: 7, + borderRadius: 4, + width: 180, + backgroundColor: 'rgba(0, 177, 255, 0.08)', + color: '#00b1ff', + border: '1px solid #00b1ff', + }, })); interface Props { @@ -527,49 +540,68 @@ const TargetResultsDetailFlow: FunctionComponent = ({ {t('Results')} - {injectExpectation.inject_expectation_results && injectExpectation.inject_expectation_results.map((expectationResult, index) => ( - - - - { - ev.stopPropagation(); - setAnchorEls({ ...anchorEls, [`${injectExpectation.inject_expectation_id}-${expectationResult.sourceId}`]: ev.currentTarget }); - }} - aria-haspopup="true" - size="large" - disabled={['collector', 'media-pressure', 'challenge'].includes(expectationResult.sourceType ?? 'unknown')} - > - - - setAnchorEls({ ...anchorEls, [`${injectExpectation.inject_expectation_id}-${expectationResult.sourceId}`]: null })} - > - handleOpenResultEdition(injectExpectation, expectationResult)}> - {t('Update')} - - handleOpenResultDeletion(injectExpectation, expectationResult)}> - {t('Delete')} - - - - } - title={expectationResult.sourceName ? t(expectationResult.sourceName) : t('Unknown')} - subheader={nsdt(expectationResult.date)} - /> - - - - - - - ))} + {injectExpectation.inject_expectation_results && injectExpectation.inject_expectation_results.map((expectationResult, index) => { + const duration = splitDuration(injectExpectation.inject_expiration_time || 0); + return ( + + + + { + ev.stopPropagation(); + setAnchorEls({ ...anchorEls, [`${injectExpectation.inject_expectation_id}-${expectationResult.sourceId}`]: ev.currentTarget }); + }} + aria-haspopup="true" + size="large" + disabled={['collector', 'media-pressure', 'challenge'].includes(expectationResult.sourceType ?? 'unknown')} + > + + + setAnchorEls({ ...anchorEls, [`${injectExpectation.inject_expectation_id}-${expectationResult.sourceId}`]: null })} + > + handleOpenResultEdition(injectExpectation, expectationResult)}> + {t('Update')} + + handleOpenResultDeletion(injectExpectation, expectationResult)}> + {t('Delete')} + + + + } + title={expectationResult.sourceName ? t(expectationResult.sourceName) : t('Unknown')} + subheader={ + <> +
{nsdt(expectationResult.date)}
+
+ {t('Expired after')} + +
+ + + + } + /> + + + + +
+
+ ); + })} {(['DETECTION', 'PREVENTION'].includes(injectExpectation.inject_expectation_type) || (injectExpectation.inject_expectation_type === 'MANUAL' && injectExpectation.inject_expectation_results && injectExpectation.inject_expectation_results.length === 0)) && ( diff --git a/openbas-front/src/admin/components/common/injects/expectations/ExpectationPopover.tsx b/openbas-front/src/admin/components/common/injects/expectations/ExpectationPopover.tsx index 057854417c..bf905b90f3 100644 --- a/openbas-front/src/admin/components/common/injects/expectations/ExpectationPopover.tsx +++ b/openbas-front/src/admin/components/common/injects/expectations/ExpectationPopover.tsx @@ -35,7 +35,7 @@ const ExpectationPopover: FunctionComponent = ({ expectation_description: expectation.expectation_description ?? '', expectation_score: expectation.expectation_score ?? 100, expectation_expectation_group: expectation.expectation_expectation_group ?? false, - expectation_expiration_time: expectation.expectation_expiration_time ?? 21600, + expectation_expiration_time: expectation.expectation_expiration_time || ((expectation.expectation_type === 'DETECTION' || expectation.expectation_type === 'PREVENTION') ? 21600 : 3600), }; // Popover diff --git a/openbas-front/src/utils/Localization.js b/openbas-front/src/utils/Localization.js index d9d2d2faa4..348723316b 100644 --- a/openbas-front/src/utils/Localization.js +++ b/openbas-front/src/utils/Localization.js @@ -462,6 +462,7 @@ const i18n = { Running: 'En cours', Failure: 'Echec', Expired: 'Expiré', + 'Expired after': 'Expiré après', Anonymized: 'Anonymisé', 'No media pressure entry in this channel yet.': 'Encore aucune entrée de pression médiatique dans ce média.', @@ -1823,6 +1824,7 @@ const i18n = { Running: '运行中', Failure: '失败', Expired: '过期', + 'Expired after': '过期时间', Anonymized: '匿名', 'No media pressure entry in this channel yet.': '这个频道中还没有媒体项.', diff --git a/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java b/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java index d80297a5f7..04f7c7ae25 100644 --- a/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java +++ b/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java @@ -121,7 +121,7 @@ public EXPECTATION_STATUS getResponse() { @Column(name = "inject_expiration_time") @JsonProperty("inject_expiration_time") @NotNull - private Long expirationTime = 60L; + private Long expirationTime; @Setter @Column(name = "inject_expectation_created_at")