Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
{
"name": "grails.exceptionresolver.params.exclude",
"type": "java.util.List<java.lang.String>",
"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": []
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand All @@ -330,6 +330,18 @@ protected String getRequestLogMessage(String exceptionName, HttpServletRequest r
return sb.toString();
}

protected boolean isExcludedRequestParameter(String parameterName, List<String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,4 +45,35 @@ class GrailsExceptionResolverSpec extends Specification {
noExceptionThrown()
params.isEmpty()
}
}
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')
}

}
Loading