From d4ffb058d8c8a98ece37e598601c47b0f0f7b56a Mon Sep 17 00:00:00 2001 From: Bertrand Date: Tue, 16 Dec 2025 18:38:30 -0800 Subject: [PATCH] Add createSubmit to audit trail filter pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The WebPostAccessLogger filter pattern was missing 'createSubmit', which meant that credential creation events (and other resource creation via createSubmit) were not being logged to the audit trail. This fix adds 'createSubmit' to the FILTER_PATTERN regex, ensuring that resource creation events are properly audited alongside updates and deletions. Before this fix: - configSubmit: ✓ logged - updateSubmit: ✓ logged - doDelete: ✓ logged - createSubmit: ✗ NOT logged After this fix: - configSubmit: ✓ logged - createSubmit: ✓ logged - updateSubmit: ✓ logged - doDelete: ✓ logged Also adds unit tests for the filter pattern matching behavior. The pattern visibility was changed from private to package-visible to allow tests to reference the actual pattern rather than duplicating it. --- .../splunkjenkins/WebPostAccessLogger.java | 3 +- .../WebPostAccessLoggerTest.java | 112 ++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 splunk-devops/src/test/java/com/splunk/splunkjenkins/WebPostAccessLoggerTest.java diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/WebPostAccessLogger.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/WebPostAccessLogger.java index 34262926..564ee379 100644 --- a/splunk-devops/src/main/java/com/splunk/splunkjenkins/WebPostAccessLogger.java +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/WebPostAccessLogger.java @@ -22,7 +22,8 @@ public class WebPostAccessLogger implements Filter { private static final Logger LOG = Logger.getLogger(WebPostAccessLogger.class.getName()); - private static final Pattern FILTER_PATTERN = Pattern.compile("/(?:configSubmit|updateSubmit|script|doDelete)"); + // Package-visible for testing + static final Pattern FILTER_PATTERN = Pattern.compile("/(?:configSubmit|createSubmit|updateSubmit|script|doDelete)"); @Override public void init(FilterConfig filterConfig) throws ServletException { diff --git a/splunk-devops/src/test/java/com/splunk/splunkjenkins/WebPostAccessLoggerTest.java b/splunk-devops/src/test/java/com/splunk/splunkjenkins/WebPostAccessLoggerTest.java new file mode 100644 index 00000000..c93ba46a --- /dev/null +++ b/splunk-devops/src/test/java/com/splunk/splunkjenkins/WebPostAccessLoggerTest.java @@ -0,0 +1,112 @@ +package com.splunk.splunkjenkins; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Unit tests for {@link WebPostAccessLogger} pattern matching. + * These tests verify that the audit trail filter correctly identifies + * URLs that should be logged for security auditing purposes. + */ +public class WebPostAccessLoggerTest { + + // ==================== configSubmit tests ==================== + + @Test + public void testConfigSubmitMatches() { + assertTrue("configSubmit should match", WebPostAccessLogger.FILTER_PATTERN.matcher("/job/test/configSubmit").find()); + assertTrue("configSubmit should match", WebPostAccessLogger.FILTER_PATTERN.matcher("/manage/configSubmit").find()); + } + + // ==================== createSubmit tests ==================== + + @Test + public void testCreateSubmitMatchesCredentials() { + // Credential creation - the main use case this fix addresses + assertTrue("createSubmit for credentials should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/manage/credentials/store/system/domain/_/createSubmit").find()); + assertTrue("createSubmit for folder credentials should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/job/folder/credentials/store/folder/domain/_/createSubmit").find()); + } + + @Test + public void testCreateSubmitMatchesOtherResources() { + // View creation + assertTrue("createSubmit for views should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/view/All/createSubmit").find()); + // Generic createSubmit + assertTrue("generic createSubmit should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/createSubmit").find()); + } + + // ==================== updateSubmit tests ==================== + + @Test + public void testUpdateSubmitMatches() { + assertTrue("updateSubmit for credentials should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/manage/credentials/store/system/domain/_/credential/my-cred/updateSubmit").find()); + assertTrue("updateSubmit for job should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/job/test/updateSubmit").find()); + } + + // ==================== script tests ==================== + + @Test + public void testScriptMatches() { + // Script console - security sensitive + assertTrue("script console should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/script").find()); + assertTrue("scriptText should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/manage/scriptText").find()); + assertTrue("folder scriptText should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/job/folder/scriptText").find()); + } + + // ==================== doDelete tests ==================== + + @Test + public void testDoDeleteMatches() { + assertTrue("doDelete for credentials should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/manage/credentials/store/system/domain/_/credential/my-cred/doDelete").find()); + assertTrue("doDelete for job should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/job/test/doDelete").find()); + } + + // ==================== Negative tests - should NOT match ==================== + + @Test + public void testRegularRequestsDoNotMatch() { + // Regular API calls should not trigger audit + assertFalse("API json should not match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/api/json").find()); + assertFalse("Job build should not match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/job/test/build").find()); + assertFalse("Job console should not match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/job/test/1/console").find()); + assertFalse("Credentials list should not match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/credentials/store/system/domain/_/").find()); + } + + @Test + public void testPatternMatchesSuffixVariations() { + // The pattern uses .find() which matches if /keyword appears as substring + // This means suffixes after the keyword will still match + assertTrue("configSubmittal contains /configSubmit - should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/configSubmittal").find()); + assertTrue("doDeleteAll contains /doDelete - should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/doDeleteAll").find()); + assertTrue("createSubmitForm contains /createSubmit - should match", + WebPostAccessLogger.FILTER_PATTERN.matcher("/createSubmitForm").find()); + } + + @Test + public void testPatternDoesNotMatchPrefixVariations() { + // Prefix before the keyword breaks the /keyword pattern + assertFalse("myCreateSubmit - no /createSubmit pattern", + WebPostAccessLogger.FILTER_PATTERN.matcher("/myCreateSubmit").find()); + assertFalse("preConfigSubmit - no /configSubmit pattern", + WebPostAccessLogger.FILTER_PATTERN.matcher("/preConfigSubmit").find()); + } +}