Filter token data out of catch_errors middleware
If an exception is caught by the catch_errors middleware the entire request is dumped into the log including sensitive information like tokens. Filter that information before outputting the failed request. Closes-Bug: #1628031 Change-Id: I2563403993513c37751576223275350cac2e0937
This commit is contained in:
parent
5307c7e43f
commit
df5c11ddcc
|
@ -14,6 +14,7 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
import webob.dec
|
import webob.dec
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
@ -24,6 +25,8 @@ from oslo_middleware import base
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_TOKEN_RE = re.compile('^(X-\w+-Token):.*$', flags=re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
class CatchErrors(base.ConfigurableMiddleware):
|
class CatchErrors(base.ConfigurableMiddleware):
|
||||||
"""Middleware that provides high-level error handling.
|
"""Middleware that provides high-level error handling.
|
||||||
|
@ -37,9 +40,8 @@ class CatchErrors(base.ConfigurableMiddleware):
|
||||||
try:
|
try:
|
||||||
response = req.get_response(self.application)
|
response = req.get_response(self.application)
|
||||||
except Exception:
|
except Exception:
|
||||||
if hasattr(req, 'environ') and 'HTTP_X_AUTH_TOKEN' in req.environ:
|
req_str = _TOKEN_RE.sub(r'\1: *****', req.as_text())
|
||||||
req.environ['HTTP_X_AUTH_TOKEN'] = '*****'
|
|
||||||
LOG.exception(_LE('An error occurred during '
|
LOG.exception(_LE('An error occurred during '
|
||||||
'processing the request: %s'), req)
|
'processing the request: %s'), req_str)
|
||||||
response = webob.exc.HTTPInternalServerError()
|
response = webob.exc.HTTPInternalServerError()
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import fixtures
|
||||||
import mock
|
import mock
|
||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
import webob.dec
|
import webob.dec
|
||||||
|
@ -48,3 +49,27 @@ class CatchErrorsTest(test_base.BaseTestCase):
|
||||||
self.assertEqual(1, log_exc.call_count)
|
self.assertEqual(1, log_exc.call_count)
|
||||||
req_log = log_exc.call_args[0][1]
|
req_log = log_exc.call_args[0][1]
|
||||||
self.assertIn('X-Auth-Token: *****', str(req_log))
|
self.assertIn('X-Auth-Token: *****', str(req_log))
|
||||||
|
|
||||||
|
def test_filter_tokens_from_log(self):
|
||||||
|
logger = self.useFixture(fixtures.FakeLogger(nuke_handlers=False))
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def application(req):
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
app = catch_errors.CatchErrors(application)
|
||||||
|
req = webob.Request.blank('/test',
|
||||||
|
text=u'test data',
|
||||||
|
method='POST',
|
||||||
|
headers={'X-Auth-Token': 'secret1',
|
||||||
|
'X-Service-Token': 'secret2',
|
||||||
|
'X-Other-Token': 'secret3'})
|
||||||
|
res = req.get_response(app)
|
||||||
|
self.assertEqual(500, res.status_int)
|
||||||
|
|
||||||
|
output = logger.output
|
||||||
|
|
||||||
|
self.assertIn('X-Auth-Token: *****', output)
|
||||||
|
self.assertIn('X-Service-Token: *****', output)
|
||||||
|
self.assertIn('X-Other-Token: *****', output)
|
||||||
|
self.assertIn('test data', output)
|
||||||
|
|
Loading…
Reference in New Issue