security(import): harden DocumentBuilderFactory against XXE (#528) #610

Merged
marcel merged 3 commits from feat/issue-528-xxe-hardening into main 2026-05-17 16:42:10 +02:00
3 changed files with 15 additions and 8 deletions
Showing only changes of commit 669eaa7c65 - Show all commits

View File

@@ -289,9 +289,10 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install Semgrep
run: pip install semgrep
run: pip install semgrep==1.163.0
- name: Run security rules
run: semgrep --config .semgrep/security.yml --error --metrics=off backend/src/

View File

@@ -17,8 +17,9 @@ rules:
message: >
DocumentBuilderFactory without XXE protection (CWE-611).
Call XxeSafeXmlParser.hardenedFactory() instead of DocumentBuilderFactory.newInstance().
See: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
languages: [java]
severity: WARNING
severity: ERROR
# SAXParserFactory without XXE hardening.
- id: sax-xxe-default
@@ -30,9 +31,11 @@ rules:
...
message: >
SAXParserFactory without XXE protection (CWE-611).
Apply disallow-doctype-decl and disable external entity features before use.
Set disallow-doctype-decl=true, external-general-entities=false, external-parameter-entities=false,
and load-external-dtd=false before use. Follow the pattern in XxeSafeXmlParser.hardenedFactory().
See: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
languages: [java]
severity: WARNING
severity: ERROR
# XMLInputFactory without XXE hardening (StAX parser).
- id: stax-xxe-default
@@ -44,6 +47,8 @@ rules:
...
message: >
XMLInputFactory without XXE protection (CWE-611).
Set IS_SUPPORTING_EXTERNAL_ENTITIES to false and SUPPORT_DTD to false before use.
Set IS_SUPPORTING_EXTERNAL_ENTITIES=false and SUPPORT_DTD=false before use.
Follow the pattern in XxeSafeXmlParser.hardenedFactory().
See: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
languages: [java]
severity: WARNING
severity: ERROR

View File

@@ -527,7 +527,7 @@ class MassImportServiceTest {
// ─── readOds — XXE security regression ───────────────────────────────────
// Security regression — do not remove. Introduced by issue #528.
// Security regression — do not remove.
@Test
void readOds_rejects_xxe_doctype_payload(@TempDir Path tempDir) throws Exception {
File malicious = buildXxeOds(tempDir, "file:///etc/hostname");
@@ -595,7 +595,8 @@ class MassImportServiceTest {
return writeOdsZip(dir.resolve("malicious.ods"), xml);
}
/** Creates a minimal valid ODS ZIP containing a content.xml with the given cell value. */
/** Creates a minimal valid ODS ZIP containing a content.xml with the given cell value.
* cellValue must not contain XML metacharacters ({@code < > &}). */
private File buildValidOds(Path dir, String cellValue) throws Exception {
String xml = "<?xml version=\"1.0\"?>"
+ "<office:document-content"