public Import Add(Import import) { return(_importRepository.Add(import)); }
public ImportResult Import(Import import) { var csvConfiguration = new Configuration(); csvConfiguration.RegisterClassMap(new ImportDataMap()); csvConfiguration.MissingFieldFound = null; // Get all the records out of the CSV file List <ImportData> importRecords; bool isDescription = true; // Special validation for Verbiage and Description using (var reader = _csvFactory.CreateReader(new StreamReader(import.File), csvConfiguration)) { reader.Read(); reader.ReadHeader(); List <string> headers = reader.Context.HeaderRecord.ToList(); if (headers.Contains("Finding (Verbiage)") && headers.Contains("Finding (Description)")) { throw new ArgumentException("You can't have both Verbiage and Description columns."); } else if (headers.Contains("Finding (Verbiage)")) { isDescription = false; } reader.Configuration.UnregisterClassMap(); if (!isDescription) { reader.Configuration.RegisterClassMap(new ImportVerbiageDataMap()); } else { reader.Configuration.RegisterClassMap(new ImportDataMap()); } List <ImportData> records = reader.GetRecords <ImportData>().ToList(); importRecords = records; } // Get engagement ID and phase ID int engagementId = Convert.ToInt32(import.EngagementId); var phaseId = Convert.ToInt32(import.PhaseId); // Create and save import entity var importEntity = _importMapper.Map(import); importEntity.ImportedDate = DateTime.Now; importEntity.AssessmentDate = DateTime.Now; // TODO: AssessmentDate will eventually be entered in by the user _importRepository.Add(importEntity); _importRepository.Save(); _importMapper.Map(importEntity, import); // Use this to store HostVulnerabilities and display them properly after importing those rows var importId = importEntity.Id; var penultimateImportId = _importRepository.GetPenultimate(engagementId)?.Id ?? null; // Get all existing hosts for this engagement and phase // Hosts and Vulnerabilities records must be unique (per phase), and so they're only imported once (per phase). // Hence, we do not need to worry about the import ID for Hosts and Vulnerabilities. var hostEntities = _hostRepository .GetByEngagementId(engagementId) .Where(x => x.PhaseId == phaseId) .ToList(); var hostsToAdd = new List <HostEntity>(); var hostsToUpdate = new List <HostEntity>(); // Get all existing vulns for this engagement and phase var vulnerabilityEntities = _vulnerabilityRepository .GetByEngagementId(engagementId) .Where(x => x.PhaseId == phaseId) .ToList(); var vulnerabilitiesToAdd = new List <VulnerabilityEntity>(); var vulnerabilitiesToUpdate = new List <VulnerabilityEntity>(); // Get all existing hostVulnerabilities for this engagement and phase var hostVulnerabilityEntities = _hostVulnerabilityRepository .GetByEngagementId(engagementId) .Where(x => x.Vulnerability.PhaseId == phaseId && x.Host.PhaseId == phaseId && x.ImportId == importId) .ToList(); var hostVulnerabilitiesToAdd = new List <HostVulnerabilityEntity>(); var hostVulnerabilitiesToUpdate = new List <HostVulnerabilityEntity>(); // Iterate through each CSV record, grouped by Finding ("Title") var hostVulnerabilityImportGroups = importRecords.GroupBy(x => new { x.Title, x.Port, x.Service, x.IPAddress, x.HostName }); foreach (var importRecordGroup in hostVulnerabilityImportGroups) { // Encrypt sensitive fields var titleBytes = Encrypt(importRecordGroup.Key.Title); var portBytes = Encrypt(int.Parse(importRecordGroup.Key.Port)); var serviceBytes = Encrypt(importRecordGroup.Key.Service); var ipAddressBytes = Encrypt(importRecordGroup.Key.IPAddress); var hostNameBytes = Encrypt(importRecordGroup.Key.HostName); var firstImportRecordGroup = importRecordGroup.First(); // Create or update a vulnerability entity for this CSV record // NOTE: All imports share a single set of unique vulnerabilities var vulnerabilityEntity = AddOrUpdateEntity( x => x.TitleBytes.SequenceEqual(titleBytes) && x.PortBytes.SequenceEqual(portBytes) && x.ServiceBytes.SequenceEqual(serviceBytes), vulnerabilityEntities, vulnerabilitiesToAdd, vulnerabilitiesToUpdate); vulnerabilityEntity.IsStatusUnknown = false; if (vulnerabilityEntity.IsHistorical) { vulnerabilityEntity.IsHistorical = false; vulnerabilityEntity.RemediationStatusId = 1; // MitigationStatus.NotMitigated.Value; vulnerabilityEntity.MitigatedDate = null; } // RemediationStatusId is technically a nullable field, but upon import we should always // set a default of NotMitigated. if (vulnerabilityEntity.RemediationStatusId == null) { vulnerabilityEntity.RemediationStatusId = 1; //MitigationStatus.NotMitigated.Value; } _importVulnerabilityMapper.Map(firstImportRecordGroup, import, vulnerabilityEntity); // Create or update a host for this CSV record // NOTE: All imports in a phase share a single set of unique hosts var hostEntity = AddOrUpdateEntity( x => x.IPAddressBytes.SequenceEqual(ipAddressBytes) && x.NameBytes.SequenceEqual(hostNameBytes), hostEntities, hostsToAdd, hostsToUpdate); hostEntity.Status = "Active"; hostEntity.PhaseId = phaseId; _importHostMapper.Map(firstImportRecordGroup, import, hostEntity); // Create a partially-encrypted hostVuln for this CSV record // NOTE: HostVulnerabilities are always added as new records during the import process AddEntity( x => x.Vulnerability.TitleBytes.SequenceEqual(titleBytes) && x.Vulnerability.PortBytes.SequenceEqual(portBytes) && x.Vulnerability.ServiceBytes.SequenceEqual(serviceBytes) && x.Host.IPAddressBytes.SequenceEqual(ipAddressBytes) && x.Host.NameBytes.SequenceEqual(hostNameBytes), hostVulnerabilityEntities, hostVulnerabilitiesToAdd, hostVulnerabilitiesToUpdate, CreateHostVulnerability(engagementId, phaseId, hostEntity, vulnerabilityEntity, importEntity)); } // Write new HostVulnerabilities to the database if (hostVulnerabilitiesToAdd.Any()) { _hostVulnerabilityRepository.AddRange(hostVulnerabilitiesToAdd); } _hostVulnerabilityRepository.Save(); _hostRepository.Save(); _vulnerabilityRepository.Save(); // Determine status of every host entity var hostsToAddOrUpdate = hostsToAdd.Union(hostsToUpdate).ToArray(); var hostsToMarkOffline = hostEntities.Except(hostsToAddOrUpdate); // Another pass through the host entities is required to figure out which hosts are missing, i.e., 'Offline' foreach (var hostEntity in hostEntities) { // TODO offline vs. retired if (hostsToMarkOffline.Contains(hostEntity)) { hostEntity.Status = "Offline"; } else if (hostsToAddOrUpdate.Contains(hostEntity)) { hostEntity.Status = "Active"; } } // Do a vuln delta check between latest and penultimate imports and adjust vuln remediation metadata var vulnerabilityEntitiesToAdd = ProcessVulnerabilityChanges( hostVulnerabilityEntities, vulnerabilityEntities, hostEntities, importId, penultimateImportId, phaseId, importEntity.AssessmentDate); // Save Hosts and Vulnerabilities _hostVulnerabilityRepository.Save(); _hostRepository.Save(); _vulnerabilityRepository.Save(); // Return vuln and host insert and update counts return(new ImportResult(vulnerabilitiesToAdd.Count, vulnerabilitiesToUpdate.Count, hostsToAdd.Count, hostsToUpdate.Count)); }