diff --git a/src/main/java/com/clearfolio/viewer/config/ArtifactStoreProperties.java b/src/main/java/com/clearfolio/viewer/config/ArtifactStoreProperties.java index 3f39c5b00..b158ce6aa 100644 --- a/src/main/java/com/clearfolio/viewer/config/ArtifactStoreProperties.java +++ b/src/main/java/com/clearfolio/viewer/config/ArtifactStoreProperties.java @@ -42,15 +42,24 @@ public String getMode() { } /** - * Sets the artifact store mode; blank or null values fall back to the - * disk-backed default, and any value other than {@link #MODE_IN_MEMORY} - * selects the disk-backed store. + * Sets the artifact store mode. Blank or null values fall back to the + * disk-backed default; unsupported non-blank modes are rejected so a + * deployment cannot silently persist artifacts to a different store than + * the operator intended. * * @param mode artifact store mode + * @throws IllegalArgumentException when a non-blank mode is unsupported */ public void setMode(String mode) { String sanitized = sanitize(mode); - this.mode = sanitized.isEmpty() ? MODE_FILESYSTEM : sanitized; + if (sanitized.isEmpty()) { + this.mode = MODE_FILESYSTEM; + return; + } + if (!MODE_FILESYSTEM.equals(sanitized) && !MODE_IN_MEMORY.equals(sanitized)) { + throw new IllegalArgumentException("unsupported artifact store mode"); + } + this.mode = sanitized; } /** @@ -86,6 +95,9 @@ private static String sanitize(String value) { if (value == null) { return ""; } - return value.replace("\u0000", "").strip().toLowerCase(Locale.ROOT); + if (value.indexOf('\u0000') >= 0) { + throw new IllegalArgumentException("unsupported artifact store mode: contains NUL"); + } + return value.strip().toLowerCase(Locale.ROOT); } } diff --git a/src/test/java/com/clearfolio/viewer/config/ArtifactStorePropertiesTest.java b/src/test/java/com/clearfolio/viewer/config/ArtifactStorePropertiesTest.java index 357ab2c3b..602e50f53 100644 --- a/src/test/java/com/clearfolio/viewer/config/ArtifactStorePropertiesTest.java +++ b/src/test/java/com/clearfolio/viewer/config/ArtifactStorePropertiesTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -27,6 +28,17 @@ void setModeNormalizesCaseAndWhitespace() { assertTrue(properties.isInMemoryMode()); } + @Test + void setModeAcceptsExplicitFilesystemMode() { + ArtifactStoreProperties properties = new ArtifactStoreProperties(); + properties.setMode(ArtifactStoreProperties.MODE_IN_MEMORY); + + properties.setMode(" FILESYSTEM "); + + assertEquals(ArtifactStoreProperties.MODE_FILESYSTEM, properties.getMode()); + assertFalse(properties.isInMemoryMode()); + } + @Test void setModeFallsBackToFilesystemForNullAndBlankValues() { ArtifactStoreProperties properties = new ArtifactStoreProperties(); @@ -39,13 +51,28 @@ void setModeFallsBackToFilesystemForNullAndBlankValues() { } @Test - void setModeKeepsUnknownValuesWithoutSelectingInMemory() { + void setModeRejectsUnknownStorageModes() { ArtifactStoreProperties properties = new ArtifactStoreProperties(); - properties.setMode("cloud"); + IllegalArgumentException error = assertThrows( + IllegalArgumentException.class, + () -> properties.setMode("cloud://tenant-secret") + ); - assertEquals("cloud", properties.getMode()); - assertFalse(properties.isInMemoryMode()); + assertEquals("unsupported artifact store mode", error.getMessage()); + assertEquals(ArtifactStoreProperties.MODE_FILESYSTEM, properties.getMode()); + } + + @Test + void setModeRejectsNulCorruptedKnownMode() { + ArtifactStoreProperties properties = new ArtifactStoreProperties(); + + assertThrows( + IllegalArgumentException.class, + () -> properties.setMode("file\u0000system") + ); + + assertEquals(ArtifactStoreProperties.MODE_FILESYSTEM, properties.getMode()); } @Test