Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/com/fuse/reporting/DocxUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1438,7 +1438,7 @@ private void replaceAssessment(String customCSS) throws Exception {
map2.put("${asmtAssessors_Comma}", wrapHTML(assessors_comma, customCSS, ""));
replaceHTML(mlp.getMainDocumentPart(), map2);
replaceHTML(mlp.getMainDocumentPart(), cfMap, false);
replaceHeaderAndFooter(map);
replaceHeaderAndFooter(map, dateMap);
} finally {
context.end();
}
Expand Down Expand Up @@ -2593,7 +2593,7 @@ private String centerImages(String content) {
}
}

private void replaceHeaderAndFooter(final Map<String, String> replacements) {
private void replaceHeaderAndFooter(final Map<String, String> replacements, final Map<String, Date> dates) {
MethodProfiler.ProfileContext context = MethodProfiler.start("DocxUtils", "replaceHeaderAndFooter");
try {
List<Object> list = new ArrayList<>();
Expand All @@ -2602,6 +2602,9 @@ private void replaceHeaderAndFooter(final Map<String, String> replacements) {
if (this.mlp.getHeaderFooterPolicy().getDefaultHeader() != null) {
HeaderPart fp = this.mlp.getHeaderFooterPolicy().getDefaultHeader();
String xml = XmlUtils.marshaltoString(fp.getContents(), false, true);
for (Map.Entry<String, Date> date : dates.entrySet()) {
xml = replaceDateVariable(xml, date.getKey(), date.getValue());
}
for (String key : replacements.keySet()) {
xml = xml.replace("${" + key + "}", replacements.get(key));
}
Expand All @@ -2610,6 +2613,9 @@ private void replaceHeaderAndFooter(final Map<String, String> replacements) {
if (this.mlp.getHeaderFooterPolicy().getFirstHeader() != null) {
HeaderPart fp = this.mlp.getHeaderFooterPolicy().getFirstHeader();
String xml = XmlUtils.marshaltoString(fp.getContents(), false, true);
for (Map.Entry<String, Date> date : dates.entrySet()) {
xml = replaceDateVariable(xml, date.getKey(), date.getValue());
}
for (String key : replacements.keySet()) {
xml = xml.replace("${" + key + "}", replacements.get(key));
}
Expand All @@ -2618,6 +2624,9 @@ private void replaceHeaderAndFooter(final Map<String, String> replacements) {
if (this.mlp.getHeaderFooterPolicy().getEvenHeader() != null) {
HeaderPart fp = this.mlp.getHeaderFooterPolicy().getEvenHeader();
String xml = XmlUtils.marshaltoString(fp.getContents(), false, true);
for (Map.Entry<String, Date> date : dates.entrySet()) {
xml = replaceDateVariable(xml, date.getKey(), date.getValue());
}
for (String key : replacements.keySet()) {
xml = xml.replace("${" + key + "}", replacements.get(key));
}
Expand All @@ -2627,6 +2636,9 @@ private void replaceHeaderAndFooter(final Map<String, String> replacements) {
if (this.mlp.getHeaderFooterPolicy().getDefaultFooter() != null) {
FooterPart fp = this.mlp.getHeaderFooterPolicy().getDefaultFooter();
String xml = XmlUtils.marshaltoString(fp.getContents(), false, true);
for (Map.Entry<String, Date> date : dates.entrySet()) {
xml = replaceDateVariable(xml, date.getKey(), date.getValue());
}
for (String key : replacements.keySet()) {
xml = xml.replace("${" + key + "}", replacements.get(key));
}
Expand All @@ -2635,6 +2647,9 @@ private void replaceHeaderAndFooter(final Map<String, String> replacements) {
if (this.mlp.getHeaderFooterPolicy().getFirstFooter() != null) {
FooterPart fp = this.mlp.getHeaderFooterPolicy().getFirstFooter();
String xml = XmlUtils.marshaltoString(fp.getContents(), false, true);
for (Map.Entry<String, Date> date : dates.entrySet()) {
xml = replaceDateVariable(xml, date.getKey(), date.getValue());
}
for (String key : replacements.keySet()) {
xml = xml.replace("${" + key + "}", replacements.get(key));
}
Expand All @@ -2643,6 +2658,9 @@ private void replaceHeaderAndFooter(final Map<String, String> replacements) {
if (this.mlp.getHeaderFooterPolicy().getEvenFooter() != null) {
FooterPart fp = this.mlp.getHeaderFooterPolicy().getEvenFooter();
String xml = XmlUtils.marshaltoString(fp.getContents(), false, true);
for (Map.Entry<String, Date> date : dates.entrySet()) {
xml = replaceDateVariable(xml, date.getKey(), date.getValue());
}
for (String key : replacements.keySet()) {
xml = xml.replace("${" + key + "}", replacements.get(key));
}
Expand Down
130 changes: 130 additions & 0 deletions test/org/fuse/docx/DocxHeaderFooterDateTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package org.fuse.docx;

import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;

import org.docx4j.TextUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.HeaderReference;
import org.docx4j.wml.Hdr;
import org.docx4j.wml.HdrFtrRef;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.SectPr;
import org.junit.Test;
import org.mockito.Mockito;

import com.fuse.dao.AppStore;
import com.fuse.dao.Assessment;
import com.fuse.dao.AssessmentType;
import com.fuse.dao.RiskLevel;
import com.fuse.dao.Teams;
import com.fuse.reporting.DocxUtils;
import com.fuse.reporting.GenerateReport;

/**
* Regression test for factionsecurity/faction#143 — ${today} (and other
* date variables) were resolved in the document body but left as literal
* text in headers/footers, because the Map<String,Date> of date values
* never reached replaceHeaderAndFooter(). This guards that the header
* now resolves both the default date format and a custom-format
* variant (${today <pattern>}).
*/
public class DocxHeaderFooterDateTest {

@Test
public void headerDateVariableResolves() throws Exception {
Teams team = new Teams();
team.setId(1L);
team.setTeamName("Hacking Team");
AssessmentType type = new AssessmentType();
type.setId(1L);
type.setType("Assessment Type");

List<RiskLevel> levels = new ArrayList<>();
String[] risk = { "Informational", "Recommended", "Low", "Medium", "High", "Critical" };
for (int i = 0; i < 10; i++) {
RiskLevel level = new RiskLevel();
level.setRiskId(i);
if (i < risk.length)
level.setRisk(risk[i]);
levels.add(level);
}

Assessment assessment = GenerateReport.createTestAssessment(team, type, levels, new String[] { "S1" });
assessment.setGuid("test-guid");
Date start = new java.util.GregorianCalendar(2026, 0, 5).getTime();
Date end = new java.util.GregorianCalendar(2026, 0, 23).getTime();
assessment.setStart(start);
assessment.setEnd(end);
assessment.getVulns().clear();

WordprocessingMLPackage mlp = WordprocessingMLPackage.createPackage();
mlp.getMainDocumentPart().addParagraphOfText("End of report");

// Wire up a default header containing both the default-format and a
// custom-format date placeholder, per docx4j's header creation pattern.
ObjectFactory factory = Context.getWmlObjectFactory();
HeaderPart headerPart = new HeaderPart();
Relationship headerRel = mlp.getMainDocumentPart().addTargetPart(headerPart);

Hdr hdr = factory.createHdr();
headerPart.setJaxbElement(hdr);

org.docx4j.wml.P headerPara = factory.createP();
org.docx4j.wml.R headerRun = factory.createR();
org.docx4j.wml.Text headerTextRun = factory.createText();
headerTextRun.setValue("today=${today} custom=${today MM-dd-yyyy}");
headerTextRun.setSpace("preserve");
headerRun.getContent().add(headerTextRun);
headerPara.getContent().add(headerRun);
hdr.getContent().add(headerPara);

HeaderReference headerRef = factory.createHeaderReference();
headerRef.setType(HdrFtrRef.DEFAULT);
headerRef.setId(headerRel.getId());

SectPr sectPr = mlp.getMainDocumentPart().getJaxbElement().getBody().getSectPr();
if (sectPr == null) {
sectPr = factory.createSectPr();
mlp.getMainDocumentPart().getJaxbElement().getBody().setSectPr(sectPr);
}
sectPr.getEGHdrFtrReferences().add(headerRef);

EntityManagerFactory emf = Mockito.mock(EntityManagerFactory.class);
EntityManager em = Mockito.mock(EntityManager.class);
Query query = Mockito.mock(Query.class);
Mockito.when(emf.createEntityManager()).thenReturn(em);
Mockito.when(em.createQuery("from AppStore order by order")).thenReturn(query);
Mockito.when(query.getResultList()).thenReturn(new ArrayList<AppStore>());

DocxUtils genDoc = new DocxUtils(emf, mlp, assessment);
genDoc.FONT = "Calibri";
mlp = genDoc.generateDocx("p { margin: 0; }");

StringWriter sw = new StringWriter();
TextUtils.extractText(
mlp.getHeaderFooterPolicy().getDefaultHeader().getContents(), sw);
String headerText = sw.toString();

String defaultFormatted = new SimpleDateFormat("MM/dd/yyyy").format(new Date());

org.junit.Assert.assertFalse("literal ${today} leaked into header",
headerText.contains("${today"));
org.junit.Assert.assertTrue("default-format date not resolved in header, got: " + headerText,
headerText.contains("today=" + defaultFormatted));

String customFormatted = new SimpleDateFormat("MM-dd-yyyy").format(new Date());
org.junit.Assert.assertTrue("custom-format date not resolved in header, got: " + headerText,
headerText.contains("custom=" + customFormatted));
}
}