This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 796d6b3d550e102793322c129851396f70513c4c
Merge: c964b9328f 642bcd5006
Author: James Daugherty <[email protected]>
AuthorDate: Thu Jul 9 22:42:11 2026 -0400

    Merge branch '7.0.x' into 7.1.x
    
    # Conflicts:
    #       
grails-web-mvc/src/test/groovy/org/grails/web/errors/GrailsExceptionResolverSpec.groovy

 ...maskingRequestParametersFromStacktraceLogs.adoc |   1 +
 .../META-INF/spring-configuration-metadata.json    |   5 +-
 .../grails/web/errors/GrailsExceptionResolver.java |  14 +-
 .../web/errors/GrailsExceptionResolverSpec.groovy  | 348 ++++++++++++---------
 4 files changed, 208 insertions(+), 160 deletions(-)

diff --cc 
grails-web-mvc/src/test/groovy/org/grails/web/errors/GrailsExceptionResolverSpec.groovy
index 384c382a7a,707c2e74fc..15ec0d61c3
--- 
a/grails-web-mvc/src/test/groovy/org/grails/web/errors/GrailsExceptionResolverSpec.groovy
+++ 
b/grails-web-mvc/src/test/groovy/org/grails/web/errors/GrailsExceptionResolverSpec.groovy
@@@ -46,262 -45,35 +46,298 @@@ class GrailsExceptionResolverSpec exten
          noExceptionThrown()
          params.isEmpty()
      }
 -    void "getRequestLogMessage masks excluded request parameters 
case-insensitively"() {
 +
 +    void "logStackTrace emits only the resolver log"() {
 +        given: "Captured System.err"
-             def originalErr = System.err
-             def baos = new ByteArrayOutputStream()
-             System.setErr(new PrintStream(baos, true))
++        def originalErr = System.err
++        def baos = new ByteArrayOutputStream()
++        System.setErr(new PrintStream(baos, true))
 +
 +        and: "A resolver with no grailsApplication wired"
-             def resolver = new GrailsExceptionResolver()
-             def request = new MockHttpServletRequest('GET', '/test')
-             def exception = new RuntimeException('boom')
++        def resolver = new GrailsExceptionResolver()
++        def request = new MockHttpServletRequest('GET', '/test')
++        def exception = new RuntimeException('boom')
 +
 +        when:
-             resolver.logStackTrace(exception, request)
++        resolver.logStackTrace(exception, request)
 +
 +        then: "Only the GrailsExceptionResolver logger emits; StackTrace 
logger is silent"
-             System.err.flush()
-             def captured = baos.toString()
-             captured.contains('o.g.web.errors.GrailsExceptionResolver') ||
++        System.err.flush()
++        def captured = baos.toString()
++        captured.contains('o.g.web.errors.GrailsExceptionResolver') ||
 +                
captured.contains('org.grails.web.errors.GrailsExceptionResolver')
-             !captured.contains('ERROR StackTrace ')
++        !captured.contains('ERROR StackTrace ')
 +
 +        cleanup:
-             System.setErr(originalErr)
++        System.setErr(originalErr)
 +    }
 +
 +    void "logFullStackTraceIfEnabled is a no-op when the opt-in property is 
unset"() {
 +        given: "Captured System.err"
-             def originalErr = System.err
-             def baos = new ByteArrayOutputStream()
-             System.setErr(new PrintStream(baos, true))
++        def originalErr = System.err
++        def baos = new ByteArrayOutputStream()
++        System.setErr(new PrintStream(baos, true))
 +
 +        and: "A resolver with no grailsApplication wired"
-             def resolver = new GrailsExceptionResolver()
-             def exception = new RuntimeException('boom')
++        def resolver = new GrailsExceptionResolver()
++        def exception = new RuntimeException('boom')
 +
 +        when:
-             resolver.logFullStackTraceIfEnabled(exception)
++        resolver.logFullStackTraceIfEnabled(exception)
 +
 +        then: "No StackTrace log entry is emitted"
-             System.err.flush()
-             !baos.toString().contains('ERROR StackTrace ')
++        System.err.flush()
++        !baos.toString().contains('ERROR StackTrace ')
 +
 +        cleanup:
-             System.setErr(originalErr)
++        System.setErr(originalErr)
 +    }
 +
 +    void "getRequestLogMessage appends auditor when logAuditor is enabled and 
the lookup returns a value"() {
          given:
--            def config = Mock(Config)
-             config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> true
 -            
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> true
 -            config.getProperty('grails.exceptionresolver.params.exclude', 
List, _) >> [null, 'password', 'token']
 -            config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> false
