diff --git a/src/com/fuse/actions/assessment/AddVulnerability.java b/src/com/fuse/actions/assessment/AddVulnerability.java index 1619da2a..c77d2298 100644 --- a/src/com/fuse/actions/assessment/AddVulnerability.java +++ b/src/com/fuse/actions/assessment/AddVulnerability.java @@ -283,7 +283,13 @@ else if (tmpRec == null) html += "" + StringEscapeUtils.escapeHtml4(vuln.getName()) +"
"; html += "" + (vuln.getDefaultVuln() == null ? "" : StringEscapeUtils.escapeHtml4(vuln.getDefaultVuln().getCategory().getName())) + "
"; - html += "" + vuln.getOverallStr() + ""; + html += "" + vuln.getOverallStr() + ""; + + if(this.getSectionsEnabled()) { + html += "
" + vuln.getSectionPretty() + ""; + }else { + html += ""; + } html += " "; html += ""; diff --git a/src/com/fuse/actions/remediation/OpenVulns.java b/src/com/fuse/actions/remediation/OpenVulns.java index 1e3c9204..cae78f62 100644 --- a/src/com/fuse/actions/remediation/OpenVulns.java +++ b/src/com/fuse/actions/remediation/OpenVulns.java @@ -126,9 +126,10 @@ public String execute() { } if (this.closed.equals("") && !this.open.equals("")) { - // open = anything whose status does not say "Closed" + // open = not closed in production. Items closed in dev + // or staging are still open in prod, so they stay here. newMongo += " 'vuln.closed' : {'$exists':false}, " - + " 'vuln.status' : {'$nin': " + VulnerabilityQueries.closedStatusArray() + "}, "; + + " 'vuln.status' : {'$ne': \"" + Vulnerability.StatusClosed + "\"}, "; } else if (!this.closed.equals("") && this.open.equals("")) { // show only closed items newMongo += " '$or' : [ {'vuln.closed' : {'$exists':true}}, " + "{'vuln.status' : {'$in': " + VulnerabilityQueries.closedStatusArray() + "}} ], "; diff --git a/src/com/fuse/dao/Vulnerability.java b/src/com/fuse/dao/Vulnerability.java index 7af7d5f5..abb0449f 100644 --- a/src/com/fuse/dao/Vulnerability.java +++ b/src/com/fuse/dao/Vulnerability.java @@ -399,7 +399,7 @@ public void setCvssString(String cvssString) { } public String getSection() { - return this.section == null || this.section == ""? "Default" : this.section.replaceAll("_", " "); + return this.section == null || this.section == ""? "Default" : this.section.replaceAll(" ", "_"); } public void setSection(String section) { diff --git a/src/com/fuse/dao/query/VulnerabilityQueries.java b/src/com/fuse/dao/query/VulnerabilityQueries.java index 556439b7..adb03695 100644 --- a/src/com/fuse/dao/query/VulnerabilityQueries.java +++ b/src/com/fuse/dao/query/VulnerabilityQueries.java @@ -279,8 +279,11 @@ public static void saveAll(FSActionSupport sender, Vulnerability vuln, EntityMan HibHelper.getInstance().commit(); } - // Mongo array of the statuses that mean a vulnerability is no longer open. - // Every other status (or a missing status on legacy documents) is open. + // Mongo array of the statuses that mean a vulnerability is closed in + // some environment (prod, dev, or staging). Used to match items that are + // explicitly "closed". Note that dev/staging-closed items are still + // open in production, so the "open" filters compare against StatusClosed + // only, not this array. public static String closedStatusArray() { return "[\"" + Vulnerability.StatusClosed + "\", \"" + Vulnerability.StatusClosedInDev + "\", \"" + Vulnerability.StatusClosedInStaging + "\"]"; @@ -295,8 +298,10 @@ public static List getOpenVulns(EntityManager em) { trackedLevels += "" + level.getRiskId() + ","; } } + // Open = not closed in production. Items closed in dev or staging + // are still open in prod, so they stay in the past-due list. String mongo = "{\"overall\" : {\"$in\": [" + trackedLevels + "]}, " + "\"closed\" : { \"$exists\" : false}," - + "\"status\" : { \"$nin\" : " + closedStatusArray() + "}," + + "\"status\" : { \"$ne\" : \"" + Vulnerability.StatusClosed + "\"}," + "\"opened\" : { \"$exists\" : true}" + "}"; return em.createNativeQuery(mongo, Vulnerability.class).getResultList(); } diff --git a/test/com/fuse/dao/query/VulnerabilityQueriesOpenVulnsTest.java b/test/com/fuse/dao/query/VulnerabilityQueriesOpenVulnsTest.java new file mode 100644 index 00000000..a47a8775 --- /dev/null +++ b/test/com/fuse/dao/query/VulnerabilityQueriesOpenVulnsTest.java @@ -0,0 +1,140 @@ +package com.fuse.dao.query; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.fuse.api.MongoTestBase; +import com.fuse.dao.HibHelper; +import com.fuse.dao.RiskLevel; +import com.fuse.dao.Vulnerability; + +/** + * Verifies the "open" semantics used by the remediation past-due queue: + * only vulnerabilities closed in production (status "Closed") are excluded. + * Items closed in dev or staging are still open in production and must + * remain in the open list. + */ +public class VulnerabilityQueriesOpenVulnsTest extends MongoTestBase { + + private static EntityManagerFactory emf; + private static Long riskLevelId; + private static int testRiskId; + private static String tag; + private static Long openVulnId; + private static Long devClosedVulnId; + private static Long stagingClosedVulnId; + private static Long prodClosedVulnId; + + @BeforeClass + public static void setupFixtures() { + emf = HibHelper.getInstance().getEMF(); + org.junit.Assume.assumeNotNull( + "EntityManagerFactory unavailable — skipping integration test", + emf); + + tag = UUID.randomUUID().toString().substring(0, 8); + testRiskId = 10000 + (int) (Math.random() * 89999); + + EntityManager em = emf.createEntityManager(); + try { + RiskLevel rl = new RiskLevel(); + rl.setRiskId(testRiskId); + rl.setRisk("open-vulns-test-" + tag); + rl.setDaysTillDue(30); + persist(em, rl); + riskLevelId = rl.getId(); + + openVulnId = persistVuln(em, "open-" + tag, Vulnerability.StatusOpen, null, null, null); + devClosedVulnId = persistVuln(em, "dev-" + tag, Vulnerability.StatusClosedInDev, new Date(), null, null); + stagingClosedVulnId = persistVuln(em, "staging-" + tag, Vulnerability.StatusClosedInStaging, null, new Date(), null); + prodClosedVulnId = persistVuln(em, "prod-" + tag, Vulnerability.StatusClosed, null, null, new Date()); + } finally { + em.close(); + } + } + + private static void persist(EntityManager em, Object entity) { + HibHelper.getInstance().preJoin(); + em.joinTransaction(); + em.persist(entity); + HibHelper.getInstance().commit(); + } + + private static Long persistVuln(EntityManager em, String name, String status, + Date devClosed, Date stagingClosed, Date closed) { + Vulnerability v = new Vulnerability(); + v.setName(name); + v.setOverall((long) testRiskId); + v.setOpened(new Date()); + v.setStatus(status); + v.setDevClosed(devClosed); + v.setStagingClosed(stagingClosed); + v.setClosed(closed); + persist(em, v); + return v.getId(); + } + + @AfterClass + public static void cleanupFixtures() { + if (emf == null) + return; + EntityManager em = emf.createEntityManager(); + try { + remove(em, RiskLevel.class, riskLevelId); + remove(em, Vulnerability.class, openVulnId); + remove(em, Vulnerability.class, devClosedVulnId); + remove(em, Vulnerability.class, stagingClosedVulnId); + remove(em, Vulnerability.class, prodClosedVulnId); + } finally { + em.close(); + } + } + + private static void remove(EntityManager em, Class clazz, Long id) { + if (id == null) + return; + Object entity = em.find(clazz, id); + if (entity != null) { + HibHelper.getInstance().preJoin(); + em.joinTransaction(); + em.remove(entity); + HibHelper.getInstance().commit(); + } + } + + private boolean contains(List vulns, String name) { + for (Vulnerability v : vulns) { + if (v.getName() != null && v.getName().startsWith(name)) + return true; + } + return false; + } + + @Test + public void openVulnsExcludeOnlyProdClosed() { + EntityManager em = emf.createEntityManager(); + try { + List open = VulnerabilityQueries.getOpenVulns(em); + + assertTrue("plain open vuln must be listed", contains(open, "open-" + tag)); + assertTrue("dev-closed vuln is still open in prod and must be listed", + contains(open, "dev-" + tag)); + assertTrue("staging-closed vuln is still open in prod and must be listed", + contains(open, "staging-" + tag)); + assertFalse("prod-closed vuln must not be listed", contains(open, "prod-" + tag)); + } finally { + em.close(); + } + } +}