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();
+ }
+ }
+}