Exemple #1
0
    async def run_dm(self, loop, instance_dir, major, minor):
        """Executor proc to host and run the .dmb file provided."""
        dd_path = os.path.join(
            self.get_work_dir(),
            f"byond{major}.{minor}\\byond\\bin\\dreamdaemon.exe")
        if not os.path.isfile(dd_path):
            raise BotError("dreadaemon.exe not found.", "run_dm")

        dmb_path = os.path.join(instance_dir, "eval.dmb")
        if not os.path.isfile(dmb_path):
            raise BotError(".dmb under evaluation not found.", "run_dm")

        p_args = [dd_path, dmb_path] + [
            "-invisible", "-ultrasafe", "-logself", "-log", "output.log",
            "-once", "-close", "-quiet"
        ]

        process = await asyncio.create_subprocess_exec(
            *p_args,
            loop=loop,
            stderr=asyncio.subprocess.DEVNULL,
            stdout=asyncio.subprocess.DEVNULL)

        try:
            await asyncio.wait_for(process.wait(), timeout=60.0, loop=loop)
        except TimeoutError:
            raise BotError("DreamDaemon timed out.", "run_dm")
Exemple #2
0
    def validate_compile(self, instance_dir):
        """Checks wether or not the compiled end result is safe to run."""
        dmb_found = False

        for fname in os.listdir(instance_dir):
            if fname.endswith(".rsc"):
                raise BotError("Resource file detected. Execution aborted.",
                               "validate_compile")
            elif fname.endswith(".dmb"):
                dmb_found = True

        if not dmb_found:
            raise BotError("Compilation failed and no .dmb was generated.",
                           "validate_compile")
Exemple #3
0
def validate_byond_build(byond_str):
    """
    A little shit of a failed command argument.
    
    Return a tuple containing (major, minor) build information if the argument
    string matches the defined format of: v:{major}.{minor} {rest of code here}.
    Returns None if such a tuple can't be generated.
    """
    if not byond_str.startswith("v:"):
        return None

    chunks = byond_str.split(" ")
    if not len(chunks) > 1:
        return None

    chunks = chunks[0].split(".")

    # This is triggered alyways forever. So. Return null if format doesn't match.
    if len(chunks) != 2:
        return None

    try:
        major = int(chunks[0][2:])
        minor = int(chunks[1])
    except ValueError:
        raise BotError("Error processing BYOND version request.",
                       "validate_byond_build")

    return major, minor
Exemple #4
0
    def validate_dm(self, code):
        """Validates the code given for potential exploits."""

        for expr in self._safety_expressions:
            if expr.search(code):
                raise BotError("Disallowed/dangerous code found. Aborting.",
                               "validate_dm")
Exemple #5
0
    def process_args(self, code):
        """
        Generates an array of code segments to be placed into the compiled DM code.
        
        Returned dictionary must have three keys: "pre_proc", "proc", and "to_out".
        If those pieces do not exist, they are to be set as None. As to avoid key
        errors further down the call stack.
        """

        res = self._arg_expression.match(code)

        if not res or not res.groupdict():
            raise BotError("No valid code sent.", "process_args")

        code_segs = {"pre_proc": None, "proc": None, "to_out": None}

        res_dict = res.groupdict()

        for key in code_segs:
            if key in res_dict:
                code_segs[key] = res_dict[key]

        if (code_segs["pre_proc"] and not code_segs["pre_proc"].endswith(";")
                and not code_segs["pre_proc"].endswith("}")):
            code_segs["pre_proc"] += ";"

        if (code_segs["proc"] and not code_segs["proc"].endswith(";")
                and not code_segs["proc"].endswith(";")):
            code_segs["proc"] += ";"

        if code_segs["to_out"]:
            code_segs["to_out"] = code_segs["to_out"].split(";")

        return code_segs
Exemple #6
0
    async def setup_byond(self, major=DEFAULT_MAJOR, minor=DEFAULT_MINOR):
        """Downloads and unzips the provided BYOND version."""
        path = self.get_work_dir()
        byond_path = os.path.join(path, f"byond{major}.{minor}")

        url = f"http://www.byond.com/download/build/{major}/{major}.{minor}_byond.zip"

        async with aiohttp.ClientSession() as session:
            async with session.get(url) as resp:
                try:
                    data = await resp.read()
                except Exception:
                    raise BotError("Unable to download the BYOND zip file.",
                                   "init_byond")

                if resp.status != 200:
                    raise BotError(
                        "Unable to download the specified BYOND version.",
                        "init_byond")

                with ZipFile(BytesIO(data)) as z:
                    z.extractall(byond_path)
Exemple #7
0
    def get_repo(self):
        """Gets the appropriate github API object."""
        conf = self.bot.Config()

        if not conf.github["api_token"]:
            raise BotError("Github API token not provided.", "get_repo")

        try:
            git = github.Github(conf.github["api_token"])
        except github.GithubException:
            raise BotError("Unable to login to Github.", "get_repo")

        try:
            org = git.get_organization(conf.github["wiki_org"])
        except github.GithubException:
            raise BotError("Unable to fetch the organization.", "get_repo")

        try:
            repo = org.get_repo(conf.github["wiki_repo"])
        except github.GithubException:
            raise BotError("Unable to acquire the repository.", "get_repo")

        return repo
Exemple #8
0
    async def compile_dm(self, loop, instance_dir, major, minor):
        """Executor proc to compile the .dme file provided."""
        dm_path = os.path.join(self.get_work_dir(),
                               f"byond{major}.{minor}\\byond\\bin\\dm.exe")
        if not os.path.isfile(dm_path):
            raise BotError("dm.exe not found.", "compile_dm")

        dme_path = os.path.join(instance_dir, "eval.dme")
        if not os.path.isfile(dme_path):
            raise BotError(".dme under evaluation not found.", "compile_dm")

        process = await asyncio.create_subprocess_exec(
            *[dm_path, dme_path],
            loop=loop,
            stderr=asyncio.subprocess.DEVNULL,
            stdout=asyncio.subprocess.DEVNULL)

        try:
            await asyncio.wait_for(process.wait(), timeout=60.0, loop=loop)
        except TimeoutError:
            raise BotError("Compiler timed out.", "compile_dm")

        if process.returncode != 0:
            raise BotError("Error compiling or running DM.", "compile_dm")
Exemple #9
0
    async def run_executor(self, proc, p_args):
        """A helper for running Windows subprocesses in a separate thread."""
        thread = WindowsProcessThread(proc, p_args)

        thread.start()

        cycles = 0

        while cycles < 60:
            if not thread.is_alive():
                break

            cycles += 1
            await asyncio.sleep(1)

        error = thread.errored
        error_msg = thread.error_msg

        thread.join()

        if error:
            raise BotError(error_msg, "run_executor")