diff --git a/.gcp.env b/.gcp.env index 62781601..0218842b 100644 --- a/.gcp.env +++ b/.gcp.env @@ -1,4 +1,4 @@ -SENTRIUS_VERSION=1.0.34 -SENTRIUS_SSH_VERSION=1.0.3 -SENTRIUS_KEYCLOAK_VERSION=1.0.4 -SENTRIUS_AGENT_VERSION=1.0.15 \ No newline at end of file +SENTRIUS_VERSION=1.0.37 +SENTRIUS_SSH_VERSION=1.0.4 +SENTRIUS_KEYCLOAK_VERSION=1.0.6 +SENTRIUS_AGENT_VERSION=1.0.16 \ No newline at end of file diff --git a/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java b/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java index eb8f0a89..96645d8f 100644 --- a/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java +++ b/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java @@ -1,40 +1,35 @@ package io.sentrius.agent.analysis.agents.sessions; +import java.nio.charset.StandardCharsets; import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Base64; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.sentrius.sso.core.model.categorization.CommandCategory; import io.sentrius.sso.core.model.metadata.AnalyticsTracking; import io.sentrius.sso.core.model.metadata.TerminalBehaviorMetrics; import io.sentrius.sso.core.model.metadata.TerminalCommand; import io.sentrius.sso.core.model.metadata.TerminalRiskIndicator; import io.sentrius.sso.core.model.metadata.TerminalSessionMetadata; import io.sentrius.sso.core.model.metadata.UserExperienceMetrics; -import io.sentrius.sso.core.model.security.IntegrationSecurityToken; import io.sentrius.sso.core.model.sessions.TerminalLogs; import io.sentrius.sso.core.repository.AnalyticsTrackingRepository; import io.sentrius.sso.core.services.IntegrationSecurityTokenService; -import io.sentrius.sso.core.services.PluggableServices; import io.sentrius.sso.core.services.SessionService; import io.sentrius.sso.core.services.metadata.TerminalBehaviorMetricsService; import io.sentrius.sso.core.services.metadata.TerminalCommandService; import io.sentrius.sso.core.services.metadata.TerminalRiskIndicatorService; import io.sentrius.sso.core.services.metadata.TerminalSessionMetadataService; import io.sentrius.sso.core.services.metadata.UserExperienceMetricsService; -import io.sentrius.sso.core.utils.JsonUtil; -import io.sentrius.sso.integrations.external.ExternalIntegrationDTO; -import io.sentrius.sso.security.ApiKey; +import io.sentrius.sso.core.services.openai.categorization.CommandCategorizer; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.ApplicationContext; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -51,6 +46,7 @@ public class SessionAnalyticsAgent { private final UserExperienceMetricsService experienceMetricsService; private final AnalyticsTrackingRepository trackingRepository; private final SessionService sessionService; + private final CommandCategorizer commandCategorizer; final IntegrationSecurityTokenService integrationSecurityTokenService; @@ -61,6 +57,7 @@ public void processSessions() { // Fetch already processed session IDs in bulk Set processedSessionIds = trackingRepository.findAllSessionIds(); + log.info("Found {} processed sessions", processedSessionIds.size()); List unprocessedSessions = sessionMetadataService.getSessionsByState("CLOSED").stream() .filter(session -> !processedSessionIds.contains(session.getId())) .collect(Collectors.toList()); @@ -69,13 +66,13 @@ public void processSessions() { try { processSession(session); // ACTIVE -> INACTIVE -> CLOSED -> PROCESSED - // saveToTracking(session.getId(), "PROCESSED"); + saveToTracking(session.getId(), "PROCESSED"); } catch (Exception e) { log.error("Error processing session {}: {}", session.getId(), e.getMessage(), e); saveToTracking(session.getId(), "ERROR"); } - // session.setSessionStatus("PROCESSED"); - // sessionMetadataService.saveSession(session); + session.setSessionStatus("PROCESSED"); + sessionMetadataService.saveSession(session); } log.info("Finished processing sessions"); @@ -185,7 +182,6 @@ public static String extractCommand(TerminalLogs previousLog, String logLine) { return matcher.group(1).trim(); } else { if (null != previousLog) { - log.info("Previous log: {}", previousLog.getOutput()); // it could be that we are at the beginning of the log set. String lastLogLine = getLastLogLine(previousLog); if (!lastLogLine.isEmpty()) { @@ -215,26 +211,20 @@ private static String getLastLogLine(TerminalLogs previousLog) { } private TerminalCommand createTerminalCommand(String command, TerminalLogs terminalLog, TerminalSessionMetadata sessionMetadata) { + String encodedString = Base64.getEncoder().encodeToString(command.trim().getBytes(StandardCharsets.UTF_8)); + TerminalCommand terminalCommand = new TerminalCommand(); - terminalCommand.setCommand(command.trim()); + terminalCommand.setCommand(encodedString); terminalCommand.setSession(sessionMetadata); terminalCommand.setExecutionTime(new Timestamp(System.currentTimeMillis())); terminalCommand.setExecutionStatus("SUCCESS"); terminalCommand.setOutput(""); // Assume no output initially - terminalCommand.setCommandCategory(categorizeCommand(command)); + terminalCommand.setCommandCategory(categorizeCommand(command).getCategoryName()); return terminalCommand; } - private String categorizeCommand(String command) { - // probably need to define externally - if (command.startsWith("sudo")) { - return "PRIVILEGED"; - } else if (command.contains("rm")) { - return "DESTRUCTIVE"; - } else if (command.contains("ls") || command.contains("cat")) { - return "INFORMATIONAL"; - } - return "GENERAL"; + private CommandCategory categorizeCommand(String command) { + return commandCategorizer.categorizeCommand(command); } } diff --git a/analyagents/src/main/resources/application.properties b/analyagents/src/main/resources/application.properties index af5a66b9..648a6a7c 100644 --- a/analyagents/src/main/resources/application.properties +++ b/analyagents/src/main/resources/application.properties @@ -61,4 +61,5 @@ spring.security.oauth2.client.provider.keycloak.issuer-uri=http://192.168.1.162: # for testing analytics agents agents.session-analytics.enabled=true management.endpoints.web.exposure.include=health -management.endpoint.health.show-details=always \ No newline at end of file +management.endpoint.health.show-details=always + diff --git a/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java b/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java index 86b0940e..eaca42be 100644 --- a/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java +++ b/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java @@ -276,7 +276,7 @@ protected List createHostGroups(List sideEffects, Map createHostGroups(List sideEffects, Map createHostGroups(List sideEffects, Map createHostGroups(List sideEffects, Map createUsers( sideEffects.add(SideEffect.builder().sideEffectDescription( - "Assigning user " + userDTO.getUsername() + " to Host Group " + + "Assigning user " + userDTO.getUsername() + " to Host Enclave " + hostGroup.getName()).type( SideEffectType.UPDATE_DATABASE).asset("Users").build()); - log.info("Assigning user {} to Host Group {}", userDTO.getUsername(), + log.info("Assigning user {} to Host Enclave {}", userDTO.getUsername(), hostGroup.getId()); user.getHostGroups().add(hostGroup); } @@ -473,15 +473,15 @@ protected List createUsers( for (var profile : userDTO.getHostGroups()) { for (HostGroup hostGroup : profiles) { if (hostGroup.getName().equals(profile.getDisplayName())) { - log.info("Assigning user {} to Host Group {}", userDTO.getUsername(), + log.info("Assigning user {} to Host Enclave {}", userDTO.getUsername(), hostGroup.getId()); if (null == hostGroup.getId() || !userRepository.isAssignedToHostGroups(user.getId(), List.of(hostGroup.getId()))) { sideEffects.add(SideEffect.builder().sideEffectDescription( - "Assigning user " + userDTO.getUsername() + " to Host Group " + + "Assigning user " + userDTO.getUsername() + " to Host Enclave " + hostGroup.getName()).type( SideEffectType.UPDATE_DATABASE).asset("Users").build()); - log.info("Assigning user {} to Host Group {}", userDTO.getUsername(), + log.info("Assigning user {} to Host Enclave {}", userDTO.getUsername(), hostGroup.getId() ); } diff --git a/api/src/main/resources/db/migration/V13__command_categorizer.sql b/api/src/main/resources/db/migration/V13__command_categorizer.sql new file mode 100644 index 00000000..2c4418a0 --- /dev/null +++ b/api/src/main/resources/db/migration/V13__command_categorizer.sql @@ -0,0 +1,9 @@ +CREATE TABLE command_categories ( + id SERIAL PRIMARY KEY, + category_name VARCHAR(50) NOT NULL, + pattern TEXT NOT NULL, -- Store regex patterns + priority INT NOT NULL DEFAULT 0 -- Optional: for matching precedence +); + + +CREATE INDEX idx_pattern ON command_categories (pattern); \ No newline at end of file diff --git a/api/src/main/resources/db/migration/V14__command_categorizer_gin.sql b/api/src/main/resources/db/migration/V14__command_categorizer_gin.sql new file mode 100644 index 00000000..8d0a7b45 --- /dev/null +++ b/api/src/main/resources/db/migration/V14__command_categorizer_gin.sql @@ -0,0 +1,3 @@ +CREATE EXTENSION IF NOT EXISTS pg_trgm; + +CREATE INDEX idx_command_pattern_trgm ON command_categories USING gin (pattern gin_trgm_ops); diff --git a/api/src/main/resources/static/js/rules.js b/api/src/main/resources/static/js/rules.js index 752343dd..5a325512 100644 --- a/api/src/main/resources/static/js/rules.js +++ b/api/src/main/resources/static/js/rules.js @@ -110,7 +110,7 @@ $(document).ready(function () { if (row.canEdit) { buttons += - ` + ` `; } @@ -165,7 +165,7 @@ $(document).ready(function () { if (response.ok) { $('#rule-table').DataTable().ajax.reload(null, false); } else { - alert("Failed to assign host groups."); + alert("Failed to assign host enclaves."); } const modal = bootstrap.Modal.getInstance(document.getElementById("assignHostGroupsModal")); modal.hide(); diff --git a/api/src/main/resources/templates/fragments/assign_rule.html b/api/src/main/resources/templates/fragments/assign_rule.html index 6710917e..ce8dde8d 100644 --- a/api/src/main/resources/templates/fragments/assign_rule.html +++ b/api/src/main/resources/templates/fragments/assign_rule.html @@ -1,10 +1,10 @@ - +
-

Host Groups

+

Host Enclaves

Assigned Systems: diff --git a/api/src/main/resources/templates/fragments/topbar.html b/api/src/main/resources/templates/fragments/topbar.html index 2747269d..a8f5e30b 100755 --- a/api/src/main/resources/templates/fragments/topbar.html +++ b/api/src/main/resources/templates/fragments/topbar.html @@ -23,7 +23,7 @@