--            config.getProperty('grails.exceptionresolver.logRemoteAddr', 
Boolean, false) >> false
--            
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
--            config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> false
--            def grailsApp = Mock(GrailsApplication)
--            grailsApp.getConfig() >> config
--            def resolver = new GrailsExceptionResolver()
--            resolver.grailsApplication = grailsApp
-             resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
-                 @Override
-                 Optional<?> getCurrentAuditor() { Optional.of('alice') }
-             }
-             def request = new MockHttpServletRequest('GET', '/test')
 -            def request = new MockHttpServletRequest('POST', '/login')
 -            request.addParameter('Password', 'secret')
 -            request.addParameter('apiToken', 'visible')
 -            request.addParameter('TOKEN', 'abc123')
 -            request.addParameter('username', 'sherlock')
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> true
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> false
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> false
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver()
++        resolver.grailsApplication = grailsApp
++        resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
++
++            @Override
++            Optional<?> getCurrentAuditor() { Optional.of('alice') }
++        }
++        def request = new MockHttpServletRequest('GET', '/test')
  
          when:
--            def msg = resolver.getRequestLogMessage('RuntimeException', 
request, 'boom')
++        def msg = resolver.getRequestLogMessage('RuntimeException', request, 
'boom')
  
          then:
-             msg.contains('(user: alice)')
 -            msg.contains('Password: ***')
 -            msg.contains('TOKEN: ***')
 -            msg.contains('username: sherlock')
 -            msg.contains('apiToken: visible')
 -            !msg.contains('Password: secret')
 -            !msg.contains('TOKEN: abc123')
++        msg.contains('(user: alice)')
      }
  
 +    void "getRequestLogMessage omits auditor when logAuditor is disabled"() {
 +        given:
-             def config = Mock(Config)
-             config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> false
-             config.getProperty('grails.exceptionresolver.logRemoteAddr', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
-             config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> false
-             def grailsApp = Mock(GrailsApplication)
-             grailsApp.getConfig() >> config
-             def resolver = new GrailsExceptionResolver()
-             resolver.grailsApplication = grailsApp
-             resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
-                 @Override
-                 Optional<?> getCurrentAuditor() { Optional.of('alice') }
-             }
-             def request = new MockHttpServletRequest('GET', '/test')
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> false
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> false
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> false
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver()
++        resolver.grailsApplication = grailsApp
++        resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
++
++            @Override
++            Optional<?> getCurrentAuditor() { Optional.of('alice') }
++        }
++        def request = new MockHttpServletRequest('GET', '/test')
 +
 +        when:
-             def msg = resolver.getRequestLogMessage('RuntimeException', 
request, 'boom')
++        def msg = resolver.getRequestLogMessage('RuntimeException', request, 
'boom')
 +
 +        then:
-             !msg.contains('(user:')
++        !msg.contains('(user:')
 +    }
 +
 +    void "getRequestLogMessage omits auditor when logAuditor is enabled but 
auditor is absent"() {
 +        given:
-             def config = Mock(Config)
-             config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> true
-             config.getProperty('grails.exceptionresolver.logRemoteAddr', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
-             config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> false
-             def grailsApp = Mock(GrailsApplication)
-             grailsApp.getConfig() >> config
-             def resolver = new GrailsExceptionResolver()
-             resolver.grailsApplication = grailsApp
-             resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
-                 @Override
-                 Optional<?> getCurrentAuditor() { Optional.empty() }
-             }
-             def request = new MockHttpServletRequest('GET', '/test')
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> true
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> false
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> false
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver()
++        resolver.grailsApplication = grailsApp
++        resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
++
++            @Override
++            Optional<?> getCurrentAuditor() { Optional.empty() }
++        }
++        def request = new MockHttpServletRequest('GET', '/test')
 +
 +        when:
-             def msg = resolver.getRequestLogMessage('RuntimeException', 
request, 'boom')
++        def msg = resolver.getRequestLogMessage('RuntimeException', request, 
'boom')
 +
 +        then:
-             !msg.contains('(user:')
++        !msg.contains('(user:')
 +    }
 +
 +    void "getRequestLogMessage appends remote address when logRemoteAddr is 
enabled"() {
 +        given:
-             def config = Mock(Config)
-             config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> false
-             config.getProperty('grails.exceptionresolver.logRemoteAddr', 
Boolean, false) >> true
-             
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
-             config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> false
-             def grailsApp = Mock(GrailsApplication)
-             grailsApp.getConfig() >> config
-             def resolver = new GrailsExceptionResolver()
-             resolver.grailsApplication = grailsApp
-             def request = new MockHttpServletRequest('GET', '/test')
-             request.remoteAddr = '198.51.100.42'
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> false
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> true
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> false
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver()
++        resolver.grailsApplication = grailsApp
++        def request = new MockHttpServletRequest('GET', '/test')
++        request.remoteAddr = '198.51.100.42'
 +
 +        when:
