////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2014 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.dao.hibernate;
import com.denimgroup.threadfix.CollectionUtils;
import com.denimgroup.threadfix.data.dao.VulnerabilityDao;
import com.denimgroup.threadfix.data.entities.Application;
import com.denimgroup.threadfix.data.entities.DeletedVulnerability;
import com.denimgroup.threadfix.data.entities.Finding;
import com.denimgroup.threadfix.data.entities.Vulnerability;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
/**
* Hibernate Vulnerability DAO implementation. Most basic methods are
* implemented in the AbstractGenericDao
*
* @author mcollins, dwolf
*/
@Repository
public class HibernateVulnerabilityDao implements VulnerabilityDao {
private SessionFactory sessionFactory;
@Autowired
public HibernateVulnerabilityDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
@SuppressWarnings("unchecked")
public List<Vulnerability> retrieveAllByApplication(int applicationId) {
return sessionFactory.getCurrentSession()
.createCriteria(Vulnerability.class)
.createAlias("application", "app")
.add(Restrictions.eq("app.id", applicationId))
.list();
}
@Override
@SuppressWarnings("unchecked")
public List<Vulnerability> retrieveAllByGenericVulnerabilityAndApp(
Vulnerability vulnerability) {
return sessionFactory
.getCurrentSession()
.createQuery(
"from Vulnerability vuln where vuln.application = :appId "
+ "and vuln.genericVulnerability = :gvId and vuln.expired = :false")
.setInteger("gvId",
vulnerability.getGenericVulnerability().getId())
.setInteger("appId", vulnerability.getApplication().getId())
.setBoolean("false", false).list();
}
@SuppressWarnings("unchecked")
@Override
public List<Vulnerability> retrieveByApplicationIdList(List<Integer> applicationIdList) {
return sessionFactory.getCurrentSession()
.createQuery("from Vulnerability vulnerability " +
"where vulnerability.hidden = false and vulnerability.application.id in (:idList)")
.setParameterList("idList", applicationIdList).list();
}
@Override
public List<Map<String, Object>> retrieveMapByApplicationIdList(List<Integer> applicationIdList) {
return (List<Map<String, Object>>) sessionFactory.getCurrentSession().createQuery(
"select new map( " +
"vulnerability.genericSeverity.name as severity, " +
"vulnerability.genericVulnerability.name as genericVulnName, " +
"vulnerability.openTime as importTime, " +
"vulnerability.closeTime as closeTime, " +
"vulnerability.active as active, " +
"vulnerability.hidden as hidden, " +
"vulnerability.isFalsePositive as isFalsePositive, " +
"vulnerability.application.name as appName, " +
"vulnerability.application.id as appId, " +
"vulnerability.application.organization.name as teamName, " +
"vulnerability.application.organization.id as teamId " +
") " +
"from Vulnerability vulnerability " +
"where vulnerability.hidden = false " +
"and vulnerability.application.id in (:idList) " +
// "and vulnerability.active = true " +
"order by vulnerability.genericSeverity.name, vulnerability.application.name")
.setParameterList("idList", applicationIdList)
.list();
}
@Override
public Vulnerability retrieveById(int id) {
Vulnerability vuln = (Vulnerability) sessionFactory.getCurrentSession()
.get(Vulnerability.class, id);
if (vuln != null && !vuln.isExpired()) {
return vuln;
} else {
assert false : "Attempted to retrieve invalid vulnerability id.";
return null;
}
}
@Override
@SuppressWarnings("unchecked")
public List<Vulnerability> retrieveSimilarHashes(Vulnerability vulnerability) {
return sessionFactory
.getCurrentSession()
.createQuery(
"from Vulnerability vuln where vuln.application = :appId "
+ "and vuln.expired = :false "
+ "and (vuln.locationVariableHash = :lvHash or "
+ "vuln.locationHash = :lHash or vuln.variableHash = :vHash)")
.setString("lvHash", vulnerability.getLocationVariableHash())
.setString("lHash", vulnerability.getLocationHash())
.setString("vHash", vulnerability.getVariableHash())
.setInteger("appId", vulnerability.getApplication().getId())
.setBoolean("false", false).list();
}
@Override
public void saveOrUpdate(Vulnerability vulnerability) {
sessionFactory.getCurrentSession().saveOrUpdate(vulnerability);
}
@Override
public void delete(Vulnerability vulnerability) {
sessionFactory.getCurrentSession().save(new DeletedVulnerability(vulnerability));
sessionFactory.getCurrentSession().delete(vulnerability);
}
/**
* I would feel bad about having so much logic here but the alternatives are
* passing in a query string which is a terrible idea or
* having a ton of methods which also isn't any good.
*/
@SuppressWarnings("unchecked")
@Override
public List<Vulnerability> retrieveActiveByAppIdAndPage(int appId, int page,
int sort, int field, Integer cwe, String description, String severity,
String path, String param, boolean open, boolean falsePositive, boolean hidden) {
String[] headers = new String[] { "",
"vuln.name",
"severity.intValue",
"surface.path",
"surface.parameter" };
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Vulnerability.class);
if (hidden) {
criteria.add(Restrictions.eq("hidden", true));
} else {
criteria.add(Restrictions.eq("hidden", false));
if (!open && falsePositive) {
criteria.add(Restrictions.eq("isFalsePositive", true));
} else {
criteria.add(Restrictions.eq("active", open))
.add(Restrictions.eq("hidden", false))
.add(Restrictions.eq("isFalsePositive", falsePositive));
}
}
criteria.add( Restrictions.eq("application.id", appId))
.createAlias("genericSeverity", "severity")
.createAlias("genericVulnerability", "vuln")
.createAlias("surfaceLocation", "surface")
.setFirstResult((page - 1) * 100)
.setMaxResults(100);
// Add Filtering restrictions
if (description != null) {
criteria.add(Restrictions.like("vuln.name", "%" + description + "%").ignoreCase());
}
if (severity != null) {
criteria.add(Restrictions.like("severity.name", "%" + severity + "%").ignoreCase());
}
if (path != null) {
criteria.add(Restrictions.like("surface.path", "%" + path + "%").ignoreCase());
}
if (param != null) {
criteria.add(Restrictions.like("surface.parameter", "%" + param + "%").ignoreCase());
}
if (cwe != null) {
criteria.add(Restrictions.eq("vuln.id", cwe));
}
// Add Ordering
if (sort != 1 && sort != 2 ||
field <= 0 || field > headers.length) {
criteria.addOrder( Order.desc("severity.intValue") )
.addOrder( Order.asc("vuln.name") )
.addOrder( Order.asc("surface.path") )
.addOrder( Order.asc("surface.parameter") );
} else {
String item = headers[field];
if (sort == 1) {
criteria.addOrder( Order.asc(item));
} else if (sort == 2) {
criteria.addOrder( Order.desc(item));
}
}
return criteria.list();
}
@Override
public long getVulnCountWithFilters(Integer appId, String description,
String severity, String path, String param, Integer cweInteger,
boolean open, boolean falsePositive, boolean hidden) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
Vulnerability.class);
if (hidden) {
criteria.add(Restrictions.eq("hidden", true));
} else {
criteria.add(Restrictions.eq("hidden", false));
if (!open && falsePositive) {
criteria.add(Restrictions.eq("isFalsePositive", true));
} else {
criteria.add(Restrictions.eq("active", open))
.add(Restrictions.eq("hidden", false))
.add(Restrictions.eq("isFalsePositive", falsePositive));
}
}
criteria.createAlias("genericVulnerability", "vuln")
.add(Restrictions.eq("application.id", appId));
// Add Filtering restrictions
if (description != null) {
criteria.add(Restrictions
.like("vuln.name", "%" + description + "%").ignoreCase());
}
if (severity != null) {
criteria.createAlias("genericSeverity", "severity")
.add(Restrictions.like("severity.name",
"%" + severity + "%").ignoreCase());
}
if (path != null) {
criteria.createAlias("surfaceLocation", "surface")
.add(Restrictions.like("surface.path", "%" + path + "%")
.ignoreCase());
}
if (cweInteger != null) {
criteria.add(Restrictions.eq("vuln.id", cweInteger));
}
if (param != null) {
criteria.createAlias("surfaceLocation", "surface")
.add(Restrictions.like("surface.parameter",
"%" + param + "%").ignoreCase());
}
return (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
}
@Override
public long getVulnCount(Integer appId, boolean open) {
return (Long) sessionFactory
.getCurrentSession()
.createCriteria(Vulnerability.class)
.add(Restrictions.eq("active", open))
.add(Restrictions.eq("hidden", false))
.add(Restrictions.eq("isFalsePositive", false))
.add(Restrictions.eq("application.id", appId))
.setProjection(Projections.rowCount())
.uniqueResult();
}
@Override
@SuppressWarnings("unchecked")
public List<Vulnerability> getFalsePositiveVulnCount(Application application,
boolean value) {
return sessionFactory
.getCurrentSession()
.createQuery("from Vulnerability vuln where vuln.application = :appId "
+ "and vuln.isFalsePositive = :fp")
.setBoolean("fp", value)
.setInteger("appId", application.getId()).list();
}
@Override
public void evict(Finding finding) {
sessionFactory.getCurrentSession().evict(finding);
}
@SuppressWarnings("unchecked")
@Override
public void markAllClosed(List<Integer> vulnerabilityIds) {
List<Vulnerability> vulns = sessionFactory.getCurrentSession()
.createCriteria(Vulnerability.class)
.add(Restrictions.in("id", vulnerabilityIds))
.list();
for (Vulnerability vuln : vulns) {
if (vuln != null && vuln.isActive()) {
vuln.setActive(false);
vuln.setCloseTime(Calendar.getInstance());
vuln.setFoundByScanner(false);
saveOrUpdate(vuln);
}
}
}
@SuppressWarnings("unchecked")
@Override
public void markAllOpen(List<Integer> vulnerabilityIds) {
List<Vulnerability> vulns = sessionFactory.getCurrentSession()
.createCriteria(Vulnerability.class)
.add(Restrictions.in("id", vulnerabilityIds))
.list();
for (Vulnerability vuln : vulns) {
if (vuln != null && !vuln.isActive()) {
vuln.setActive(true);
vuln.setFoundByScanner(true);
saveOrUpdate(vuln);
}
}
}
@Override
public boolean activeVulnerabilitiesExist() {
return sessionFactory
.getCurrentSession()
.createCriteria(Vulnerability.class)
.createAlias("application", "app")
.add( Restrictions.eq("app.active", true))
.setMaxResults(1)
.uniqueResult() != null;
}
@SuppressWarnings("unchecked")
@Override
public List<Integer> getTopTenVulnTypes(List<Integer> applicationIdList) {
return sessionFactory
.getCurrentSession()
.createQuery(
"select vuln.genericVulnerability.id " +
"from Vulnerability vuln " +
"where vuln.active = true " +
"and vuln.hidden = false " +
"and vuln.application.id in (:ids) " +
"group by vuln.genericVulnerability.id " +
"order by count(vuln.genericVulnerability.id) desc")
.setParameterList("ids", applicationIdList)
.setMaxResults(10)
.list();
}
@Override
public List<Object[]> getTopVulnsInfo(List<Integer> applicationIdList, List<Integer> vulnIds) {
List<Object[]> results = sessionFactory
.getCurrentSession()
.createQuery(
"select vulnerability.genericVulnerability.id as genericVulnId, count(vulnerability) as vulnCount " +
"from Vulnerability vulnerability " +
"where vulnerability.active = true " +
"and vulnerability.application.active = true " +
"and vulnerability.isFalsePositive = false " +
"and vulnerability.application.id in (:appIds) " +
"and vulnerability.genericVulnerability.id in (:vulnIds) " +
"group by vulnerability.genericVulnerability.id " +
"order by count(vulnerability) desc")
.setParameterList("appIds", applicationIdList)
.setParameterList("vulnIds", vulnIds)
.list();
if (results == null)
results = CollectionUtils.list();
return results;
}
public Criteria getActiveVulnCriteria() {
return sessionFactory.getCurrentSession()
.createCriteria(Vulnerability.class)
.add(Restrictions.eq("active", true))
.add(Restrictions.eq("hidden", false))
.add(Restrictions.eq("isFalsePositive", false));
}
}