# -*- coding: utf-8 -*-"""werkzeug.debug~~~~~~~~~~~~~~WSGI application traceback debugger.:copyright: 2007 Pallets:license: BSD-3-Clause"""import getpassimport hashlibimport jsonimport mimetypesimport osimport pkgutilimport reimport sysimport timeimport uuidfrom itertools import chainfrom os.path import basenamefrom os.path import joinfrom .._compat import text_typefrom .._internal import _logfrom ..http import parse_cookiefrom ..security import gen_saltfrom ..wrappers import BaseRequest as Requestfrom ..wrappers import BaseResponse as Responsefrom .console import Consolefrom .tbtools import get_current_tracebackfrom .tbtools import render_console_html# A weekPIN_TIME = 60 * 60 * 24 * 7def hash_pin(pin):if isinstance(pin, text_type):pin = pin.encode("utf-8", "replace")return hashlib.md5(pin + b"shittysalt").hexdigest()[:12]_machine_id = Nonedef get_machine_id():global _machine_idif _machine_id is not None:return _machine_iddef _generate():linux = b""# machine-id is stable across boots, boot_id is not.for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id":try:with open(filename, "rb") as f:value = f.readline().strip()except IOError:continueif value:linux += valuebreak# Containers share the same machine id, add some cgroup# information. This is used outside containers too but should be# relatively stable across boots.try:with open("/proc/self/cgroup", "rb") as f:linux += f.readline().strip().rpartition(b"/")[2]except IOError:passif linux:return linux# On OS X, use ioreg to get the computer's serial number.try:# subprocess may not be available, e.g. Google App Engine# https://github.com/pallets/werkzeug/issues/925from subprocess import Popen, PIPEdump = Popen(["ioreg", "-c", "IOPlatformExpertDevice", "-d", "2"], stdout=PIPE).communicate()[0]match = re.search(b'"serial-number" = <([^>]+)', dump)if match is not None:return match.group(1)except (OSError, ImportError):pass# On Windows, use winreg to get the machine guid.try:import winreg as wrexcept ImportError:try:import _winreg as wrexcept ImportError:wr = Noneif wr is not None:try:with wr.OpenKey(wr.HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Cryptography",0,wr.KEY_READ | wr.KEY_WOW64_64KEY,) as rk:guid, guid_type = wr.QueryValueEx(rk, "MachineGuid")if guid_type == wr.REG_SZ:return guid.encode("utf-8")return guidexcept WindowsError:pass_machine_id = _generate()return _machine_idclass _ConsoleFrame(object):"""Helper class so that we can reuse the frame console code for thestandalone console."""def __init__(self, namespace):self.console = Console(namespace)self.id = 0def get_pin_and_cookie_name(app):"""Given an application object this returns a semi-stable 9 digit pincode and a random key. The hope is that this is stable betweenrestarts to not make debugging particularly frustrating. If the pinwas forcefully disabled this returns `None`.Second item in the resulting tuple is the cookie name for remembering."""pin = os.environ.get("WERKZEUG_DEBUG_PIN")rv = Nonenum = None# Pin was explicitly disabledif pin == "off":return None, None# Pin was provided explicitlyif pin is not None and pin.replace("-", "").isdigit():# If there are separators in the pin, return it directlyif "-" in pin:rv = pinelse:num = pinmodname = getattr(app, "__module__", app.__class__.__module__)try:# getuser imports the pwd module, which does not exist in Google# App Engine. It may also raise a KeyError if the UID does not# have a username, such as in Docker.username = getpass.getuser()except (ImportError, KeyError):username = Nonemod = sys.modules.get(modname)# This information only exists to make the cookie unique on the# computer, not as a security feature.probably_public_bits = [username,modname,getattr(app, "__name__", app.__class__.__name__),getattr(mod, "__file__", None),]# This information is here to make it harder for an attacker to# guess the cookie name. They are unlikely to be contained anywhere# within the unauthenticated debug page.private_bits = [str(uuid.getnode()), get_machine_id()]h = hashlib.md5()for bit in chain(probably_public_bits, private_bits):if not bit:continueif isinstance(bit, text_type):bit = bit.encode("utf-8")h.update(bit)h.update(b"cookiesalt")cookie_name = "__wzd" + h.hexdigest()[:20]# If we need to generate a pin we salt it a bit more so that we don't# end up with the same value and generate out 9 digitsif num is None:h.update(b"pinsalt")num = ("%09d" % int(h.hexdigest(), 16))[:9]# Format the pincode in groups of digits for easier remembering if# we don't have a result yet.if rv is None:for group_size in 5, 4, 3:if len(num) % group_size == 0:rv = "-".join(num[x : x + group_size].rjust(group_size, "0")for x in range(0, len(num), group_size))breakelse:rv = numreturn rv, cookie_nameclass DebuggedApplication(object):"""Enables debugging support for a given application::from werkzeug.debug import DebuggedApplicationfrom myapp import appapp = DebuggedApplication(app, evalex=True)The `evalex` keyword argument allows evaluating expressions in atraceback's frame context.:param app: the WSGI application to run debugged.:param evalex: enable exception evaluation feature (interactivedebugging). This requires a non-forking server.:param request_key: The key that points to the request object in thsenvironment. This parameter is ignored in currentversions.:param console_path: the URL for a general purpose console.:param console_init_func: the function that is executed before startingthe general purpose console. The return valueis used as initial namespace.:param show_hidden_frames: by default hidden traceback frames are skipped.You can show them by setting this parameterto `True`.:param pin_security: can be used to disable the pin based security system.:param pin_logging: enables the logging of the pin system."""def __init__(self,app,evalex=False,request_key="werkzeug.request",console_path="/console",console_init_func=None,show_hidden_frames=False,pin_security=True,pin_logging=True,):if not console_init_func:console_init_func = Noneself.app = appself.evalex = evalexself.frames = {}self.tracebacks = {}self.request_key = request_keyself.console_path = console_pathself.console_init_func = console_init_funcself.show_hidden_frames = show_hidden_framesself.secret = gen_salt(20)self._failed_pin_auth = 0self.pin_logging = pin_loggingif pin_security:# Print out the pin for the debugger on standard out.if os.environ.get("WERKZEUG_RUN_MAIN") == "true" and pin_logging:_log("warning", " * Debugger is active!")if self.pin is None:_log("warning", " * Debugger PIN disabled. DEBUGGER UNSECURED!")else:_log("info", " * Debugger PIN: %s" % self.pin)else:self.pin = None@propertydef pin(self):if not hasattr(self, "_pin"):self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app)return self._pin@pin.setterdef pin(self, value):self._pin = value@propertydef pin_cookie_name(self):"""The name of the pin cookie."""if not hasattr(self, "_pin_cookie"):self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app)return self._pin_cookiedef debug_application(self, environ, start_response):"""Run the application and conserve the traceback frames."""app_iter = Nonetry:app_iter = self.app(environ, start_response)for item in app_iter:yield itemif hasattr(app_iter, "close"):app_iter.close()except Exception:if hasattr(app_iter, "close"):app_iter.close()traceback = get_current_traceback(skip=1,show_hidden_frames=self.show_hidden_frames,ignore_system_exceptions=True,)for frame in traceback.frames:self.frames[frame.id] = frameself.tracebacks[traceback.id] = tracebacktry:start_response("500 INTERNAL SERVER ERROR",[("Content-Type", "text/html; charset=utf-8"),# Disable Chrome's XSS protection, the debug# output can cause false-positives.("X-XSS-Protection", "0"),],)except Exception:# if we end up here there has been output but an error# occurred. in that situation we can do nothing fancy any# more, better log something into the error log and fall# back gracefully.environ["wsgi.errors"].write("Debugging middleware caught exception in streamed ""response at a point where response headers were already ""sent.\n")else:is_trusted = bool(self.check_pin_trust(environ))yield traceback.render_full(evalex=self.evalex, evalex_trusted=is_trusted, secret=self.secret).encode("utf-8", "replace")traceback.log(environ["wsgi.errors"])def execute_command(self, request, command, frame):"""Execute a command in a console."""return Response(frame.console.eval(command), mimetype="text/html")def display_console(self, request):"""Display a standalone shell."""if 0 not in self.frames:if self.console_init_func is None:ns = {}else:ns = dict(self.console_init_func())ns.setdefault("app", self.app)self.frames[0] = _ConsoleFrame(ns)is_trusted = bool(self.check_pin_trust(request.environ))return Response(render_console_html(secret=self.secret, evalex_trusted=is_trusted),mimetype="text/html",)def paste_traceback(self, request, traceback):"""Paste the traceback and return a JSON response."""rv = traceback.paste()return Response(json.dumps(rv), mimetype="application/json")def get_resource(self, request, filename):"""Return a static resource from the shared folder."""filename = join("shared", basename(filename))try:data = pkgutil.get_data(__package__, filename)except OSError:data = Noneif data is not None:mimetype = mimetypes.guess_type(filename)[0] or "application/octet-stream"return Response(data, mimetype=mimetype)return Response("Not Found", status=404)def check_pin_trust(self, environ):"""Checks if the request passed the pin test. This returns `True` if therequest is trusted on a pin/cookie basis and returns `False` if not.Additionally if the cookie's stored pin hash is wrong it will return`None` so that appropriate action can be taken."""if self.pin is None:return Trueval = parse_cookie(environ).get(self.pin_cookie_name)if not val or "|" not in val:return Falsets, pin_hash = val.split("|", 1)if not ts.isdigit():return Falseif pin_hash != hash_pin(self.pin):return Nonereturn (time.time() - PIN_TIME) < int(ts)def _fail_pin_auth(self):time.sleep(5.0 if self._failed_pin_auth > 5 else 0.5)self._failed_pin_auth += 1def pin_auth(self, request):"""Authenticates with the pin."""exhausted = Falseauth = Falsetrust = self.check_pin_trust(request.environ)# If the trust return value is `None` it means that the cookie is# set but the stored pin hash value is bad. This means that the# pin was changed. In this case we count a bad auth and unset the# cookie. This way it becomes harder to guess the cookie name# instead of the pin as we still count up failures.bad_cookie = Falseif trust is None:self._fail_pin_auth()bad_cookie = True# If we're trusted, we're authenticated.elif trust:auth = True# If we failed too many times, then we're locked out.elif self._failed_pin_auth > 10:exhausted = True# Otherwise go through pin based authenticationelse:entered_pin = request.args.get("pin")if entered_pin.strip().replace("-", "") == self.pin.replace("-", ""):self._failed_pin_auth = 0auth = Trueelse:self._fail_pin_auth()rv = Response(json.dumps({"auth": auth, "exhausted": exhausted}),mimetype="application/json",)if auth:rv.set_cookie(self.pin_cookie_name,"%s|%s" % (int(time.time()), hash_pin(self.pin)),httponly=True,)elif bad_cookie:rv.delete_cookie(self.pin_cookie_name)return rvdef log_pin_request(self):"""Log the pin if needed."""if self.pin_logging and self.pin is not None:_log("info", " * To enable the debugger you need to enter the security pin:")_log("info", " * Debugger pin code: %s" % self.pin)return Response("")def __call__(self, environ, start_response):"""Dispatch the requests."""# important: don't ever access a function here that reads the incoming# form data! Otherwise the application won't have access to that data# any more!request = Request(environ)response = self.debug_applicationif request.args.get("__debugger__") == "yes":cmd = request.args.get("cmd")arg = request.args.get("f")secret = request.args.get("s")traceback = self.tracebacks.get(request.args.get("tb", type=int))frame = self.frames.get(request.args.get("frm", type=int))if cmd == "resource" and arg:response = self.get_resource(request, arg)elif cmd == "paste" and traceback is not None and secret == self.secret:response = self.paste_traceback(request, traceback)elif cmd == "pinauth" and secret == self.secret:response = self.pin_auth(request)elif cmd == "printpin" and secret == self.secret:response = self.log_pin_request()elif (self.evalexand cmd is not Noneand frame is not Noneand self.secret == secretand self.check_pin_trust(environ)):response = self.execute_command(request, cmd, frame)elif (self.evalexand self.console_path is not Noneand request.path == self.console_path):response = self.display_console(request)return response(environ, start_response)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。