using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations;
using System.Management.Automation;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Collections.Concurrent;
// SECURE: ASP.NET Web API with comprehensive security measures
[ApiController]
[Route("api/[controller]")]
public class SecureSystemManagementController : ControllerBase
{
private readonly ILogger<SecureSystemManagementController> _logger;
private readonly SecurePowerShellService _powerShellService;
private readonly IRateLimitingService _rateLimitingService;
private readonly ISecurityAuditService _auditService;
public SecureSystemManagementController(
ILogger<SecureSystemManagementController> logger,
SecurePowerShellService powerShellService,
IRateLimitingService rateLimitingService,
ISecurityAuditService auditService)
{
_logger = logger;
_powerShellService = powerShellService;
_rateLimitingService = rateLimitingService;
_auditService = auditService;
}
// SECURE: Process information with validation
[HttpGet("processes/{processName}")]
public async Task<IActionResult> GetProcesses([ProcessNameValidation] string processName)
{
var clientIP = GetClientIP();
// Rate limiting
if (!await _rateLimitingService.IsAllowedAsync(clientIP, "GetProcesses"))
{
return StatusCode(429, "Rate limit exceeded");
}
// Security audit logging
_auditService.LogAPIRequest("GetProcesses", clientIP, new { processName });
try
{
// Input validation
var validation = PowerShellInputValidator.ValidateProcessName(processName);
if (!validation.IsValid)
{
return BadRequest(new { errors = validation.Errors });
}
// Use secure PowerShell execution
var result = await _powerShellService.GetProcessInfoSafely(processName);
return Ok(new {
processes = result,
processName = processName,
timestamp = DateTime.UtcNow
});
}
catch (ValidationException ex)
{
_logger.LogWarning("Validation error in GetProcesses: {Error}", ex.Message);
return BadRequest(new { error = "Invalid input parameters" });
}
catch (SecurityException ex)
{
_logger.LogWarning("Security violation in GetProcesses from {ClientIP}: {Error}", clientIP, ex.Message);
_auditService.LogSecurityViolation("GetProcesses", clientIP, ex.Message);
return Forbid("Security policy violation");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in GetProcesses for process {ProcessName}", processName);
return StatusCode(500, "Internal server error");
}
}
// SECURE: Restricted command execution with allowlist
[HttpPost("execute")]
public async Task<IActionResult> ExecuteAllowedCommand([FromBody] SecureCommandRequest request)
{
var clientIP = GetClientIP();
if (!await _rateLimitingService.IsAllowedAsync(clientIP, "ExecuteCommand"))
{
return StatusCode(429, "Rate limit exceeded");
}
_auditService.LogAPIRequest("ExecuteCommand", clientIP, new { command = request.CommandName });
try
{
// Validate request
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Execute with sandboxed PowerShell
var result = await _powerShellService.ExecuteAllowedCommandSafely(
request.CommandName,
request.Parameters ?? new Dictionary<string, object>(),
TimeSpan.FromSeconds(30));
if (!result.Success)
{
return BadRequest(new {
error = "Command execution failed",
details = result.Errors
});
}
return Ok(new {
success = true,
output = result.Output,
executionTime = result.Duration.TotalMilliseconds,
timestamp = DateTime.UtcNow
});
}
catch (SecurityException ex)
{
_logger.LogWarning("Security violation in ExecuteCommand from {ClientIP}: {Error}", clientIP, ex.Message);
_auditService.LogSecurityViolation("ExecuteCommand", clientIP, ex.Message);
return Forbid("Command not allowed");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error executing command {Command}", request.CommandName);
return StatusCode(500, "Command execution failed");
}
}
// SECURE: File listing with path validation
[HttpGet("files")]
public async Task<IActionResult> ListFiles([FromQuery] SecureFileListRequest request)
{
var clientIP = GetClientIP();
if (!await _rateLimitingService.IsAllowedAsync(clientIP, "ListFiles"))
{
return StatusCode(429, "Rate limit exceeded");
}
_auditService.LogAPIRequest("ListFiles", clientIP, new { path = request.Path });
try
{
// Validate model
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Use .NET file system operations instead of PowerShell
var fileSystemService = new SecureFileSystemService();
var files = fileSystemService.GetDirectoryContents(
request.Path,
request.Filter ?? "*.*",
request.MaxResults ?? 100);
return Ok(new {
path = request.Path,
filter = request.Filter,
fileCount = files.Count(),
files = files.Select(f => new {
name = f.Name,
size = f is FileInfo fi ? fi.Length : 0,
lastModified = f.LastWriteTime,
isDirectory = f is DirectoryInfo
})
});
}
catch (SecurityException ex)
{
_logger.LogWarning("Security violation in ListFiles from {ClientIP}: {Error}", clientIP, ex.Message);
return Forbid("Path access denied");
}
catch (DirectoryNotFoundException)
{
return NotFound("Directory not found");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error listing files for path {Path}", request.Path);
return StatusCode(500, "File listing failed");
}
}
// SECURE: System information with restricted operations
[HttpPost("system-info")]
public async Task<IActionResult> GetSystemInfo([FromBody] SecureSystemInfoRequest request)
{
var clientIP = GetClientIP();
if (!await _rateLimitingService.IsAllowedAsync(clientIP, "GetSystemInfo"))
{
return StatusCode(429, "Rate limit exceeded");
}
_auditService.LogAPIRequest("GetSystemInfo", clientIP, new { infoType = request.InfoType });
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Use .NET libraries instead of PowerShell where possible
var systemService = new SecureSystemOperations();
object result = request.InfoType.ToLower() switch
{
"processes" => systemService.GetRunningProcesses(request.ProcessFilter),
"services" => systemService.GetSystemServices(request.ServiceFilter),
"system" => systemService.GetSystemInformation(),
_ => throw new ArgumentException($"Invalid info type: {request.InfoType}")
};
return Ok(new {
infoType = request.InfoType,
data = result,
timestamp = DateTime.UtcNow
});
}
catch (ArgumentException ex)
{
return BadRequest(new { error = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting system info for type {InfoType}", request.InfoType);
return StatusCode(500, "System information retrieval failed");
}
}
// SECURE: File download with validation
[HttpPost("download")]
public async Task<IActionResult> DownloadFile([FromBody] SecureDownloadRequest request)
{
var clientIP = GetClientIP();
if (!await _rateLimitingService.IsAllowedAsync(clientIP, "DownloadFile"))
{
return StatusCode(429, "Rate limit exceeded");
}
_auditService.LogAPIRequest("DownloadFile", clientIP, new { url = request.Url });
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Use secure HTTP client instead of PowerShell
var downloadService = new SecureDownloadService();
var result = await downloadService.DownloadFileAsync(
request.Url,
request.OutputFileName,
maxSizeBytes: 50 * 1024 * 1024); // 50MB limit
return Ok(new {
message = "Download completed successfully",
fileName = request.OutputFileName,
size = result.FileSize,
downloadTime = result.Duration.TotalSeconds
});
}
catch (SecurityException ex)
{
_logger.LogWarning("Security violation in DownloadFile from {ClientIP}: {Error}", clientIP, ex.Message);
return Forbid("Download not allowed");
}
catch (HttpRequestException ex)
{
return BadRequest(new { error = $"Download failed: {ex.Message}" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error downloading file from {Url}", request.Url);
return StatusCode(500, "Download failed");
}
}
private string GetClientIP()
{
return HttpContext.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
}
}
// Secure request DTOs with validation attributes
public class SecureCommandRequest
{
[Required]
[AllowedCommand]
public string CommandName { get; set; }
[ValidatedParameters]
public Dictionary<string, object> Parameters { get; set; }
[Range(1, 120)]
public int TimeoutSeconds { get; set; } = 30;
}
public class SecureFileListRequest
{
[Required]
[AllowedPath]
public string Path { get; set; }
[FileFilter]
public string Filter { get; set; } = "*.*";
[Range(1, 1000)]
public int? MaxResults { get; set; } = 100;
}
public class SecureSystemInfoRequest
{
[Required]
[AllowedValues("processes", "services", "system")]
public string InfoType { get; set; }
[ProcessNameValidation]
public string ProcessFilter { get; set; }
[ServiceNameValidation]
public string ServiceFilter { get; set; }
}
public class SecureDownloadRequest
{
[Required]
[AllowedUrl]
public string Url { get; set; }
[Required]
[ValidFileName]
public string OutputFileName { get; set; }
}
// Custom validation attributes
public class ProcessNameValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value is string processName)
{
var validation = PowerShellInputValidator.ValidateProcessName(processName);
ErrorMessage = string.Join(", ", validation.Errors);
return validation.IsValid;
}
return false;
}
}
public class AllowedCommandAttribute : ValidationAttribute
{
private static readonly HashSet<string> AllowedCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Get-Process", "Get-Service", "Get-ChildItem", "Test-Path", "Get-Date"
};
public override bool IsValid(object value)
{
if (value is string command)
{
var isAllowed = AllowedCommands.Contains(command);
if (!isAllowed)
ErrorMessage = $"Command '{command}' is not allowed";
return isAllowed;
}
return false;
}
}
public class AllowedPathAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value is string path)
{
var validation = PowerShellInputValidator.ValidateFilePath(path);
ErrorMessage = string.Join(", ", validation.Errors);
return validation.IsValid;
}
return false;
}
}
public class AllowedUrlAttribute : ValidationAttribute
{
private static readonly HashSet<string> AllowedHosts = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"api.example.com", "secure.mycompany.com", "files.trusted.com"
};
public override bool IsValid(object value)
{
if (value is string url)
{
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
{
var isAllowed = AllowedHosts.Contains(uri.Host) &&
(uri.Scheme == "https" || uri.Scheme == "http");
if (!isAllowed)
ErrorMessage = $"URL host '{uri.Host}' is not in allowed list";
return isAllowed;
}
ErrorMessage = "Invalid URL format";
}
return false;
}
}
// Supporting services would be implemented here...
public interface IRateLimitingService
{
Task<bool> IsAllowedAsync(string clientId, string operation);
}
public interface ISecurityAuditService
{
void LogAPIRequest(string operation, string clientIP, object parameters);
void LogSecurityViolation(string operation, string clientIP, string details);
}
public class SecureDownloadService
{
public async Task<DownloadResult> DownloadFileAsync(string url, string fileName, int maxSizeBytes)
{
// Implementation using HttpClient with security controls
throw new NotImplementedException();
}
}
public class DownloadResult
{
public long FileSize { get; set; }
public TimeSpan Duration { get; set; }
}
public class SecureFileSystemService
{
public IEnumerable<FileSystemInfo> GetDirectoryContents(string path, string filter, int maxResults)
{
// Implementation using .NET file system APIs with security controls
throw new NotImplementedException();
}
}