CVE-2021-43857 (Python, published 2021-12-27T19:15Z)
Description: value: Gerapy is a distributed crawler management framework. Gerapy prior to version 0.9.8 is vulnerable to remote code execution, and this issue is patched in version 0.9.8.
Severity: MEDIUM (CVSS v3 base score: 8.8)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/Gerapy/Gerapy
Files changed:
gerapy/server/core/views.py
Commit message: fix remote execute


Vulnerable code:
import json, os, requests, time, pytz, pymongo
        spiders = [{'name': spider, 'id': index + 1} for index, spider in enumerate(spiders)]
        project['configuration'] = json.loads(project['configuration']) if project['configuration'] else None
    
        configuration = json.dumps(data.get('configuration'), ensure_ascii=False)
        
        project_name = re.sub('[\!\@\#\$\;\&\*\~\"\'\{\}\]\[\-\+\%\^]+', '', project_name)
        cmd = ' '.join(['gerapy', 'generate', project_name])
        p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        
        path = join(os.path.abspath(join(os.getcwd(), PROJECTS_FOLDER)), data['name'])
        cmd = 'git clone {address} {target}'.format(address=address, target=join(PROJECTS_FOLDER, Path(address).stem))
        p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        if stderr: logger.error(stderr)
                deployed_at = timezone.datetime.fromtimestamp(int(version), tz=pytz.timezone(TIME_ZONE))
            deploy, result = Deploy.objects.update_or_create(client=client, project=project, deployed_at=deployed_at)
    
    
            Project(name=project_name, description=description, built_at=built_at, egg=egg).save()
        
        args_cmd = ' '.join(
            ['--{arg} {value}'.format(arg=arg, value=value) for arg, value in args.items()])
        logger.debug('args cmd %s', args_cmd)
        cmd = 'gerapy parse {args_cmd} {project_path} {spider_name}'.format(
            args_cmd=args_cmd,
            project_path=project_path,
            spider_name=spider_name
        )
        p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
    
        url = log_url(client.ip, client.port, project_name, spider_name, job_id)
        data['configuration'] = json.dumps(data['configuration'], ensure_ascii=False)
                                   configuration=json.dumps(data.get('configuration'), ensure_ascii=False),
        data['configuration'] = json.dumps(data.get('configuration'), ensure_ascii=False)
            DjangoJob.objects.filter(name=job_id).delete()
    
            jobs = DjangoJob.objects.filter(name=job_id)
            if not jobs: continue
            job = DjangoJob.objects.get(name=job_id)
            executions = serialize('json', DjangoJobExecution.objects.filter(job=job))

Fixed code:
import json
import os
import requests
import time
import pytz
import pymongo
        spiders = [{'name': spider, 'id': index + 1}
                   for index, spider in enumerate(spiders)]
        project['configuration'] = json.loads(
            project['configuration']) if project['configuration'] else None

        configuration = json.dumps(
            data.get('configuration'), ensure_ascii=False)
        project_name = re.sub(
            '[\s\!\@\#\$\;\&\*\~\"\'\{\}\]\[\-\+\%\^]+', '', project_name)
        cmd = ['gerapy', 'generate', project_name]
        p = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)

        path = join(os.path.abspath(
            join(os.getcwd(), PROJECTS_FOLDER)), data['name'])
        cmd = ['git', 'clone', 'address', join(PROJECTS_FOLDER, Path(address).stem)]
        p = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        if stderr:
            logger.error(stderr)
                deployed_at = timezone.datetime.fromtimestamp(
                    int(version), tz=pytz.timezone(TIME_ZONE))
            deploy, result = Deploy.objects.update_or_create(
                client=client, project=project, deployed_at=deployed_at)


            Project(name=project_name, description=description,
                    built_at=built_at, egg=egg).save()

        args_array = []
        for arg, value in args.items():
            args_array.append(f'--{arg}')
            args_array.append(f'{value}')
        cmd = ['gerapy', 'parse'] + args_array + [project_path] + [spider_name]
        print('cmd', cmd)
        p = Popen(cmd, shell=False, stdin=PIPE,
                         stdout=PIPE, stderr=PIPE, close_fds=True)
        url = log_url(client.ip, client.port,
                      project_name, spider_name, job_id)
        data['configuration'] = json.dumps(
            data['configuration'], ensure_ascii=False)
                                   configuration=json.dumps(
                                       data.get('configuration'), ensure_ascii=False),
        data['configuration'] = json.dumps(
            data.get('configuration'), ensure_ascii=False)
            DjangoJob.objects.filter(id=job_id).delete()
            jobs = DjangoJob.objects.filter(id=job_id)
            if not jobs:
                continue
            job = DjangoJob.objects.get(id=job_id)
            executions = serialize(
                'json', DjangoJobExecution.objects.filter(job=job))