module fortplot_system_commands !! System command operations module !! !! This module handles system command operations with strict security !! controls to prevent command injection vulnerabilities. use fortplot_os_detection, only: is_debug_enabled, is_windows implicit none private public :: open_with_default_app_runtime public :: check_command_available_runtime public :: use_system_mkdir_ci public :: try_system_mkdir contains subroutine open_with_default_app_runtime(filename, success) !! Open file with default application - SECURITY: Disabled for compliance character(len=*), intent(in) :: filename logical, intent(out) :: success character(len=256) :: ci_env integer :: status associate(dfn=>len_trim(filename)); end associate success = .false. ! Check if running in CI environment (skip file opening to prevent hangs) call get_environment_variable("CI", ci_env, status=status) if (status == 0 .and. len_trim(ci_env) > 0) then ! Running in CI - don't open files with GUI applications success = .true. ! Pretend success to allow tests to continue return end if ! Check for GitHub Actions specifically call get_environment_variable("GITHUB_ACTIONS", ci_env, status=status) if (status == 0 .and. len_trim(ci_env) > 0) then ! Running in GitHub Actions - don't open files with GUI applications success = .true. ! Pretend success to allow tests to continue return end if ! SECURITY: External application execution disabled for security compliance ! This functionality requires execute_command_line which is prohibited success = .false. end subroutine open_with_default_app_runtime subroutine use_system_mkdir_ci(path, success) !! SECURITY FIX: Pure Fortran directory creation - no system calls character(len=*), intent(in) :: path logical, intent(out) :: success logical :: debug_enabled success = .false. debug_enabled = is_debug_enabled() if (debug_enabled) then write(*,'(A,A)') 'SECURITY: Using safe Fortran-only directory creation for: ', trim(path) end if ! SECURITY FIX: Replace execute_command_line with pure Fortran implementation ! Use existing file operations module instead success = .false. ! Defer to other directory creation methods if (debug_enabled) then write(*,'(A)') 'SECURITY: System mkdir disabled, using pure Fortran approach' end if end subroutine use_system_mkdir_ci subroutine try_system_mkdir(path, success) !! Try to use system mkdir command as last resort (with security checks) character(len=*), intent(in) :: path logical, intent(out) :: success character(len=256) :: env_value integer :: status logical :: is_ci associate(dpn=>len_trim(path)); end associate success = .false. ! Only allow in CI environments for security is_ci = .false. call get_environment_variable("CI", env_value, status=status) if (status == 0 .and. (trim(env_value) == "true" .or. trim(env_value) == "1")) then is_ci = .true. end if call get_environment_variable("GITHUB_ACTIONS", env_value, status=status) if (status == 0 .and. (trim(env_value) == "true" .or. trim(env_value) == "1")) then is_ci = .true. end if ! Also check for FPM enablement call get_environment_variable("FORTPLOT_ENABLE_FPM", env_value, status=status) if (status == 0 .and. (trim(env_value) == "true" .or. trim(env_value) == "1")) then is_ci = .true. end if if (.not. is_ci) then ! Not in CI, cannot use system commands return end if ! SECURITY: execute_command_line is not allowed - always fail success = .false. end subroutine try_system_mkdir subroutine check_command_available_runtime(command_name, available) !! Check if a command is available - with security restrictions character(len=*), intent(in) :: command_name logical, intent(out) :: available logical :: debug_enabled logical :: is_allowed_command character(len=256) :: env_value integer :: status available = .false. debug_enabled = is_debug_enabled() ! Check if this is an allowed command is_allowed_command = .false. ! Check for allowed development tools (FPM) if (trim(command_name) == 'fpm') then ! FPM is essential for development - allow in CI environments call get_environment_variable("CI", env_value, status) if (status == 0 .and. (trim(env_value) == "true" .or. trim(env_value) == "1")) then is_allowed_command = .true. end if call get_environment_variable("GITHUB_ACTIONS", env_value, status) if (status == 0 .and. (trim(env_value) == "true" .or. trim(env_value) == "1")) then is_allowed_command = .true. end if ! Also check for explicit FPM enablement call get_environment_variable("FORTPLOT_ENABLE_FPM", env_value, status) if (status == 0 .and. len_trim(env_value) > 0 .and. & (trim(env_value) == "1" .or. trim(env_value) == "true")) then is_allowed_command = .true. end if else if (trim(command_name) == 'ffmpeg' .or. trim(command_name) == 'ffprobe') then ! Check for media tool environment call get_environment_variable("FORTPLOT_ENABLE_FFMPEG", env_value, status) if (status == 0 .and. trim(env_value) == "1") then is_allowed_command = .true. end if call get_environment_variable("CI", env_value, status) if (status == 0 .and. trim(env_value) == "true") then is_allowed_command = .true. end if else if (trim(command_name) == 'magick' .or. trim(command_name) == 'convert' .or. & trim(command_name) == 'compare' .or. trim(command_name) == 'identify') then ! Check for ImageMagick tool environment call get_environment_variable("FORTPLOT_ENABLE_IMAGEMAGICK", env_value, status) if (status == 0 .and. (trim(env_value) == "1" .or. trim(env_value) == "true")) then is_allowed_command = .true. end if call get_environment_variable("CI", env_value, status) if (status == 0 .and. trim(env_value) == "true") then is_allowed_command = .true. end if call get_environment_variable("GITHUB_ACTIONS", env_value, status) if (status == 0 .and. trim(env_value) == "true") then is_allowed_command = .true. end if end if if (is_allowed_command) then ! For allowed commands, we still can't actually check availability ! without execute_command_line, but we can assume they exist in CI available = .true. if (debug_enabled) then write(*,'(A,A,A)') 'Command assumed available in enabled environment: ', trim(command_name), & ' (actual check requires execute_command_line)' end if else available = .false. if (debug_enabled) then write(*,'(A,A)') 'Command check disabled for security: ', trim(command_name) end if end if end subroutine check_command_available_runtime end module fortplot_system_commands