creeper-adventure/.venv/lib/python3.8/site-packages/rope/base/oi/doa.py
2022-03-31 20:20:07 -05:00

233 lines
7.2 KiB
Python

import base64
import hashlib
import hmac
try:
import cPickle as pickle
except ImportError:
import pickle
import marshal
import os
import socket
import subprocess
import sys
import tempfile
import threading
def _compat_compare_digest(a, b):
"""Implementation of hmac.compare_digest for python < 2.7.7.
This function uses an approach designed to prevent timing analysis by
avoiding content-based short circuiting behaviour, making it appropriate
for cryptography.
"""
if len(a) != len(b):
return False
# Computes the bitwise difference of all characters in the two strings
# before returning whether or not they are equal.
difference = 0
for (a_char, b_char) in zip(a, b):
difference |= ord(a_char) ^ ord(b_char)
return difference == 0
try:
from hmac import compare_digest
except ImportError:
compare_digest = _compat_compare_digest
class PythonFileRunner(object):
"""A class for running python project files"""
def __init__(
self, pycore, file_, args=None, stdin=None, stdout=None, analyze_data=None
):
self.pycore = pycore
self.file = file_
self.analyze_data = analyze_data
self.observers = []
self.args = args
self.stdin = stdin
self.stdout = stdout
def run(self):
"""Execute the process"""
env = dict(os.environ)
file_path = self.file.real_path
path_folders = (
self.pycore.project.get_source_folders()
+ self.pycore.project.get_python_path_folders()
)
env["PYTHONPATH"] = os.pathsep.join(folder.real_path for folder in path_folders)
runmod_path = self.pycore.project.find_module("rope.base.oi.runmod").real_path
self.receiver = None
self._init_data_receiving()
send_info = "-"
if self.receiver:
send_info = self.receiver.get_send_info()
args = [
sys.executable,
runmod_path,
send_info,
self.pycore.project.address,
self.file.real_path,
]
if self.analyze_data is None:
del args[1:4]
if self.args is not None:
args.extend(self.args)
self.process = subprocess.Popen(
executable=sys.executable,
args=args,
env=env,
cwd=os.path.split(file_path)[0],
stdin=self.stdin,
stdout=self.stdout,
stderr=self.stdout,
close_fds=os.name != "nt",
)
def _init_data_receiving(self):
if self.analyze_data is None:
return
# Disabling FIFO data transfer due to blocking when running
# unittests in the GUI.
# XXX: Handle FIFO data transfer for `rope.ui.testview`
if True or os.name == "nt":
self.receiver = _SocketReceiver()
else:
self.receiver = _FIFOReceiver()
self.receiving_thread = threading.Thread(target=self._receive_information)
self.receiving_thread.setDaemon(True)
self.receiving_thread.start()
def _receive_information(self):
# temp = open('/dev/shm/info', 'wb')
for data in self.receiver.receive_data():
self.analyze_data(data)
# temp.write(str(data) + '\n')
# temp.close()
for observer in self.observers:
observer()
def wait_process(self):
"""Wait for the process to finish"""
self.process.wait()
if self.analyze_data:
self.receiving_thread.join()
def kill_process(self):
"""Stop the process"""
if self.process.poll() is not None:
return
try:
if hasattr(self.process, "terminate"):
self.process.terminate()
elif os.name != "nt":
os.kill(self.process.pid, 9)
else:
import ctypes
handle = int(self.process._handle)
ctypes.windll.kernel32.TerminateProcess(handle, -1)
except OSError:
pass
def add_finishing_observer(self, observer):
"""Notify this observer when execution finishes"""
self.observers.append(observer)
class _MessageReceiver(object):
def receive_data(self):
pass
def get_send_info(self):
pass
class _SocketReceiver(_MessageReceiver):
def __init__(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.data_port = 3037
self.key = os.urandom(32)
while self.data_port < 4000:
try:
self.server_socket.bind(("localhost", self.data_port))
break
except socket.error:
self.data_port += 1
self.server_socket.listen(1)
def get_send_info(self):
return "%d:%s" % (self.data_port, base64.b64encode(self.key).decode("utf-8"))
def receive_data(self):
conn, addr = self.server_socket.accept()
self.server_socket.close()
my_file = conn.makefile("rb")
while True:
# Received messages must meet the following criteria:
# 1. Must be contained on a single line.
# 2. Must be prefixed with a base64 encoded sha256 message digest
# of the base64 encoded pickle data.
# 3. Message digest must be computed using the correct key.
#
# Any messages received that do not meet these criteria will never
# be unpickled and will be dropped silently.
try:
buf = my_file.readline()
if len(buf) == 0:
break
try:
digest_end = buf.index(b":")
buf_digest = base64.b64decode(buf[:digest_end])
buf_data = buf[digest_end + 1 : -1]
decoded_buf_data = base64.b64decode(buf_data)
except:
# Corrupted data; the payload cannot be trusted and just has
# to be dropped. See CVE-2014-3539.
continue
digest = hmac.new(self.key, buf_data, hashlib.sha256).digest()
if not compare_digest(buf_digest, digest):
# Signature mismatch; the payload cannot be trusted and just
# has to be dropped. See CVE-2014-3539.
continue
yield pickle.loads(decoded_buf_data)
except EOFError:
break
my_file.close()
conn.close()
class _FIFOReceiver(_MessageReceiver):
def __init__(self):
# XXX: this is insecure and might cause race conditions
self.file_name = self._get_file_name()
os.mkfifo(self.file_name)
def _get_file_name(self):
prefix = tempfile.gettempdir() + "/__rope_"
i = 0
while os.path.exists(prefix + str(i).rjust(4, "0")):
i += 1
return prefix + str(i).rjust(4, "0")
def get_send_info(self):
return self.file_name
def receive_data(self):
my_file = open(self.file_name, "rb")
while True:
try:
yield marshal.load(my_file)
except EOFError:
break
my_file.close()
os.remove(self.file_name)