//////////////////////////////////////////////////////////////////////// // // Copyright (c) 2009-2013 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 java.util.List; 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 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; /** * Hibernate Vulnerability DAO implementation. Most basic methods are * implemented in the AbstractGenericDao * * @author mcollins, dwolf * @see AbstractGenericDao */ @Repository public class HibernateVulnerabilityDao implements VulnerabilityDao { private SessionFactory sessionFactory; @Autowired public HibernateVulnerabilityDao(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } @Override @SuppressWarnings("unchecked") public List<Vulnerability> retrieveAll() { return sessionFactory .getCurrentSession() .createQuery( "from Vulnerability vulnerability " + "where vulnerability.expired = :false order by vulnerability.id ") .setBoolean("false", false).list(); } @Override @SuppressWarnings("unchecked") public List<Vulnerability> retrieveAllActive() { return sessionFactory .getCurrentSession() .createQuery( "from Vulnerability vulnerability " + "where vulnerability.active = :true " + "and vulnerability.expired = :false") .setBoolean("false", false).setBoolean("true", true).list(); } @Override @SuppressWarnings("unchecked") public List<Vulnerability> retrieveAllActiveByApplication(int applicationId) { return sessionFactory .getCurrentSession() .createQuery( "from Vulnerability vuln where vuln.application = :appId " + "and vuln.active = :true and vuln.expired = :false") .setInteger("appId", applicationId).setBoolean("true", true) .setBoolean("false", false).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(); } @Override @SuppressWarnings("unchecked") public List<Vulnerability> retrieveAllInactive() { return sessionFactory .getCurrentSession() .createQuery( "from Vulnerability vulnerability " + "where vulnerability.active = :false " + "and vulnerability.expired = :false") .setBoolean("false", false).list(); } @SuppressWarnings("unchecked") @Override public List<Vulnerability> retrieveByApplicationIdList(List<Integer> applicationIdList) { return sessionFactory.getCurrentSession() .createQuery("from Vulnerability vulnerability " + "where vulnerability.application.id in (:idList)") .setParameterList("idList", applicationIdList).list(); } @Override public Vulnerability retrieveByHashAndApp(String hash, int applicationId) { // the function defaults to locationVulnerabilityHash (to avoid a super // long name) return (Vulnerability) sessionFactory .getCurrentSession() .createQuery( "from Vulnerability vulnerability " + "where vulnerability.locationVariableHash = :hash " + "and vulnerability.application = :appId " + "and vulnerability.expired = :false") .setString("hash", hash).setInteger("appId", applicationId) .setBoolean("false", false).uniqueResult(); } @Override public Vulnerability retrieveById(int id) { Vulnerability vuln = (Vulnerability) sessionFactory.getCurrentSession() .get(Vulnerability.class, id); if (vuln != null && !vuln.isExpired()) { return vuln; } else { return null; } } @SuppressWarnings("unchecked") @Override public List<Vulnerability> retrieveByLocationHashAndApp(String hash, int applicationId) { return sessionFactory .getCurrentSession() .createQuery( "from Vulnerability vulnerability " + "where vulnerability.locationHash = :hash " + "and vulnerability.application = :appId " + "and vulnerability.expired = :false") .setString("hash", hash).setInteger("appId", applicationId) .setBoolean("false", false).list(); } @SuppressWarnings("unchecked") @Override public List<Vulnerability> retrieveByVariableHashAndApp(String hash, int applicationId) { return sessionFactory .getCurrentSession() .createQuery( "from Vulnerability vulnerability " + "where vulnerability.variableHash = :hash " + "and vulnerability.application = :appId " + "and vulnerability.expired = :false") .setString("hash", hash).setInteger("appId", applicationId) .setBoolean("false", false).list(); } @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) { String[] headers = new String[] { "", "vuln.name", "severity.intValue", "surface.path", "surface.parameter" }; Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Vulnerability.class); if (!open && falsePositive) { criteria.add(Restrictions.eq("isFalsePositive", true)); } else { criteria.add(Restrictions.eq("active", open)) .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) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria( Vulnerability.class); if (!open && falsePositive) { criteria.add(Restrictions.eq("isFalsePositive", true)); } else { criteria.add(Restrictions.eq("active", open)) .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.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("isFalsePositive", false)) .add(Restrictions.eq("application.id", appId)) .setProjection(Projections.rowCount()) .uniqueResult(); } @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.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 ((Vulnerability) 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 (List<Integer>) sessionFactory .getCurrentSession() .createQuery( "select vuln.genericVulnerability.id " + "from Vulnerability vuln " + "where vuln.application.id in (:ids) " + "group by vuln.genericVulnerability.id " + "order by count(vuln.genericVulnerability.id) desc") .setParameterList("ids", applicationIdList) .setMaxResults(10) .list(); } }