-             def msg = resolver.getRequestLogMessage('RuntimeException', 
request, 'boom')
++        def msg = resolver.getRequestLogMessage('RuntimeException', request, 
'boom')
 +
 +        then:
-             msg.contains('(ip: 198.51.100.42)')
-             !msg.contains('user:')
++        msg.contains('(ip: 198.51.100.42)')
++        !msg.contains('user:')
 +    }
 +
 +    void "getRequestLogMessage combines remote address and auditor into a 
single clause when both are enabled"() {
 +        given:
-             def config = Mock(Config)
-             config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> true
-             config.getProperty('grails.exceptionresolver.logRemoteAddr', 
Boolean, false) >> true
-             
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
-             config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> false
-             def grailsApp = Mock(GrailsApplication)
-             grailsApp.getConfig() >> config
-             def resolver = new GrailsExceptionResolver()
-             resolver.grailsApplication = grailsApp
-             resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
-                 @Override
-                 Optional<?> getCurrentAuditor() { Optional.of(42L) }
-             }
-             def request = new MockHttpServletRequest('GET', '/test')
-             request.remoteAddr = '198.51.100.42'
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> true
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> true
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> false
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver()
++        resolver.grailsApplication = grailsApp
++        resolver.auditorAwareLookup = new AuditorAwareLookup(null) {
++
++            @Override
++            Optional<?> getCurrentAuditor() { Optional.of(42L) }
++        }
++        def request = new MockHttpServletRequest('GET', '/test')
++        request.remoteAddr = '198.51.100.42'
 +
 +        when:
-             def msg = resolver.getRequestLogMessage('RuntimeException', 
request, 'boom')
++        def msg = resolver.getRequestLogMessage('RuntimeException', request, 
'boom')
 +
 +        then:
-             msg.contains('(ip: 198.51.100.42, user: 42)')
++        msg.contains('(ip: 198.51.100.42, user: 42)')
 +    }
 +
 +    void "subclasses can override resolveRemoteAddr to supply a custom IP 
extraction"() {
 +        given:
-             def config = Mock(Config)
-             config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> false
-             config.getProperty('grails.exceptionresolver.logRemoteAddr', 
Boolean, false) >> true
-             
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
-             config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> false
-             def grailsApp = Mock(GrailsApplication)
-             grailsApp.getConfig() >> config
-             def resolver = new GrailsExceptionResolver() {
-                 @Override
-                 protected String resolveRemoteAddr(HttpServletRequest req) {
-                     req.getHeader('X-Forwarded-For') ?: req.remoteAddr
-                 }
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> false
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> true
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> false
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver() {
++
++            @Override
++            protected String resolveRemoteAddr(HttpServletRequest req) {
++                req.getHeader('X-Forwarded-For') ?: req.remoteAddr
 +            }
-             resolver.grailsApplication = grailsApp
-             def request = new MockHttpServletRequest('GET', '/test')
-             request.remoteAddr = '10.0.0.1'
-             request.addHeader('X-Forwarded-For', '203.0.113.7')
++        }
++        resolver.grailsApplication = grailsApp
++        def request = new MockHttpServletRequest('GET', '/test')
++        request.remoteAddr = '10.0.0.1'
++        request.addHeader('X-Forwarded-For', '203.0.113.7')
 +
 +        when:
-             def msg = resolver.getRequestLogMessage('RuntimeException', 
request, 'boom')
++        def msg = resolver.getRequestLogMessage('RuntimeException', request, 
'boom')
 +
 +        then:
-             msg.contains('(ip: 203.0.113.7)')
++        msg.contains('(ip: 203.0.113.7)')
 +    }
 +
 +    void "AuditorAwareLookup returns empty when no application context is 
provided"() {
 +        given:
-             def lookup = new AuditorAwareLookup(null)
++        def lookup = new AuditorAwareLookup(null)
 +
 +        expect:
-             !lookup.getCurrentAuditor().isPresent()
++        !lookup.getCurrentAuditor().isPresent()
 +    }
 +
 +    void "logFullStackTraceIfEnabled emits the unfiltered trace when opt-in 
is enabled, and filterStackTrace then removes internal frames so the resolver 
log only sees the filtered trace"() {
 +        given: "Captured System.err"
-             def originalErr = System.err
-             def baos = new ByteArrayOutputStream()
-             System.setErr(new PrintStream(baos, true))
++        def originalErr = System.err
++        def baos = new ByteArrayOutputStream()
++        System.setErr(new PrintStream(baos, true))
 +
 +        and: "A resolver whose config opts in to full stack trace logging"
-             def config = Mock(Config)
-             config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> true
-             
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
-             config.getProperty('grails.exceptionresolver.logAuditor', 
Boolean, false) >> false
-             config.getProperty('grails.exceptionresolver.logRemoteAddr', 
Boolean, false) >> false
-             
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
-             
config.getProperty('grails.exceptionresolver.logRequestParameters', Boolean, _) 
>> false
-             config.getProperty('grails.logging.stackTraceFiltererClass', 
Class, _) >>
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> true
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> false
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> false
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> false
++        config.getProperty('grails.logging.stackTraceFiltererClass', Class, 
_) >>
 +                DefaultStackTraceFilterer
-             def grailsApp = Mock(GrailsApplication)
-             grailsApp.getConfig() >> config
-             def resolver = new GrailsExceptionResolver()
-             resolver.grailsApplication = grailsApp
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver()
++        resolver.grailsApplication = grailsApp
 +
 +        and: "An exception with a mix of internal (filterable) and 
application frames"
-             def exception = new RuntimeException('boom')
-             exception.stackTrace = [
++        def exception = new RuntimeException('boom')
++        exception.stackTrace = [
 +                new StackTraceElement('java.lang.reflect.Method', 'invoke', 
'Method.java', 580),
 +                new StackTraceElement('com.example.MyController', 'show', 
'MyController.groovy', 10),
-             ] as StackTraceElement[]
-             def request = new MockHttpServletRequest('GET', '/test')
++        ] as StackTraceElement[]
++        def request = new MockHttpServletRequest('GET', '/test')
 +
 +        when: "The real resolveException ordering runs: log full trace, 
filter, then log with request context"
-             resolver.logFullStackTraceIfEnabled(exception)
-             resolver.filterStackTrace(exception)
-             resolver.logStackTrace(exception, request)
++        resolver.logFullStackTraceIfEnabled(exception)
++        resolver.filterStackTrace(exception)
++        resolver.logStackTrace(exception, request)
 +
 +        then: "Both loggers emit"
-             System.err.flush()
-             def captured = baos.toString()
-             captured.contains('ERROR StackTrace ')
-             captured.contains('Full Stack Trace:')
-             captured.contains('o.g.web.errors.GrailsExceptionResolver') ||
++        System.err.flush()
++        def captured = baos.toString()
++        captured.contains('ERROR StackTrace ')
++        captured.contains('Full Stack Trace:')
++        captured.contains('o.g.web.errors.GrailsExceptionResolver') ||
 +                
captured.contains('org.grails.web.errors.GrailsExceptionResolver')
 +
 +        and: "The application frame appears in both log entries"
-             
captured.count('com.example.MyController.show(MyController.groovy:10)') == 2
++        
captured.count('com.example.MyController.show(MyController.groovy:10)') == 2
 +
 +        and: "The internal frame appears only once — in the unfiltered 
StackTrace entry, not in the filtered resolver entry"
-             
captured.count('java.lang.reflect.Method.invoke(Method.java:580)') == 1
++        captured.count('java.lang.reflect.Method.invoke(Method.java:580)') == 
1
 +
 +        cleanup:
-             System.setErr(originalErr)
++        System.setErr(originalErr)
++    }
++
++    void "getRequestLogMessage masks excluded request parameters 
case-insensitively"() {
++        given:
++        def config = Mock(Config)
++        config.getProperty('grails.exceptionresolver.logRequestParameters', 
Boolean, _) >> true
++        config.getProperty('grails.exceptionresolver.params.exclude', List, 
_) >> [null, 'password', 'token']
++        config.getProperty('grails.exceptionresolver.logAuditor', Boolean, 
false) >> false
++        config.getProperty('grails.exceptionresolver.logRemoteAddr', Boolean, 
false) >> false
++        
config.getProperty('grails.exceptionresolver.logFullStackTraceOnFilter', 
Boolean, true) >> false
++        config.getProperty('grails.exceptionresolver.logFullStackTrace', 
Boolean, false) >> false
++        def grailsApp = Mock(GrailsApplication)
++        grailsApp.getConfig() >> config
++        def resolver = new GrailsExceptionResolver()
++        resolver.grailsApplication = grailsApp
++        def request = new MockHttpServletRequest('POST', '/login')
++        request.addParameter('Password', 'secret')
++        request.addParameter('apiToken', 'visible')
++        request.addParameter('TOKEN', 'abc123')
++        request.addParameter('username', 'sherlock')
++
++        when:
++        def msg = resolver.getRequestLogMessage('RuntimeException', request, 
'boom')
++
++        then:
++        msg.contains('Password: ***')
++        msg.contains('TOKEN: ***')
++        msg.contains('username: sherlock')
++        msg.contains('apiToken: visible')
++        !msg.contains('Password: secret')
++        !msg.contains('TOKEN: abc123')
 +    }
  }

Reply via email to