10-DB_Security_Risks.r
# -----------------------------------------------------------------------------------# File Name : 10-DB_Security_Risks.r# Author : Pierre Montbleau# Description : Advanced security risk analysis for Oracle databases using anomaly # detection, behavioral clustering, and pattern analysis to identify # potentially malicious user activities and security vulnerabilities# Purpose : Proactive security monitoring, risk assessment, and threat detection# Call Syntax : source("F:\\DBA\\Scripts\\R\\10-DB_Security_Risks.r")# Parameters : None - all database connection parameters are hardcoded within script# Last Modified: 2025-12-17# -----------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------# Load Required Libraries# -----------------------------------------------------------------------------------library(DBI) # Database Interfacelibrary(odbc) # ODBC Database Connectivitylibrary(dplyr) # Data Manipulationlibrary(lubridate) # Date-Time Processinglibrary(ggplot2) # Data Visualizationlibrary(anomalize) # Anomaly Detectionlibrary(cluster) # Clustering Algorithmslibrary(tidyr) # Data Reshapinglibrary(config) # Configuration Management
# -----------------------------------------------------------------------------------# Function: safe_db_connect# Purpose : Secure database connection with error handling# Returns : Connection object on success, NULL on failure# Security: Implements secure connection practices with proper error handling# -----------------------------------------------------------------------------------safe_db_connect <- function() { tryCatch({ conn <- dbConnect(odbc::odbc(), dsn = "[DB_NAME]", uid = "[USERNAME]", pwd = "[PASSWORD]") message("Successfully connected to database") return(conn) }, error = function(e) { message("Connection failed: ", e$message) return(NULL) })}
# -----------------------------------------------------------------------------------# Function: analyze_security_risks# Purpose : Main analysis function that orchestrates security risk assessment# Process : 1. Connects to database# 2. Retrieves multiple security-related datasets# 3. Performs anomaly detection on login patterns# 4. Clusters users by behavioral patterns# 5. Identifies potentially risky users# 6. Generates security recommendations# -----------------------------------------------------------------------------------analyze_security_risks <- function() { # Establish secure database connection conn <- safe_db_connect() if (is.null(conn)) { stop("Failed to establish database connection. Analysis aborted.") } # Ensure connection is closed on exit on.exit({ dbDisconnect(conn) message("Database connection closed") }) # Main analysis block with error handling tryCatch({ # ------------------------------------------------------------------------------- # Data Collection Phase # Retrieves security-related data from multiple audit tables # ------------------------------------------------------------------------------- # Retrieve user activity data (last 90 days) user_activity <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, LOGIN_TIME, CLIENT_IP, CLIENT_HOSTNAME, OS_USER, ACTION, CLIENT_PROGRAM_NAME FROM ATTSTATS.USER_ACTIVITY_LOG WHERE TRUNC(LOGIN_TIME) > TRUNC(SYSDATE-90)") }, error = function(e) { warning("Failed to retrieve user activity data: ", e$message) return(NULL) }) # Retrieve audit activity data audit_activity <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, ACTION_NAME, TIMESTAMP FROM ATTSTATS.RECENT_AUDIT_ACTIVITY") }, error = function(e) { warning("Failed to retrieve audit activity data: ", e$message) return(NULL) }) # Retrieve user account information (last 90 days) user_accounts <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, ACCOUNT_STATUS, DEFAULT_TABLESPACE, PROFILE, LAST_LOGIN, ORACLE_MAINTAINED FROM ATTSTATS.USER_ACCOUNT_INFORMATION WHERE TRUNC(CHECK_DATE) > TRUNC(SYSDATE-90)") }, error = function(e) { warning("Failed to retrieve user account data: ", e$message) return(NULL) }) # Retrieve DDL (Data Definition Language) audit data ddl_audit <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, OPERATION, OBJECT_TYPE, OBJECT_NAME, OBJECT_OWNER FROM ATTSTATS.DDL_AUDIT_LOG") }, error = function(e) { warning("Failed to retrieve DDL audit data: ", e$message) return(NULL) }) # Validate minimum data requirements if (is.null(user_activity)) { stop("User activity data is required but not available. Analysis aborted.") } # ------------------------------------------------------------------------------- # Data Preprocessing Phase # Prepares user activity data for analysis # ------------------------------------------------------------------------------- activity_summary <- user_activity %>% mutate( LOGIN_DATETIME = as.POSIXct(LOGIN_TIME), # Convert to datetime LOGIN_DATE = as.Date(LOGIN_DATETIME), # Extract date component LOGIN_HOUR = hour(LOGIN_DATETIME) # Extract hour component ) %>% group_by(USERNAME, LOGIN_DATE) %>% summarise( DAILY_LOGINS = n(), # Count of daily logins UNIQUE_CLIENTS = n_distinct(CLIENT_IP), # Unique IP addresses UNIQUE_PROGRAMS = n_distinct(CLIENT_PROGRAM_NAME), # Unique client programs ACTIONS = paste(unique(ACTION), collapse = ", "), # Concatenated actions .groups = "drop" ) # ------------------------------------------------------------------------------- # Anomaly Detection Phase # Identifies unusual patterns in daily login activity # ------------------------------------------------------------------------------- login_anomalies <- activity_summary %>% as_tibble() %>% mutate(LOGIN_DATETIME = as.POSIXct(paste(LOGIN_DATE, "00:00:00"))) %>% time_decompose(DAILY_LOGINS, method = "stl", frequency = "1 week") %>% # Decompose time series anomalize(remainder, method = "gesd") %>% # Detect anomalies using GESD method time_recompose() # Reconstruct time series # ------------------------------------------------------------------------------- # Visualization: Anomaly Plot # Shows detected anomalies in login patterns # ------------------------------------------------------------------------------- anomaly_plot <- login_anomalies %>% plot_anomalies(time_recomposed = TRUE) + labs( title = "Anomalous Login Activity by User", subtitle = "Daily login patterns with detected anomalies (Red points indicate anomalies)", x = "Date", y = "Number of Logins" ) + theme_minimal() + theme(plot.title = element_text(face = "bold", size = 14), plot.subtitle = element_text(color = "darkgray")) print(anomaly_plot) # Extract suspicious users from anomaly detection suspicious_users <- login_anomalies %>% filter(anomaly == "Yes") %>% select(USERNAME, LOGIN_DATE, DAILY_LOGINS, recomposed_l1, recomposed_l2) # ------------------------------------------------------------------------------- # DDL Pattern Analysis # Analyzes structural changes to database objects # ------------------------------------------------------------------------------- ddl_patterns <- if (!is.null(ddl_audit)) { ddl_audit %>% group_by(USERNAME, OPERATION, OBJECT_TYPE) %>% summarise(COUNT = n(), .groups = "drop") %>% pivot_wider( names_from = c(OPERATION, OBJECT_TYPE), values_from = COUNT, values_fill = 0 ) } else { warning("DDL patterns analysis skipped due to missing data") tibble(USERNAME = character()) } # ------------------------------------------------------------------------------- # Behavioral Clustering Phase # Groups users by their activity patterns using k-means clustering # ------------------------------------------------------------------------------- behavior_data <- activity_summary %>% group_by(USERNAME) %>% summarise( AVG_LOGINS = mean(DAILY_LOGINS), # Average daily logins MAX_LOGINS = max(DAILY_LOGINS), # Maximum daily logins CLIENT_VARIETY = mean(UNIQUE_CLIENTS), # Average unique clients PROGRAM_VARIETY = mean(UNIQUE_PROGRAMS), # Average unique programs .groups = "drop" ) # Enrich behavior data with DDL patterns if available if (nrow(ddl_patterns) > 0) { behavior_data <- behavior_data %>% left_join(ddl_patterns, by = "USERNAME") } # Handle missing values (replace NA with 0) behavior_data <- behavior_data %>% replace(is.na(.), 0) # ------------------------------------------------------------------------------- # Clustering Analysis # Performs k-means clustering on normalized behavioral data # ------------------------------------------------------------------------------- if (nrow(behavior_data) > 1) { # Normalize data for clustering scaled_data <- scale(select(behavior_data, -USERNAME)) # Determine optimal number of clusters (maximum of 5 or n-1) set.seed(123) # For reproducibility max_clusters <- min(5, nrow(behavior_data) - 1) kmeans_result <- kmeans(scaled_data, centers = max_clusters) behavior_data$CLUSTER <- as.factor(kmeans_result$cluster) # Analyze cluster characteristics cluster_summary <- behavior_data %>% group_by(CLUSTER) %>% summarise(across(where(is.numeric), mean), .groups = "drop") %>% arrange(desc(AVG_LOGINS)) # Identify unusual clusters (potential security risks) # Clusters with high average logins or extremely high maximum logins unusual_cluster <- cluster_summary %>% filter( AVG_LOGINS > quantile(cluster_summary$AVG_LOGINS, 0.75) | MAX_LOGINS > quantile(cluster_summary$MAX_LOGINS, 0.9) ) %>% pull(CLUSTER) # Flag users in unusual clusters as potentially risky risky_users <- behavior_data %>% filter(CLUSTER %in% unusual_cluster) # Enrich risky users with account information if available if (!is.null(user_accounts)) { risky_users <- risky_users %>% left_join(user_accounts, by = "USERNAME") %>% select( USERNAME, AVG_LOGINS, MAX_LOGINS, CLIENT_VARIETY, PROGRAM_VARIETY, ACCOUNT_STATUS, PROFILE, ORACLE_MAINTAINED, CLUSTER ) } # ----------------------------------------------------------------------------- # Visualization: Cluster Plot # Shows user clusters based on behavioral patterns # ----------------------------------------------------------------------------- cluster_plot <- ggplot(behavior_data, aes(x = AVG_LOGINS, y = MAX_LOGINS, color = CLUSTER)) + geom_point(alpha = 0.7, size = 3) + labs( title = "User Behavior Clusters for Security Analysis", subtitle = "K-means clustering based on login patterns (Colors represent behavioral groups)", x = "Average Daily Logins", y = "Maximum Daily Logins", color = "Behavior Cluster" ) + theme_minimal() + scale_color_brewer(palette = "Set1") + theme(plot.title = element_text(face = "bold", size = 14), legend.position = "bottom") print(cluster_plot) # ----------------------------------------------------------------------------- # Generate Security Recommendations # Provides actionable insights based on analysis results # ----------------------------------------------------------------------------- generate_recommendations(risky_users) } else { warning("Insufficient data for clustering analysis") cat("No user behavior patterns detected due to insufficient data\n") } }, error = function(e) { message("Analysis failed: ", e$message) })}
# -----------------------------------------------------------------------------------# Function: generate_recommendations# Purpose : Generates actionable security recommendations based on identified risks# Input : risky_users - Data frame of potentially risky users with metrics# Output : Printed recommendations to console# -----------------------------------------------------------------------------------generate_recommendations <- function(risky_users) { if (nrow(risky_users) > 0) { cat("\n" + strrep("=", 60) + "\n") cat("POTENTIALLY RISKY USERS IDENTIFIED\n") cat(strrep("=", 60) + "\n\n") print(risky_users) cat("\n" + strrep("=", 60) + "\n") cat("SECURITY RECOMMENDATIONS\n") cat(strrep("=", 60) + "\n\n") cat("1. USER ACCOUNT REVIEW:\n") cat(" - Review activity patterns and permissions for:\n") cat(" ", paste(unique(risky_users$USERNAME), collapse = ", "), "\n\n") cat("2. ENHANCED MONITORING:\n") cat(" - Implement additional monitoring for flagged accounts\n") cat(" - Set up alerts for unusual login patterns\n\n") cat("3. ACTIVITY VALIDATION:\n") cat(" - Verify if the detected high activity levels are legitimate\n") cat(" - Check business justification for observed patterns\n\n") # Special warning for Oracle-maintained accounts if ("ORACLE_MAINTAINED" %in% colnames(risky_users)) { oracle_maintained <- filter(risky_users, ORACLE_MAINTAINED == "Y") if (nrow(oracle_maintained) > 0) { cat("4. CRITICAL WARNING - ORACLE-MAINTAINED ACCOUNTS:\n") cat(" - The following Oracle-maintained accounts show risky patterns:\n") cat(" ", paste(unique(oracle_maintained$USERNAME), collapse = ", "), "\n") cat(" - These accounts typically have minimal activity\n") cat(" - Investigate immediately for potential security breaches\n\n") } } cat("5. NEXT STEPS:\n") cat(" - Schedule security review meeting\n") cat(" - Update monitoring policies based on findings\n") cat(" - Consider implementing multi-factor authentication\n") } else { cat("\n" + strrep("=", 60) + "\n") cat("SECURITY ASSESSMENT RESULTS\n") cat(strrep("=", 60) + "\n") cat("\nNo unusual user behavior patterns detected in this analysis period.\n") cat("Current security posture appears normal based on analyzed metrics.\n") }}
# -----------------------------------------------------------------------------------# Execute Security Risk Analysis# -----------------------------------------------------------------------------------cat("\n" + strrep("=", 60) + "\n")cat("INITIATING DATABASE SECURITY RISK ANALYSIS\n")cat(strrep("=", 60) + "\n\n")
analyze_security_risks()
cat("\n" + strrep("=", 60) + "\n")cat("SECURITY ANALYSIS COMPLETED\n")cat(strrep("=", 60) + "\n")
# -----------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------# Load Required Libraries# -----------------------------------------------------------------------------------library(DBI) # Database Interfacelibrary(odbc) # ODBC Database Connectivitylibrary(dplyr) # Data Manipulationlibrary(lubridate) # Date-Time Processinglibrary(ggplot2) # Data Visualizationlibrary(anomalize) # Anomaly Detectionlibrary(cluster) # Clustering Algorithmslibrary(tidyr) # Data Reshapinglibrary(config) # Configuration Management
# -----------------------------------------------------------------------------------# Function: safe_db_connect# Purpose : Secure database connection with error handling# Returns : Connection object on success, NULL on failure# Security: Implements secure connection practices with proper error handling# -----------------------------------------------------------------------------------safe_db_connect <- function() { tryCatch({ conn <- dbConnect(odbc::odbc(), dsn = "[DB_NAME]", uid = "[USERNAME]", pwd = "[PASSWORD]") message("Successfully connected to database") return(conn) }, error = function(e) { message("Connection failed: ", e$message) return(NULL) })}
# -----------------------------------------------------------------------------------# Function: analyze_security_risks# Purpose : Main analysis function that orchestrates security risk assessment# Process : 1. Connects to database# 2. Retrieves multiple security-related datasets# 3. Performs anomaly detection on login patterns# 4. Clusters users by behavioral patterns# 5. Identifies potentially risky users# 6. Generates security recommendations# -----------------------------------------------------------------------------------analyze_security_risks <- function() { # Establish secure database connection conn <- safe_db_connect() if (is.null(conn)) { stop("Failed to establish database connection. Analysis aborted.") } # Ensure connection is closed on exit on.exit({ dbDisconnect(conn) message("Database connection closed") }) # Main analysis block with error handling tryCatch({ # ------------------------------------------------------------------------------- # Data Collection Phase # Retrieves security-related data from multiple audit tables # ------------------------------------------------------------------------------- # Retrieve user activity data (last 90 days) user_activity <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, LOGIN_TIME, CLIENT_IP, CLIENT_HOSTNAME, OS_USER, ACTION, CLIENT_PROGRAM_NAME FROM ATTSTATS.USER_ACTIVITY_LOG WHERE TRUNC(LOGIN_TIME) > TRUNC(SYSDATE-90)") }, error = function(e) { warning("Failed to retrieve user activity data: ", e$message) return(NULL) }) # Retrieve audit activity data audit_activity <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, ACTION_NAME, TIMESTAMP FROM ATTSTATS.RECENT_AUDIT_ACTIVITY") }, error = function(e) { warning("Failed to retrieve audit activity data: ", e$message) return(NULL) }) # Retrieve user account information (last 90 days) user_accounts <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, ACCOUNT_STATUS, DEFAULT_TABLESPACE, PROFILE, LAST_LOGIN, ORACLE_MAINTAINED FROM ATTSTATS.USER_ACCOUNT_INFORMATION WHERE TRUNC(CHECK_DATE) > TRUNC(SYSDATE-90)") }, error = function(e) { warning("Failed to retrieve user account data: ", e$message) return(NULL) }) # Retrieve DDL (Data Definition Language) audit data ddl_audit <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, OPERATION, OBJECT_TYPE, OBJECT_NAME, OBJECT_OWNER FROM ATTSTATS.DDL_AUDIT_LOG") }, error = function(e) { warning("Failed to retrieve DDL audit data: ", e$message) return(NULL) }) # Validate minimum data requirements if (is.null(user_activity)) { stop("User activity data is required but not available. Analysis aborted.") } # ------------------------------------------------------------------------------- # Data Preprocessing Phase # Prepares user activity data for analysis # ------------------------------------------------------------------------------- activity_summary <- user_activity %>% mutate( LOGIN_DATETIME = as.POSIXct(LOGIN_TIME), # Convert to datetime LOGIN_DATE = as.Date(LOGIN_DATETIME), # Extract date component LOGIN_HOUR = hour(LOGIN_DATETIME) # Extract hour component ) %>% group_by(USERNAME, LOGIN_DATE) %>% summarise( DAILY_LOGINS = n(), # Count of daily logins UNIQUE_CLIENTS = n_distinct(CLIENT_IP), # Unique IP addresses UNIQUE_PROGRAMS = n_distinct(CLIENT_PROGRAM_NAME), # Unique client programs ACTIONS = paste(unique(ACTION), collapse = ", "), # Concatenated actions .groups = "drop" ) # ------------------------------------------------------------------------------- # Anomaly Detection Phase # Identifies unusual patterns in daily login activity # ------------------------------------------------------------------------------- login_anomalies <- activity_summary %>% as_tibble() %>% mutate(LOGIN_DATETIME = as.POSIXct(paste(LOGIN_DATE, "00:00:00"))) %>% time_decompose(DAILY_LOGINS, method = "stl", frequency = "1 week") %>% # Decompose time series anomalize(remainder, method = "gesd") %>% # Detect anomalies using GESD method time_recompose() # Reconstruct time series # ------------------------------------------------------------------------------- # Visualization: Anomaly Plot # Shows detected anomalies in login patterns # ------------------------------------------------------------------------------- anomaly_plot <- login_anomalies %>% plot_anomalies(time_recomposed = TRUE) + labs( title = "Anomalous Login Activity by User", subtitle = "Daily login patterns with detected anomalies (Red points indicate anomalies)", x = "Date", y = "Number of Logins" ) + theme_minimal() + theme(plot.title = element_text(face = "bold", size = 14), plot.subtitle = element_text(color = "darkgray")) print(anomaly_plot) # Extract suspicious users from anomaly detection suspicious_users <- login_anomalies %>% filter(anomaly == "Yes") %>% select(USERNAME, LOGIN_DATE, DAILY_LOGINS, recomposed_l1, recomposed_l2) # ------------------------------------------------------------------------------- # DDL Pattern Analysis # Analyzes structural changes to database objects # ------------------------------------------------------------------------------- ddl_patterns <- if (!is.null(ddl_audit)) { ddl_audit %>% group_by(USERNAME, OPERATION, OBJECT_TYPE) %>% summarise(COUNT = n(), .groups = "drop") %>% pivot_wider( names_from = c(OPERATION, OBJECT_TYPE), values_from = COUNT, values_fill = 0 ) } else { warning("DDL patterns analysis skipped due to missing data") tibble(USERNAME = character()) } # ------------------------------------------------------------------------------- # Behavioral Clustering Phase # Groups users by their activity patterns using k-means clustering # ------------------------------------------------------------------------------- behavior_data <- activity_summary %>% group_by(USERNAME) %>% summarise( AVG_LOGINS = mean(DAILY_LOGINS), # Average daily logins MAX_LOGINS = max(DAILY_LOGINS), # Maximum daily logins CLIENT_VARIETY = mean(UNIQUE_CLIENTS), # Average unique clients PROGRAM_VARIETY = mean(UNIQUE_PROGRAMS), # Average unique programs .groups = "drop" ) # Enrich behavior data with DDL patterns if available if (nrow(ddl_patterns) > 0) { behavior_data <- behavior_data %>% left_join(ddl_patterns, by = "USERNAME") } # Handle missing values (replace NA with 0) behavior_data <- behavior_data %>% replace(is.na(.), 0) # ------------------------------------------------------------------------------- # Clustering Analysis # Performs k-means clustering on normalized behavioral data # ------------------------------------------------------------------------------- if (nrow(behavior_data) > 1) { # Normalize data for clustering scaled_data <- scale(select(behavior_data, -USERNAME)) # Determine optimal number of clusters (maximum of 5 or n-1) set.seed(123) # For reproducibility max_clusters <- min(5, nrow(behavior_data) - 1) kmeans_result <- kmeans(scaled_data, centers = max_clusters) behavior_data$CLUSTER <- as.factor(kmeans_result$cluster) # Analyze cluster characteristics cluster_summary <- behavior_data %>% group_by(CLUSTER) %>% summarise(across(where(is.numeric), mean), .groups = "drop") %>% arrange(desc(AVG_LOGINS)) # Identify unusual clusters (potential security risks) # Clusters with high average logins or extremely high maximum logins unusual_cluster <- cluster_summary %>% filter( AVG_LOGINS > quantile(cluster_summary$AVG_LOGINS, 0.75) | MAX_LOGINS > quantile(cluster_summary$MAX_LOGINS, 0.9) ) %>% pull(CLUSTER) # Flag users in unusual clusters as potentially risky risky_users <- behavior_data %>% filter(CLUSTER %in% unusual_cluster) # Enrich risky users with account information if available if (!is.null(user_accounts)) { risky_users <- risky_users %>% left_join(user_accounts, by = "USERNAME") %>% select( USERNAME, AVG_LOGINS, MAX_LOGINS, CLIENT_VARIETY, PROGRAM_VARIETY, ACCOUNT_STATUS, PROFILE, ORACLE_MAINTAINED, CLUSTER ) } # ----------------------------------------------------------------------------- # Visualization: Cluster Plot # Shows user clusters based on behavioral patterns # ----------------------------------------------------------------------------- cluster_plot <- ggplot(behavior_data, aes(x = AVG_LOGINS, y = MAX_LOGINS, color = CLUSTER)) + geom_point(alpha = 0.7, size = 3) + labs( title = "User Behavior Clusters for Security Analysis", subtitle = "K-means clustering based on login patterns (Colors represent behavioral groups)", x = "Average Daily Logins", y = "Maximum Daily Logins", color = "Behavior Cluster" ) + theme_minimal() + scale_color_brewer(palette = "Set1") + theme(plot.title = element_text(face = "bold", size = 14), legend.position = "bottom") print(cluster_plot) # ----------------------------------------------------------------------------- # Generate Security Recommendations # Provides actionable insights based on analysis results # ----------------------------------------------------------------------------- generate_recommendations(risky_users) } else { warning("Insufficient data for clustering analysis") cat("No user behavior patterns detected due to insufficient data\n") } }, error = function(e) { message("Analysis failed: ", e$message) })}
# -----------------------------------------------------------------------------------# Function: generate_recommendations# Purpose : Generates actionable security recommendations based on identified risks# Input : risky_users - Data frame of potentially risky users with metrics# Output : Printed recommendations to console# -----------------------------------------------------------------------------------generate_recommendations <- function(risky_users) { if (nrow(risky_users) > 0) { cat("\n" + strrep("=", 60) + "\n") cat("POTENTIALLY RISKY USERS IDENTIFIED\n") cat(strrep("=", 60) + "\n\n") print(risky_users) cat("\n" + strrep("=", 60) + "\n") cat("SECURITY RECOMMENDATIONS\n") cat(strrep("=", 60) + "\n\n") cat("1. USER ACCOUNT REVIEW:\n") cat(" - Review activity patterns and permissions for:\n") cat(" ", paste(unique(risky_users$USERNAME), collapse = ", "), "\n\n") cat("2. ENHANCED MONITORING:\n") cat(" - Implement additional monitoring for flagged accounts\n") cat(" - Set up alerts for unusual login patterns\n\n") cat("3. ACTIVITY VALIDATION:\n") cat(" - Verify if the detected high activity levels are legitimate\n") cat(" - Check business justification for observed patterns\n\n") # Special warning for Oracle-maintained accounts if ("ORACLE_MAINTAINED" %in% colnames(risky_users)) { oracle_maintained <- filter(risky_users, ORACLE_MAINTAINED == "Y") if (nrow(oracle_maintained) > 0) { cat("4. CRITICAL WARNING - ORACLE-MAINTAINED ACCOUNTS:\n") cat(" - The following Oracle-maintained accounts show risky patterns:\n") cat(" ", paste(unique(oracle_maintained$USERNAME), collapse = ", "), "\n") cat(" - These accounts typically have minimal activity\n") cat(" - Investigate immediately for potential security breaches\n\n") } } cat("5. NEXT STEPS:\n") cat(" - Schedule security review meeting\n") cat(" - Update monitoring policies based on findings\n") cat(" - Consider implementing multi-factor authentication\n") } else { cat("\n" + strrep("=", 60) + "\n") cat("SECURITY ASSESSMENT RESULTS\n") cat(strrep("=", 60) + "\n") cat("\nNo unusual user behavior patterns detected in this analysis period.\n") cat("Current security posture appears normal based on analyzed metrics.\n") }}
# -----------------------------------------------------------------------------------# Execute Security Risk Analysis# -----------------------------------------------------------------------------------cat("\n" + strrep("=", 60) + "\n")cat("INITIATING DATABASE SECURITY RISK ANALYSIS\n")cat(strrep("=", 60) + "\n\n")
analyze_security_risks()
cat("\n" + strrep("=", 60) + "\n")cat("SECURITY ANALYSIS COMPLETED\n")cat(strrep("=", 60) + "\n")
# -----------------------------------------------------------------------------------