Skip to content

Commit

Permalink
Change model
Browse files Browse the repository at this point in the history
  • Loading branch information
koromodako committed Apr 11, 2019
1 parent 6fca6fe commit 05afdb0
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 61 deletions.
18 changes: 13 additions & 5 deletions mkctf.yml.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Default categories to be created for each CTF
# Default categories to be created for each mkCTF repository
#
tags:
- bugbounty
Expand All @@ -11,6 +11,15 @@ tags:
- re
- web
#
# Default difficulties to be created for each mkCTF repository
#
difficulties:
- trivial
- easy
- medium
- hard
- extreme
#
# Default directories to be created for each challenge
#
directories:
Expand All @@ -27,13 +36,12 @@ files:
txt:
- Dockerfile
- writeup.md
- public-files/description.md
build: build
deploy: deploy
status: exploit/exploit
config:
challenge: .mkctf.yml
repository: .repo.mkctf.yml
repo_conf: .mkctf.repo.yml
chall_conf: .mkctf.yml
description: public-files/description.md
#
# Default flag information
#
Expand Down
4 changes: 2 additions & 2 deletions mkctf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__major__ = 3
__minor__ = 1
__major__ = 4
__minor__ = 0
__patch__ = 0
__version_info__ = (__major__, __minor__, __patch__)
__version__ = f'{__major__}.{__minor__}.{__patch__}'
Expand Down
8 changes: 6 additions & 2 deletions mkctf/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def __init__(self, repo_root, config_path=None):
self._glob_conf = config_load(self._glob_conf_path)
app_log.debug(f"glob_conf: {self._glob_conf}")

self._repo_conf_path = self._repo_root / self._glob_conf['files']['config']['repository']
self._repo_conf_path = self._repo_root / self._glob_conf['files']['repo_conf']
app_log.debug(f"repo_conf_path: {self._repo_conf_path}")

self._repo = Repository(self._repo_conf_path, self._glob_conf)
Expand Down Expand Up @@ -66,7 +66,11 @@ def enum(self, tags=[], slug=None):
self.__assert_valid_repo()
for challenge in self._repo.scan(tags):
if slug is None or slug == challenge.slug:
yield {'slug': challenge.slug, 'conf': challenge.get_conf()}
yield {
'slug': challenge.slug,
'conf': challenge.get_conf(),
'description': challenge.description,
}

def create(self, configuration=None):
'''
Expand Down
9 changes: 6 additions & 3 deletions mkctf/cli/enum.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =============================================================================
# IMPORTS
# =============================================================================

from textwrap import indent, wrap
from mkctf.helper.log import app_log
from mkctf.helper.formatting import TAB, HSEP, format_text, format_dict2str
# =============================================================================
Expand All @@ -25,9 +25,12 @@ async def enum(api, args):
del conf['enabled']
del conf['standalone']
del conf['slug']
text = format_dict2str(conf).replace("\n", f"\n{TAB}{TAB}")
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(text[1:])
print(indent(text[1:], TAB * 2))
if not found:
app_log.warning("no challenge found matching given constraints.")
return found
Expand Down
4 changes: 3 additions & 1 deletion mkctf/helper/cli.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# =============================================================================
# IMPORTS
# =============================================================================
import re
from mkctf.helper.log import app_log
# =============================================================================
# GLOBALS
# =============================================================================
INT_RE = re.compile(r'[\-]?[0-9]+')
PROG_PROMPT = f"[mkctf](?)>"
# =============================================================================
# CLASSES
Expand Down Expand Up @@ -34,7 +36,7 @@ def readline(prompt, allow_empty=False, expect_digit=False, default=None):
while True:
value = input(full_prompt)
if len(value) > 0:
if expect_digit and not value.isdigit():
if expect_digit and not INT_RE.fullmatch(value):
app_log.error("answer must be a digit.")
continue
break
Expand Down
2 changes: 1 addition & 1 deletion mkctf/helper/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# =============================================================================
# GLOBALS
# =============================================================================
TAB = ' ' * 4
TAB = ' ' * 2
HSEP = '-' * 80
COLORIZE = not WINDOWS
EXIT_CODE_MAP = {
Expand Down
10 changes: 9 additions & 1 deletion mkctf/object/challenge.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ def is_standalone(self):
'''
return self.get_conf('standalone')

@property
def description(self):
'''Retrieve challenge description from filesystem
'''
desc_path = self.working_dir().joinpath(self.repo_conf['files']['description'])
if desc_path.is_file():
return desc_path.read_text()
return None

@property
def enabled(self):
'''Determines if challenge is enabled
Expand Down Expand Up @@ -259,4 +268,3 @@ async def status(self, timeout=4):
'''
return await self.__run(self.repo_conf['files']['status'], timeout)


94 changes: 48 additions & 46 deletions mkctf/object/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,90 +21,92 @@ def __init__(self, conf_path, glob_conf):
super().__init__(conf_path)
self.glob_conf = glob_conf

def __make_repo_conf(self, previous={}, override=None):
def __make_repo_conf(self, prev={}, override=None):
'''[summary]
'''
if previous is None:
previous = {}
if prev is None:
prev = {}
if override:
conf = previous
conf = prev
conf.update(override)
else:
name = previous.get('name')
tags = previous.get('tags')
pub_dirs = previous.get('directories', {}).get('public')
priv_dirs = previous.get('directories', {}).get('private')
txt_files = previous.get('files', {}).get('txt')
chall_file = previous.get('files', {}).get('config', {}).get('challenge')
build_file = previous.get('files', {}).get('build')
deploy_file = previous.get('files', {}).get('deploy')
status_file = previous.get('files', {}).get('status')
flag_prefix = previous.get('flag', {}).get('prefix')
flag_suffix = previous.get('flag', {}).get('suffix')
name = cli.readline("enter repository name:", default=name)
tags = prev.get('tags')
difficulties = prev.get('difficulties')
dirs = prev.get('directories', {})
pub_dirs = dirs.get('public')
priv_dirs = dirs.get('private')
files = prev.get('files', {})
txt_files = files.get('txt')
flag = prev.get('flag', {})
name = cli.readline("enter repository name:", default=prev.get('name'))
tags = cli.choose_many("select tags:", tags, default=tags)
difficulties = cli.choose_many("select difficulties:", difficulties, default=difficulties)
pub_dirs = cli.choose_many("select public directories:", pub_dirs, default=pub_dirs)
priv_dirs = cli.choose_many("select private directories:", priv_dirs, default=priv_dirs)
txt_files = cli.choose_many("select text files:", txt_files, default=txt_files)
chall_file = cli.readline("enter challenge file name:", default=chall_file)
build_file = cli.readline("enter build file name:", default=build_file)
deploy_file = cli.readline("enter deploy file name:", default=deploy_file)
status_file = cli.readline("enter status file name:", default=status_file)
flag_prefix = cli.readline("enter flag prefix:", default=flag_prefix)
flag_suffix = cli.readline("enter flag suffix:", default=flag_suffix)
chall_file = cli.readline("enter challenge file name:", default=files.get('chall_conf'))
build_file = cli.readline("enter build file name:", default=files.get('build'))
deploy_file = cli.readline("enter deploy file name:", default=files.get('deploy'))
status_file = cli.readline("enter status file name:", default=files.get('status'))
description_file = cli.readline("enter description file name:", default=files.get('description'))
flag_prefix = cli.readline("enter flag prefix:", default=flag.get('prefix'))
flag_suffix = cli.readline("enter flag suffix:", default=flag.get('suffix'))
conf = {
'name': name,
'tags': tags,
'difficulties': difficulties,
'directories': {
'public': pub_dirs,
'private': priv_dirs
'private': priv_dirs,
},
'files': {
'txt': txt_files,
'build': build_file,
'deploy': deploy_file,
'status': status_file,
'config': {
'challenge': chall_file
}
'chall_conf': chall_file,
'description': description_file,
},
'flag': {
'prefix': flag_prefix,
'suffix': flag_suffix
'suffix': flag_suffix,
}
}
return conf

def __make_chall_conf(self, previous={}, override=None):
def __make_chall_conf(self, prev={}, override=None):
'''[summary]
'''
repo_conf = self.get_conf()
if previous is None:
previous = {}
if prev is None:
prev = {}
if override:
conf = previous
conf = prev
conf.update(override)
else:
flag = previous.get('flag', Challenge.make_flag(repo_conf))
enabled = previous.get('enabled', False)
parameters = previous.get('parameters', {})
name = previous.get('name')
tags = previous.get('tags')
points = previous.get('points')
standalone = previous.get('standalone')
name = cli.readline("enter challenge name:", default=name)
tags = cli.choose_many("select one or more tags:", repo_conf['tags'], default=tags)
points = cli.readline("enter number of points:", default=points, expect_digit=True)
standalone = cli.confirm("is it a standalone challenge?", default=standalone)
flag = prev.get('flag', Challenge.make_flag(repo_conf))
name = cli.readline("enter challenge name:", default=prev.get('name'))
tags = cli.choose_many("select one or more tags:", repo_conf['tags'], default=prev.get('tags'))
points = cli.readline("enter number of points:", default=prev.get('points'), expect_digit=True)
enabled = prev.get('enabled', False)
difficulties = repo_conf['difficulties']
difficulty = cli.choose_one("how difficult is your challenge?", repo_conf['difficulties'], default=prev.get('difficulty', ))
parameters = prev.get('parameters', {})
standalone = cli.confirm("is it a standalone challenge?", default=prev.get('standalone'))
static_url = prev.get('static_url', '')
company_logo_url = prev.get('company_logo_url', '')
conf = {
'name': name,
'tags': tags,
'slug': slugify(name),
'flag': flag,
'points': points,
'enabled': enabled,
'difficulty': difficulty,
'parameters': parameters,
'standalone': standalone
'standalone': standalone,
'static_url': static_url,
'company_logo_url': company_logo_url
}
return conf

Expand All @@ -127,7 +129,7 @@ def scan(self, tags=[]):
keep = lambda entry: entry.is_dir() and not entry.name.startswith('.')
challenges = []
for chall_dirent in self._scandirs(wd, keep):
chall_conf_path = Path(chall_dirent.path).joinpath(repo_conf['files']['config']['challenge'])
chall_conf_path = Path(chall_dirent.path).joinpath(repo_conf['files']['chall_conf'])
chall = Challenge(chall_conf_path, repo_conf)
if not tags or tags.intersection(chall.tags):
challenges.append(chall)
Expand All @@ -142,7 +144,7 @@ def find_chall(self, slug):
app_log.warning(f"challenge not found: {slug}")
return None
repo_conf = self.get_conf()
chall_conf_path = chall_path.joinpath(repo_conf['files']['config']['challenge'])
chall_conf_path = chall_path.joinpath(repo_conf['files']['chall_conf'])
return Challenge(chall_conf_path, repo_conf)

def configure(self, configuration=None):
Expand All @@ -161,7 +163,7 @@ def create_chall(self, configuration=None):
app_log.warning("aborted challenge creation, slug is empty.")
return False
chall_conf_path = self.working_dir().joinpath(chall_conf['slug'],
repo_conf['files']['config']['challenge'])
repo_conf['files']['chall_conf'])
chall = Challenge(chall_conf_path, repo_conf)
if not chall.create():
return False
Expand Down

0 comments on commit 05afdb0

Please sign in to comment.