Ejemplo n.º 1
0
async def enum(api, args):
    '''Enumerates challenges
    '''
    found = False
    for challenge in api.enum(tags=args.tags,
                              categories=args.categories,
                              slug=args.slug):
        slug, conf = challenge['slug'], challenge['conf']
        if not found:
            found = True
            print("challenges:")
        if conf is None:
            app_log.error(
                f"configuration missing. Run `mkctf configure -s {slug}`")
            continue
        chall_entry = f"{TAB}- {slug} [{conf['category'].upper()}]"
        color = 'green' if conf['enabled'] else 'red'
        chall_entry = format_text(chall_entry, color, attrs=['bold'])
        del conf['slug']
        del conf['enabled']
        del conf['category']
        description = challenge['description'] or format_text(
            'empty description', 'red', attrs=['bold'])
        chall_details = format_dict2str(conf)
        chall_details += "\n+ description:"
        chall_details += indent(f"\n{HSEP}\n{description}\n{HSEP}", TAB)
        print(chall_entry)
        if not args.summarize:
            print(indent(chall_details[1:], TAB * 2))
    if not found:
        app_log.warning("no challenge found.")
    return found
Ejemplo n.º 2
0
def readline(prompt, allow_empty=False, expect_digit=False, default=None):
    '''Reads one line
    '''
    value = ''
    if not isinstance(prompt, str):
        raise ValueError("prompt argument must be a string.")

    if default is not None:
        if expect_digit:
            if not isinstance(default, int):
                raise ValueError("default argument must be an integer.")
        elif not isinstance(default, str):
            raise ValueError("default argument must be an string.")
        prompt = prompt.strip()
        prompt = f"{prompt} [default={default}] "
    full_prompt = get_prompt(prompt)
    while True:
        value = input(full_prompt)
        if len(value) > 0:
            if expect_digit and not INT_RE.fullmatch(value):
                app_log.error("answer must be a digit.")
                continue
            break
        if default is not None:
            return default
        if allow_empty:
            return None
        app_log.error("empty answer is not allowed.")
    if expect_digit:
        value = int(value, 0)
    return value
Ejemplo n.º 3
0
def choose_one(prompt, choices, default=None, allow_custom=False):
    '''Elect one element among a collection

    You can also allow tht user to enter a custom value
    '''
    selection = default
    if not isinstance(choices, list):  # do not check number of elements
        raise ValueError("choices must be a list")
    has_default = default is not None
    if has_default and not default in choices:
        raise ValueError("default value must be one of choices.")
    print(get_prompt(prompt))
    k = 0
    for choice in choices:
        default_str = ''
        if choice == default:
            default_str = ' [default]'
        print(f"\t{k:02d}: {choices[k]}{default_str}")
        k += 1
    if allow_custom:
        print(f"\t{k:02d}: custom value")
    while True:
        choice = readline("enter a number: ",
                          allow_empty=has_default,
                          expect_digit=True)
        if choice is None:
            return default
        if choice >= 0 or choice <= k:
            break
        app_log.error(f"input must be in [0,{k}]")
    if allow_custom and choice == k:
        selection = readline("enter custom value: ")
    else:
        selection = choices[choice]
    return selection
Ejemplo n.º 4
0
async def enum(api, args):
    '''Enumerates challenges
    '''
    found = False
    print("challenges:")
    for challenge in api.enum(args.tags, args.slug):
        slug, conf = challenge['slug'], challenge['conf']
        found = True
        if conf is None:
            app_log.error(
                f"configuration missing. Run `mkctf configure -s {slug}`")
            continue
        static = ' [STANDALONE]' if conf['standalone'] else ''
        chall_entry = f"{TAB}- {slug}{static}"
        color = 'green' if conf['enabled'] else 'red'
        chall_entry = format_text(chall_entry, color, attrs=['bold'])
        del conf['enabled']
        del conf['standalone']
        del conf['slug']
        description = challenge['description'] or format_text(
            'empty description', 'red', attrs=['bold'])
        text = format_dict2str(conf)
        text += "\n+ description:"
        text += indent(f"\n{HSEP}\n{description}\n{HSEP}", TAB)
        print(chall_entry)
        print(indent(text[1:], TAB * 2))
    if not found:
        app_log.warning("no challenge found matching given constraints.")
    return found
Ejemplo n.º 5
0
 def _save_conf(self):
     '''Save repository configuration to disk
     '''
     if not self._conf.validate(throw=False):
         app_log.error("save operation aborted: invalid configuration")
         return False
     self._conf.save(self._conf_path)
     return True
Ejemplo n.º 6
0
 def create_chall(self, override_conf=None):
     '''Creates a challenge
     '''
     final_conf = override_conf
     if not final_conf:
         wizard = ChallengeConfigurationWizard(self.conf)
         if not wizard.show():
             return False
         final_conf = wizard.result
     chall_path = self.challenges_dir.joinpath(final_conf.slug)
     if chall_path.is_dir():
         app_log.error(f"{final_conf.slug} already exists")
         return False
     chall = Challenge(self, self.challenges_dir.joinpath(final_conf.slug),
                       final_conf)
     return chall.init()
Ejemplo n.º 7
0
    def export(self, export_dir, include_disabled):
        '''Export the challenge

        Creates a gzipped tar archive containing all of the challenge "exportable" files
        '''
        if not include_disabled and not self.conf.enabled:
            app_log.warning(f"export ignored {self.conf.slug} (disabled)")
            return

        app_log.info(f"exporting {self.conf.slug}...")
        archive_name = self.conf.static_url.split('/')[-1]
        if not archive_name:
            app_log.error(
                f"export ignored {self.conf.slug} (invalid/empty static_url)")
            app_log.error(
                f"running `mkctf-cli update-meta` should be enough to fix this issue."
            )
            return

        archive_path = export_dir.joinpath(archive_name)
        checksum_file = ChecksumFile()
        with tarfile.open(str(archive_path), 'w:gz') as arch:
            for directory in self.repo.conf.directories(self.conf.category,
                                                        public_only=True):
                dir_path = self.path.joinpath(directory)
                for entry in scandir(dir_path):
                    entry_path = Path(entry.path)
                    if entry_path.is_dir():
                        app_log.warning(
                            f"export ignored {entry_path} within {self.conf.slug} (directory)"
                        )
                        continue
                    checksum_file.add(entry_path)
                    app_log.debug(f"adding {entry_path} to archive...")
                    arch.add(str(entry_path), arcname=entry.name)
            with tempfile.NamedTemporaryFile('w') as tmpfile:
                tmpfile.write(checksum_file.content)
                tmpfile.flush()
                app_log.debug(f"adding checksum.sha256 to archive...")
                arch.add(tmpfile.name, arcname='checksum.sha256')

        arch_checksum_file = ChecksumFile()
        arch_checksum_file.add(archive_path)
        export_dir.joinpath(f'{archive_name}.sha256').write_text(
            arch_checksum_file.content)
        return archive_path
Ejemplo n.º 8
0
def readline(prompt, empty=False, digit=False, default=None):
    '''Reads one line
    '''
    if isinstance(default, tuple):
        default_str = default[1]
        default = default[0]
    else:
        default_str = default
    value = ''
    while True:
        value = input(build_prompt(prompt, default_str))
        if len(value) > 0:
            if digit and not INT_RE.fullmatch(value):
                app_log.error("answer must be a digit.")
                continue
            break
        if default is not None:
            return default
        if empty:
            return None
        app_log.error("empty answer is not allowed.")
    if digit:
        value = int(value, 0)
    return value