def do_challenge_import(file, dry_run, log_var):
    time.sleep(5)  # Wait for client to connect
    log("Staring challenge import", log_var, MessageType.INFO)

    if dry_run:
        log("Dry run: no changes will be saved", log_var, MessageType.WARNING)

    path = extract_zip(file, log_var)

    if path is None:
        return

    challenge_dir, challenge_names = find_challenge_dir(path, log_var)

    if not challenge_dir:
        log("Uploaded file does not contain challenges directory", log_var,
            MessageType.ERROR)
        return

    for challenge_name in challenge_names:
        # noinspection PyBroadException
        try:
            with transaction.atomic():
                import_challenge(challenge_dir, challenge_name, dry_run,
                                 log_var)
        except Exception as e:
            log(f"Exception during challenge import: {e}", log_var,
                MessageType.ERROR)

    # Cleanup: delete folder where the zip was extracted
    shutil.rmtree(path)
    os.remove(file)
    log("Challenge import completed", log_var, MessageType.SUCCESS)
Exemple #2
0
def validate_challenge_structure(challenge_path, log_var):
    valid = True
    import_setup = False
    challenge_files = []

    challenge_json_found = False
    description_md_found = False
    solution_txt_found = False

    for root, dirs, files in os.walk(challenge_path):
        if root == challenge_path:
            for file in files:
                if file == "challenge.json":
                    challenge_json_found = True
                elif file == "description.md":
                    description_md_found = True
                elif file == "solution.txt":
                    solution_txt_found = True
                elif file == "setup.txt":
                    import_setup = True
                elif file in [".gitignore", ".gitkeep"]:
                    continue
                else:
                    valid = False
                    log(f"Challenge directory contains unknown file: {file}", log_var, MessageType.ERROR)
        else:
            private = False
            config_folder = root
            if os.path.basename(root) == "private":
                private = True
                config_folder = os.path.dirname(root)

            if os.path.basename(config_folder) == "qpa-files":
                conf = "qpa"
            elif os.path.basename(config_folder) == "hacktivity-files":
                conf = "hacktivity"
            else:
                valid = False
                log(f"Challenge directory contains unknown directory: {os.path.basename(root)}", log_var, MessageType.ERROR)
                continue
            for f in files:
                if f == ".gitkeep" or f == ".gitignore":
                    continue
                challenge_files.append({"private": private, "conf": conf, "path": os.path.join(root, f)})

    if not challenge_json_found:
        log("challenge.json not found", log_var, MessageType.ERROR)
        valid = False
    if not description_md_found:
        log(f"description.md not found", log_var, MessageType.ERROR)
        valid = False
    if not solution_txt_found:
        log(f"solution.txt not found", log_var, MessageType.ERROR)
        valid = False

    return valid, import_setup, challenge_files
def extract_zip(file, log_var):
    # noinspection PyBroadException
    try:
        path = os.path.join(MEDIA_ROOT, "challenge-temp")
        with ZipFile(file) as archive:
            archive.extractall(path=path)
        return path
    except Exception as e:
        log(f"Error during zip extraction: {e}", log_var, MessageType.ERROR)
        return None
Exemple #4
0
def import_files(challenge, files, dry_run, log_var):
    file_entity = challenge.files.all()
    if file_entity.exists():
        log(f"Some files already exist, deleting...", log_var, MessageType.WARNING)
        if not dry_run:
            file_entity.delete()
    for file in files:
        filename = os.path.basename(file["path"])
        with open(os.path.join(file["path"]), "rb") as fp:
            challenge_file = ChallengeFile()
            challenge_file.challenge = challenge
            challenge_file.private = file["private"]
            challenge_file.display_name = filename
            challenge_file.config_name = file["conf"]
            challenge_file.filename = filename
            if not dry_run:
                challenge_file.file.save(filename, File(fp))
                challenge_file.save()
def import_challenge_json(file, challenge, log_var):
    data = json.load(file)
    valid = True
    tags = []

    if not list(data.keys()) == export_keys:
        log(
            "Error: challenges.json contains invalid keys or does not contain all keys",
            log_var, MessageType.ERROR)
        return False

    for key, value in data.items():
        if key == "tags":
            tags = value
        else:
            challenge.__setattr__(key, value)

    if not re.match("^SECURITEAM{[a-f0-9]{32}}$", challenge.flag_qpa,
                    re.RegexFlag.IGNORECASE):
        log("Invalid QPA flag format", log_var, MessageType.ERROR)
        valid = False

    if not re.match("^SECURITEAM{[a-f0-9]{32}}$", challenge.flag_hacktivity,
                    re.RegexFlag.IGNORECASE):
        log("Invalid hacktivity flag format", log_var, MessageType.ERROR)
        valid = False

    return valid, tags
Exemple #6
0
def do_user_import(path, dry_run, log_var):
    time.sleep(5)  # Wait for client to connect
    log("Starting user import", log_var, MessageType.INFO)

    if dry_run:
        log("Dry run: no changes will be saved or emails sent", log_var,
            MessageType.WARNING)

    messages = []

    subject = StaticContent.objects.get(key="email_subject").html
    email_template = StaticContent.objects.get(key="email_text").html

    with open(path, "r", encoding="utf-8-sig") as fp:
        reader = csv.reader(fp, delimiter=",")
        for row in reader:
            team_name = row[0]
            email = row[1]
            password = get_random_string(16)

            if not dry_run:
                User.objects.create_user(team_name, email, password)

            message = email_template.replace("%TEAM%", team_name).replace(
                "%PASSWORD%", password)
            messages.append((subject, message, base.EMAIL_FROM, [email]))

            log(f"Imported {team_name}", log_var, MessageType.SUCCESS)

    log("Sending emails...", log_var, MessageType.INFO)

    if not dry_run:
        send_mass_mail(messages)

    os.remove(path)
    log("User import complete", log_var, MessageType.SUCCESS)
def import_challenge(challenge_dir, challenge_name, dry_run, log_var):
    log(f"Importing challenge {challenge_name}", log_var, MessageType.INFO)
    challenge_path = os.path.join(challenge_dir, challenge_name)

    valid, import_setup, files = validate_challenge_structure(
        challenge_path, log_var)

    if not valid:
        return

    # Get or create challenge
    try:
        challenge = Challenge.objects.get(import_name=challenge_name)
    except Challenge.DoesNotExist:
        challenge = Challenge()
        challenge.import_name = challenge_name
    else:
        log("Challenge already exists, replacing values", log_var,
            MessageType.WARNING)

    with open(os.path.join(challenge_path, "challenge.json"),
              encoding="utf-8-sig") as file:
        valid, tags = import_challenge_json(file, challenge, log_var)
        if not valid:
            return

    with open(os.path.join(challenge_path, "description.md"),
              encoding="utf-8-sig") as file:
        challenge.description = file.read()

    with open(os.path.join(challenge_path, "solution.txt"),
              encoding="utf-8-sig") as file:
        challenge.solution = file.read()

    if import_setup:
        with open(os.path.join(challenge_path, "setup.txt"),
                  encoding="utf-8-sig") as file:
            challenge.setup = file.read()

    if not dry_run:
        challenge.save()
        challenge.tags.add(*tags)
        challenge.save()

    import_files(challenge, files, dry_run, log_var)

    log(f"Challenge imported: {challenge_name}", log_var, MessageType.SUCCESS)
def find_challenge_dir(path, log_var):
    for root, dirs, _ in os.walk(path):
        if os.path.basename(root) == "challenges":
            log(f"{len(dirs)} challenges found", log_var, MessageType.INFO)
            return root, dirs
    return None, None