All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.denimgroup.threadfix.data.entities.Scan Maven / Gradle / Ivy

Go to download

ThreadFix is a software vulnerability aggregation and management system that reduces the time it takes to fix software vulnerabilities. ThreadFix imports the results from dynamic, static and manual testing to provide a centralized view of software security defects across development teams and applications. The system allows companies to correlate testing results and streamline software remediation efforts by simplifying feeds to software issue trackers. By auto generating application firewall rules, this tool allows organizations to continue remediation work uninterrupted. ThreadFix empowers managers with vulnerability trending reports that show progress over time, giving them justification for their efforts. ThreadFix is developed and maintained by Denim Group, Ltd (http://www.denimgroup.com) For information about commercial support and other services, contact Denim Group about ThreadFix http://www.denimgroup.com/resources-threadfix/

There is a newer version: 2.5
Show newest version
////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2015 Denim Group, Ltd.
//
//     The contents of this file are subject to the Mozilla Public License
//     Version 2.0 (the "License"); you may not use this file except in
//     compliance with the License. You may obtain a copy of the License at
//     http://www.mozilla.org/MPL/
//
//     Software distributed under the License is distributed on an "AS IS"
//     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//     License for the specific language governing rights and limitations
//     under the License.
//
//     The Original Code is ThreadFix.
//
//     The Initial Developer of the Original Code is Denim Group, Ltd.
//     Portions created by Denim Group, Ltd. are Copyright (C)
//     Denim Group, Ltd. All Rights Reserved.
//
//     Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.data.entities;

import com.denimgroup.threadfix.views.AllViews;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.Index;

import javax.persistence.*;
import javax.validation.constraints.Size;
import java.util.*;

import static com.denimgroup.threadfix.CollectionUtils.list;
import static com.denimgroup.threadfix.CollectionUtils.map;

@Entity
@Table(name = "Scan")
public class Scan extends BaseEntity implements Iterable {

	private static final long serialVersionUID = -8461350611851383656L;

	private ApplicationChannel applicationChannel;
	private Calendar importTime;
	private Application application;
	private Integer numberClosedVulnerabilities=0;
	private Integer numberNewVulnerabilities=0;
	private Integer numberOldVulnerabilities=0;
	private Integer numberResurfacedVulnerabilities=0;
	private Integer numberTotalVulnerabilities=0;
	private Integer numberHiddenVulnerabilities=0;
	private Integer numberRepeatResults=0;
	private Integer numberRepeatFindings=0;
	
	private Long numberInfoVulnerabilities = 0L, numberLowVulnerabilities = 0L,
			numberMediumVulnerabilities = 0L, numberHighVulnerabilities = 0L,
            numberCriticalVulnerabilities = 0L;

	private Long numberAuditedInfoVulnerabilities = 0L, numberAuditedLowVulnerabilities = 0L,
			numberAuditedMediumVulnerabilities = 0L, numberAuditedHighVulnerabilities = 0L,
			numberAuditedCriticalVulnerabilities = 0L, numberTotalAuditedVulnerabilities = 0L;

	private Boolean lockedMetadata = false;

    private User user;

    private List       scanRepeatFindingMaps;
    private List scanReopenVulnerabilityMaps;
    private List  scanCloseVulnerabilityMaps;

    // TODO probably rename this - it's for the graphs
    private Integer numberOldVulnerabilitiesInitiallyFromThisChannel;

    private List findings;

    private List events;

	private Integer numWithoutChannelVulns    = null;
    private Integer numWithoutGenericMappings = null;

    private Integer totalNumberSkippedResults       = null;
    private Integer totalNumberFindingsMergedInScan = null;

    // These are for determining what type of scanner was used
    private static final List DYNAMIC_TYPES = list(
            ScannerType.ACUNETIX_WVS.getDisplayName(),
            ScannerType.APPSCAN_ENTERPRISE.getDisplayName(),
            ScannerType.ARACHNI.getDisplayName(),
            ScannerType.BURPSUITE.getDisplayName(),
            ScannerType.NESSUS.getDisplayName(),
            ScannerType.NETSPARKER.getDisplayName(),
            ScannerType.APP_SPIDER.getDisplayName(),
            ScannerType.SKIPFISH.getDisplayName(),
            ScannerType.W3AF.getDisplayName(),
            ScannerType.WEBINSPECT.getDisplayName(),
            ScannerType.ZAPROXY.getDisplayName(),
            ScannerType.QUALYSGUARD_WAS.getDisplayName(),
            ScannerType.APPSCAN_DYNAMIC.getDisplayName(),
            ScannerType.CENZIC_HAILSTORM.getDisplayName());

    private static final List STATIC_TYPES = list(
            ScannerType.APPSCAN_SOURCE.getDisplayName(),
            ScannerType.FINDBUGS.getDisplayName(),
            ScannerType.FORTIFY.getDisplayName(),
            ScannerType.VERACODE.getDisplayName(),
            ScannerType.CAT_NET.getDisplayName(),
            ScannerType.BRAKEMAN.getDisplayName(),
            ScannerType.CHECKMARX.getDisplayName());
    private static final List MIXED_TYPES  = Arrays.asList(ScannerType.SENTINEL.getDisplayName());
    private static final String       DYNAMIC      = "Dynamic", STATIC = "Static", MIXED = "Mixed";

    @Size(max = 255, message = "{errors.maxlength} 255.")
    private String filePathRoot;
    @Size(max = 255, message = "{errors.maxlength} 255.")
    private String urlPathRoot;
    @Size(max = 32, message = "{errors.maxlength} 32.")
    private String fileName;
    private List originalFileNames;
	private List savedFileNames;

	private boolean isDownloadable;

    @ManyToOne(cascade = CascadeType.MERGE)
    @JoinColumn(name = "applicationChannelId")
    @JsonIgnore
    public ApplicationChannel getApplicationChannel() {
        return applicationChannel;
    }

    public void setApplicationChannel(ApplicationChannel applicationChannel) {
        this.applicationChannel = applicationChannel;
    }

    @Temporal(TemporalType.TIMESTAMP)
    @JsonView({AllViews.TableRow.class, AllViews.FormInfo.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class, AllViews.RestViewScanList.class})
	@Index(name="importTime")
    public Calendar getImportTime() {
        return importTime;
    }

    public void setImportTime(Calendar importTime) {
        this.importTime = importTime;
    }

    @ManyToOne
    @JoinColumn(name = "applicationId")
    @JsonIgnore
    public Application getApplication() {
        return application;
    }

    public void setApplication(Application application) {
        this.application = application;
    }

    @ManyToOne
    @JoinColumn(nullable = true, name = "userId")
    @JsonIgnore
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @OneToMany(mappedBy = "scan", cascade = CascadeType.ALL)
    @JsonView(AllViews.RestViewScan2_1.class)
    public List getFindings() {
        return findings;
    }

    public void setFindings(List findings) {
        this.findings = findings;
    }

	@OneToMany(mappedBy = "scan")
	@OrderBy("date ASC")
	@JsonIgnore
	public List getEvents() {
		return events;
	}

	public void setEvents(List events) {
		this.events = events;
	}

    @Column(length = 256)
    public String getFilePathRoot() {
        return filePathRoot;
    }

    public void setFilePathRoot(String filePathRoot) {
        this.filePathRoot = filePathRoot;
    }

    @Column(length = 256)
    public String getUrlPathRoot() {
        return urlPathRoot;
    }

    public void setUrlPathRoot(String urlPathRoot) {
        this.urlPathRoot = urlPathRoot;
    }

    @JsonView({AllViews.FormInfo.class, AllViews.RestViewScanStatistic.class})
    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

	@CollectionTable(name="ScanFileNames", joinColumns=@JoinColumn(name="scanId"))
	@CollectionOfElements // for sonar
	@ElementCollection
	@JsonView({AllViews.FormInfo.class, AllViews.TableRow.class, AllViews.RestViewScanStatistic.class})
    public List getOriginalFileNames() {
        return originalFileNames;
    }

    public void setOriginalFileNames(List originalFileNames) {
        this.originalFileNames = originalFileNames;
    }

	@CollectionTable(name="ScanSavedFileNames", joinColumns=@JoinColumn(name="scanId"))
	@CollectionOfElements
	@ElementCollection
	@JsonView({AllViews.FormInfo.class, AllViews.RestViewScanStatistic.class})
	public List getSavedFileNames() {
		if ((savedFileNames == null || savedFileNames.size() == 0) && fileName != null)
			savedFileNames = list(fileName);

		return savedFileNames;
	}

	public void setSavedFileNames(List savedFileNames) {
		this.savedFileNames = savedFileNames;
	}

	@OneToMany(mappedBy = "scan", cascade = CascadeType.ALL)
    @JsonIgnore
    public List getScanRepeatFindingMaps() {
        return scanRepeatFindingMaps;
    }

    public void setScanRepeatFindingMaps(List scanRepeatFindingMaps) {
        this.scanRepeatFindingMaps = scanRepeatFindingMaps;
    }

    @JsonIgnore
    @OneToMany(mappedBy = "scan", cascade = CascadeType.ALL)
	public List getScanReopenVulnerabilityMaps() {
		return scanReopenVulnerabilityMaps;
	}

	public void setScanReopenVulnerabilityMaps(List ScanReopenVulnerabilityMaps) {
		this.scanReopenVulnerabilityMaps = ScanReopenVulnerabilityMaps;
	}

    @JsonIgnore
	@OneToMany(mappedBy = "scan", cascade = CascadeType.ALL)
	public List getScanCloseVulnerabilityMaps() {
		return scanCloseVulnerabilityMaps;
	}

	public void setScanCloseVulnerabilityMaps(List ScanCloseVulnerabilityMaps) {
		this.scanCloseVulnerabilityMaps = ScanCloseVulnerabilityMaps;
	}

	@Column
    @JsonView({AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class})
	public Integer getNumberClosedVulnerabilities() {
		return numberClosedVulnerabilities;
	}

	public void setNumberClosedVulnerabilities(Integer numberClosedVulnerabilities) {
		this.numberClosedVulnerabilities = numberClosedVulnerabilities;
	}

    @JsonView({AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class})
	@Column
	public Integer getNumberNewVulnerabilities() {
		return numberNewVulnerabilities;
	}

	public void setNumberNewVulnerabilities(Integer numberNewVulnerabilities) {
		this.numberNewVulnerabilities = numberNewVulnerabilities;
	}

    @JsonView({AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class})
	@Column
	public Integer getNumberOldVulnerabilities() {
		return numberOldVulnerabilities;
	}

	public void setNumberOldVulnerabilities(Integer numberOldVulnerabilities) {
		this.numberOldVulnerabilities = numberOldVulnerabilities;
	}

	/**
	 * Keeping track of this information allows us to produce scans without extensive recalculation,
	 * because we don't have to track down which application channel we should count a vulnerability for.
	 *
	 * This may lead to a small bug if a vuln is opened in one channel, then found in another and
	 * subsequently closed there. This needs to be looked into.
	 * @return
	 */
	@Column
    @JsonView(AllViews.RestViewScanStatistic.class)
	public Integer getNumberOldVulnerabilitiesInitiallyFromThisChannel() {
		return numberOldVulnerabilitiesInitiallyFromThisChannel;
	}

	public void setNumberOldVulnerabilitiesInitiallyFromThisChannel(
			Integer numberOldVulnerabilitiesInitiallyFromThisChannel) {
		this.numberOldVulnerabilitiesInitiallyFromThisChannel = numberOldVulnerabilitiesInitiallyFromThisChannel;
	}

	@Column
    @JsonView({AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class})
    public Integer getNumberResurfacedVulnerabilities() {
		return numberResurfacedVulnerabilities;
	}

	public void setNumberResurfacedVulnerabilities(Integer numberResurfacedVulnerabilities) {
		this.numberResurfacedVulnerabilities = numberResurfacedVulnerabilities;
	}

	@Column
    @JsonView({ AllViews.TableRow.class, AllViews.FormInfo.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
    public Integer getNumberTotalVulnerabilities() {
		return numberTotalVulnerabilities;
	}

	public void setNumberTotalVulnerabilities(Integer numberTotalVulnerabilities) {
		this.numberTotalVulnerabilities = numberTotalVulnerabilities;
	}

    @JsonView({ AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
    @Column
	public Integer getNumberRepeatFindings() {
		return numberRepeatFindings;
	}

	public void setNumberRepeatFindings(Integer numberRepeatFindings) {
		this.numberRepeatFindings = numberRepeatFindings;
	}

    @JsonView({ AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
    @Column
	public Integer getNumberRepeatResults() {
		return numberRepeatResults;
	}

	public void setNumberRepeatResults(Integer numberRepeatResults) {
		this.numberRepeatResults = numberRepeatResults;
	}

	@Transient
	public Integer getNumWithoutGenericMappings() {
		return numWithoutGenericMappings;
	}

	public void setNumWithoutGenericMappings(Integer numWithoutGenericMappings) {
		this.numWithoutGenericMappings = numWithoutGenericMappings;
	}

	@Transient
	public Integer getNumWithoutChannelVulns() {
		return numWithoutChannelVulns;
	}

	public void setNumWithoutChannelVulns(Integer numWithoutChannelVulns) {
		this.numWithoutChannelVulns = numWithoutChannelVulns;
	}

	@Transient
	public Integer getTotalNumberSkippedResults() {
		return totalNumberSkippedResults;
	}

	public void setTotalNumberSkippedResults(Integer totalNumberSkippedResults) {
		this.totalNumberSkippedResults = totalNumberSkippedResults;
	}

	@Transient
	public Integer getTotalNumberFindingsMergedInScan() {
		return totalNumberFindingsMergedInScan;
	}

	public void setTotalNumberFindingsMergedInScan(
			Integer totalNumberFindingsMergedInScan) {
		this.totalNumberFindingsMergedInScan = totalNumberFindingsMergedInScan;
	}

	// These two functions establish the order the integers come in and this
	// order should not be changed.
	@Transient
	@JsonIgnore
	public List getReportList() {
		List integerList = new ArrayList();
		integerList.add(getId());
		integerList.add(getNumberTotalVulnerabilities());
		integerList.add(getNumberNewVulnerabilities());
		integerList.add(getNumberOldVulnerabilities());
		integerList.add(getNumberResurfacedVulnerabilities());
		integerList.add(getNumberClosedVulnerabilities());
		return integerList;
	}

	@JsonIgnore
	public static ScanTimeComparator getTimeComparator() {
		return new ScanTimeComparator();
	}

    @Override
    public Iterator iterator() {
        return findings == null ? new ArrayList().iterator() : findings.iterator();
    }

    static public class ScanTimeComparator implements Comparator {

		@Override
		public int compare(Scan scan1, Scan scan2){
			Calendar scan1Time = scan1.getImportTime();
			Calendar scan2Time = scan2.getImportTime();

			if (scan1Time == null || scan2Time == null) {
				return 0;
			}

			return scan1Time.compareTo(scan2Time);
		}
	}

	@Column
    @JsonView({ AllViews.TableRow.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
	public Long getNumberInfoVulnerabilities() {
		return numberInfoVulnerabilities;
	}

	public void setNumberInfoVulnerabilities(Long numberInfoVulnerabilities) {
		this.numberInfoVulnerabilities = numberInfoVulnerabilities;
	}

	@Column
    @JsonView({ AllViews.TableRow.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
	public Long getNumberLowVulnerabilities() {
		return numberLowVulnerabilities;
	}

	public void setNumberLowVulnerabilities(Long numberLowVulnerabilities) {
		this.numberLowVulnerabilities = numberLowVulnerabilities;
	}

	@Column
    @JsonView({ AllViews.TableRow.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
	public Long getNumberMediumVulnerabilities() {
		return numberMediumVulnerabilities;
	}

	public void setNumberMediumVulnerabilities(Long numberMediumVulnerabilities) {
		this.numberMediumVulnerabilities = numberMediumVulnerabilities;
	}

	@Column
    @JsonView({ AllViews.TableRow.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
	public Long getNumberHighVulnerabilities() {
		return numberHighVulnerabilities;
	}

	public void setNumberHighVulnerabilities(Long numberHighVulnerabilities) {
		this.numberHighVulnerabilities = numberHighVulnerabilities;
	}

	@Column
    @JsonView({ AllViews.TableRow.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class })
    public Long getNumberCriticalVulnerabilities() {
		return numberCriticalVulnerabilities;
	}

	public void setNumberCriticalVulnerabilities(
			Long numberCriticalVulnerabilities) {
		this.numberCriticalVulnerabilities = numberCriticalVulnerabilities;
	}

	@Column
    @JsonView({AllViews.TableRow.class, AllViews.FormInfo.class, AllViews.RestViewScanStatistic.class})
    public Integer getNumberHiddenVulnerabilities() {
		if (numberHiddenVulnerabilities == null) {
			return 0;
		} else {
			return numberHiddenVulnerabilities;
		}
	}

	public void setNumberHiddenVulnerabilities(
			Integer numberHiddenVulnerabilities) {
		this.numberHiddenVulnerabilities = numberHiddenVulnerabilities;
	}

	@Transient
	public String getScannerType() {
		if (getApplicationChannel() != null && getApplicationChannel().getChannelType() != null
				&& getApplicationChannel().getChannelType().getName() != null) {
			String scannerName = getApplicationChannel().getChannelType().getName();
			if (DYNAMIC_TYPES.contains(scannerName)) {
				return DYNAMIC;
			} else if (STATIC_TYPES.contains(scannerName)) {
				return STATIC;
			} else if (MIXED_TYPES.contains(scannerName)) {
				return MIXED;
			}
		}

		return null;
	}

    @Transient
	public boolean isStatic() {
		return STATIC.equals(getScannerType());
	}

    // This should get serialized.
    @JsonView({AllViews.TableRow.class})
    @Transient
    private String getType() {
        String type = getApplicationChannel().getChannelType().getName();
        if (ChannelType.DYNAMIC_TYPES.contains(type)) {
            return ChannelType.DYNAMIC;
        } else if (ChannelType.STATIC_TYPES.contains(type)) {
            return ChannelType.STATIC;
        } else {
            return ChannelType.MIXED;
        }
    }

    // TODO figure out JSON serialization better
    @JsonView({ AllViews.TableRow.class, AllViews.RestViewScanStatistic.class})
    @Transient
    private Map getApp() {
        Application app = getApplication();

        Map map = new HashMap();
        map.put("id", app.getId());
        map.put("name", app.getName());

        return map;
    }

    @JsonView({AllViews.TableRow.class, AllViews.FormInfo.class, AllViews.RestView2_1.class, AllViews.RestViewScanStatistic.class, AllViews.RestViewScanList.class })
    @Transient
    private String getScannerName() {
        return getApplicationChannel().getChannelType().getName();
    }

    @JsonView({AllViews.TableRow.class, AllViews.RestViewScanStatistic.class})
    @Transient
    private Map getTeam() {
        Organization team = getApplication().getOrganization();

        Map map = new HashMap();
        map.put("id", team.getId());
        map.put("name", team.getName());

        return map;
    }

    @JsonView({AllViews.TableRow.class, AllViews.RestViewScanStatistic.class})
    @Transient
    private Integer getApplicationChannelId(){
        return (getApplicationChannel()==null ? null : getApplicationChannel().getId());
    }

    @JsonView(AllViews.RestViewScanStatistic.class)
    @Transient
    private List getApplicationTags(){
        List maps = list();
        List tags = getApplication().getTags();
        for (Tag tag: tags) {
            Map map = map();
            map.put("id", tag.getId());
            map.put("name", tag.getName());
            maps.add(map);
        }
        return maps;
    }

	// Audited stuff
	@Column
	public Long getNumberAuditedInfoVulnerabilities() {
		return numberAuditedInfoVulnerabilities;
	}

	public void setNumberAuditedInfoVulnerabilities(Long numberAuditedInfoVulnerabilities) {
		this.numberAuditedInfoVulnerabilities = numberAuditedInfoVulnerabilities;
	}

	@Column
	public Long getNumberAuditedLowVulnerabilities() {
		return numberAuditedLowVulnerabilities;
	}

	public void setNumberAuditedLowVulnerabilities(Long numberAuditedLowVulnerabilities) {
		this.numberAuditedLowVulnerabilities = numberAuditedLowVulnerabilities;
	}

	@Column
	public Long getNumberAuditedMediumVulnerabilities() {
		return numberAuditedMediumVulnerabilities;
	}

	public void setNumberAuditedMediumVulnerabilities(Long numberAuditedMediumVulnerabilities) {
		this.numberAuditedMediumVulnerabilities = numberAuditedMediumVulnerabilities;
	}

	@Column
	public Long getNumberAuditedHighVulnerabilities() {
		return numberAuditedHighVulnerabilities;
	}

	public void setNumberAuditedHighVulnerabilities(Long numberAuditedHighVulnerabilities) {
		this.numberAuditedHighVulnerabilities = numberAuditedHighVulnerabilities;
	}

	@Column
	public Long getNumberAuditedCriticalVulnerabilities() {
		return numberAuditedCriticalVulnerabilities;
	}

	public void setNumberAuditedCriticalVulnerabilities(Long numberAuditedCriticalVulnerabilities) {
		this.numberAuditedCriticalVulnerabilities = numberAuditedCriticalVulnerabilities;
	}

	@Column
	public Long getNumberTotalAuditedVulnerabilities() {
		return numberTotalAuditedVulnerabilities;
	}

	public void setNumberTotalAuditedVulnerabilities(Long numberTotalAuditedVulnerabilities) {
		this.numberTotalAuditedVulnerabilities = numberTotalAuditedVulnerabilities;
	}

	// this can be used to suppress changes to these scans by other parts of ThreadFix
	@Column
	public Boolean getLockedMetadata() {
		return lockedMetadata;
	}

	public void setLockedMetadata(Boolean lockedMetadata) {
		this.lockedMetadata = lockedMetadata;
	}

	@Transient
	@JsonView(AllViews.FormInfo.class)
	public boolean isDownloadable() {
		return isDownloadable;
	}

	public void setDownloadable(boolean isDownloadable) {
		this.isDownloadable = isDownloadable;
	}

    @Transient
    public List getUnmappedFindings(){
        List unMappedFindings = list();

        if(findings != null && !findings.isEmpty()){
            for(Finding finding : findings){
                if(finding.getVulnerability() == null){
                    unMappedFindings.add(finding);
                }
            }
        }

        return unMappedFindings;
    }

	@Override
	public String toString() {
		String type = applicationChannel == null ? "no type" : applicationChannel.getChannelType().getName();

		return type + " (" + (findings == null ? 0 : findings.size()) + " results)";

	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy