diff --git a/grails-doc/src/en/guide/conf/config/logging/maskingRequestParametersFromStacktraceLogs.adoc b/grails-doc/src/en/guide/conf/config/logging/maskingRequestParametersFromStacktraceLogs.adoc index 50efe741566..b43f3bf5951 100644 --- a/grails-doc/src/en/guide/conf/config/logging/maskingRequestParametersFromStacktraceLogs.adoc +++ b/grails-doc/src/en/guide/conf/config/logging/maskingRequestParametersFromStacktraceLogs.adoc @@ -19,6 +19,7 @@ under the License. When Grails logs a stacktrace, the log message may include the names and values of all of the request parameters for the current request. To mask out the values of secure request parameters, specify the parameter names in the `grails.exceptionresolver.params.exclude` config property: +Parameter names are matched case-insensitively. [source, yaml] .grails-app/conf/application.yml diff --git a/grails-web-core/src/main/resources/META-INF/spring-configuration-metadata.json b/grails-web-core/src/main/resources/META-INF/spring-configuration-metadata.json index 1dd9840914e..0ca996c8285 100644 --- a/grails-web-core/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/grails-web-core/src/main/resources/META-INF/spring-configuration-metadata.json @@ -115,7 +115,7 @@ { "name": "grails.exceptionresolver.params.exclude", "type": "java.util.List", - "description": "List of parameter names to mask (replace with [*****]) in exception stack traces, typically used for password and creditCard.", + "description": "List of parameter names to mask (replace with ***) in exception stack traces, typically used for password and creditCard. Parameter names are matched case-insensitively.", "defaultValue": [] }, { diff --git a/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java b/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java index 9d41a20e928..2ae2c2bc17d 100644 --- a/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java +++ b/grails-web-mvc/src/main/groovy/org/grails/web/errors/GrailsExceptionResolver.java @@ -310,7 +310,7 @@ protected String getRequestLogMessage(String exceptionName, HttpServletRequest r for (i = 0; i < values.length; i++) { sb.append(LINE_SEPARATOR).append(param).append(": "); - if (blackList.contains(param)) { + if (isExcludedRequestParameter(param, blackList)) { sb.append("***"); } else { sb.append(values[i]); @@ -330,6 +330,18 @@ protected String getRequestLogMessage(String exceptionName, HttpServletRequest r return sb.toString(); } + protected boolean isExcludedRequestParameter(String parameterName, List excludedParameterNames) { + if (parameterName == null || excludedParameterNames == null) { + return false; + } + for (String excludedParameterName : excludedParameterNames) { + if (excludedParameterName != null && parameterName.equalsIgnoreCase(excludedParameterName)) { + return true; + } + } + return false; + } + protected void createStackFilterer() { try { Class filtererClass = grailsApplication.getConfig().getProperty(Settings.SETTING_LOGGING_STACKTRACE_FILTER_CLASS, Class.class, DefaultStackTraceFilterer.class); diff --git 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 index 0ef3ea062c9..707c2e74fc1 100644 --- 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 @@ -18,6 +18,8 @@ */ package org.grails.web.errors +import grails.config.Config +import grails.core.GrailsApplication import grails.web.mapping.UrlMappingsHolder import grails.web.mapping.exceptions.UrlMappingException import org.springframework.mock.web.MockHttpServletRequest @@ -43,4 +45,35 @@ class GrailsExceptionResolverSpec extends Specification { noExceptionThrown() params.isEmpty() } -} \ No newline at end of file + 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') + } + +}