[100645]: / Allura / allura / lib / patches.py  Maximize  Restore  History

Repo status: analyzing...

Download this file

151 lines (127 with data), 6.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import sys
import traceback
import webob
import tg.decorators
from decorator import decorator
from tg import request
import paste.exceptions
from allura.lib import helpers as h
_patched = False
def apply():
global _patched # noqa: PLW0603
if _patched:
return
_patched = True
old_lookup_template_engine = tg.decorators.Decoration.lookup_template_engine
@h.monkeypatch(tg.decorators.Decoration)
def lookup_template_engine(self, request):
'''Wrapper to handle totally borked-up HTTP-ACCEPT headers'''
try:
return old_lookup_template_engine(self, request)
except Exception:
pass
environ = dict(request.environ, HTTP_ACCEPT='*/*')
request = webob.Request(environ)
return old_lookup_template_engine(self, request)
@h.monkeypatch(tg, tg.decorators)
def override_template(controller, template):
'''Copy-pasted patch to allow multiple colons in a template spec'''
if hasattr(controller, 'decoration'):
decoration = controller.decoration
else:
return
if hasattr(decoration, 'engines'):
engines = decoration.engines
else:
return
for content_type, content_engine in engines.items():
template = template.split(':', 1)
template.extend(content_engine[2:])
try:
override_mapping = request._override_mapping
except AttributeError:
override_mapping = request._override_mapping = {}
override_mapping[controller.__func__] = {content_type: template}
@h.monkeypatch(tg, tg.decorators)
@decorator
def without_trailing_slash(func, *args, **kwargs):
'''Monkey-patched to use 301 redirects for SEO, and handle query strings, and HEAD'''
__traceback_hide__ = 'before_and_this' # for paste/werkzeug shorter traces
response_type = getattr(request, 'response_type', None)
if (request.method in ('GET', 'HEAD') and request.path.endswith('/') and not response_type):
location = request.path_url[:-1]
if request.query_string:
location += '?' + request.query_string
raise webob.exc.HTTPMovedPermanently(location=location)
return func(*args, **kwargs)
@h.monkeypatch(tg, tg.decorators)
@decorator
def with_trailing_slash(func, *args, **kwargs):
'''Monkey-patched to use 301 redirects for SEO, and handle query strings, and HEAD'''
__traceback_hide__ = 'before_and_this' # for paste/werkzeug shorter traces
response_type = getattr(request, 'response_type', None)
if (request.method in ('GET', 'HEAD') and not request.path.endswith('/') and not response_type):
location = request.path_url + '/'
if request.query_string:
location += '?' + request.query_string
raise webob.exc.HTTPMovedPermanently(location=location)
return func(*args, **kwargs)
orig_Supplement_extraData = paste.exceptions.errormiddleware.Supplement.extraData
# add py3 chained exceptions into paste ErrorMiddleware output
@h.monkeypatch(paste.exceptions.errormiddleware.Supplement)
def extraData(*a, **kw):
data = orig_Supplement_extraData(*a, **kw)
curr_exc: BaseException = sys.exc_info()[1]
prev_exc: BaseException = getattr(curr_exc, '__context__', None)
# TODO this chains use "raise .. from None" How to detect that? __suppress_context__ isn't an indicator
if prev_exc:
# ErrorMiddleware formatters will take this 'extra' stuff and put it into the output
data[('extra', '\nChained Exception')] = '\n' + ''.join(
traceback.format_exception(type(prev_exc), prev_exc, prev_exc.__traceback__)
)
return data
# http://blog.watchfire.com/wfblog/2011/10/json-based-xss-exploitation.html
# change < to its unicode escape when rendering JSON out of turbogears
# This is to avoid IE9 and earlier, which don't know the json content type
# and may attempt to render JSON data as HTML if the URL ends in .html
original_tg_jsonify_JSONEncoder_encode = tg.jsonify.JSONEncoder.encode
@h.monkeypatch(tg.jsonify.JSONEncoder)
def encode(self, o):
return original_tg_jsonify_JSONEncoder_encode(self, o).replace('<', r'\u003C')
old_controller_call = tg.controllers.DecoratedController._call
def newrelic():
@h.monkeypatch(tg.controllers.DecoratedController,
tg.controllers.decoratedcontroller.DecoratedController)
def _call(self, controller, *args, **kwargs):
'''Set NewRelic transaction name to actual controller name'''
__traceback_hide__ = 'before_and_this' # for paste/werkzeug shorter traces
import newrelic.agent
controller_name = newrelic.agent.callable_name(controller)
# https://docs.newrelic.com/docs/apm/agents/python-agent/python-agent-api/settransactionname-python-agent-api/
# if a second internal request for /error/document happens, use a lower (1) priority so original name stays
name_priority = 1 if 'ErrorController' in controller_name else 2
newrelic.agent.set_transaction_name(controller_name, priority=name_priority)
return old_controller_call(self, controller, *args, **kwargs)
import newrelic.api.error_trace
import newrelic.api.function_trace
# These are based on newrelic/hooks/framework_pylons.py since TG is similar to Pylons
# capture exceptions:
newrelic.api.error_trace.wrap_error_trace('tg.wsgiapp', 'TGApp.__call__')
# record as its own component in transaction breakdown; should help distinguish middleware vs controller time
newrelic.api.function_trace.wrap_function_trace('tg.controllers.tgcontroller', 'TGController.__call__')