{"id":"PYSEC-2026-1470","summary":"jcvi vulnerable to Configuration Injection due to unsanitized user input ","details":"### Summary\nA configuration injection happens when user input is considered by the application in an unsanitized format and can reach the configuration file. A malicious user may craft a special payload that may lead to a command injection.\n\n### PoC\n\nThe vulnerable code snippet is [/jcvi/apps/base.py#LL2227C1-L2228C41](https://github.com/tanghaibao/jcvi/blob/cede6c65c8e7603cb266bc3395ac8f915ea9eac7/jcvi/apps/base.py#LL2227C1-L2228C41). Under some circumstances a user input is retrieved and stored within the `fullpath` variable which reaches the configuration file `~/.jcvirc`.\n\n```python\n        fullpath = input(msg).strip()\n        config.set(PATH, name, fullpath)\n```\n\nI ripped a part of the codebase into a runnable PoC as follows. All the PoC does is call the `getpath()` function under some circumstances.\n\n```python\nfrom configparser import (\n    ConfigParser,\n    RawConfigParser,\n    NoOptionError,\n    NoSectionError,\n    ParsingError,\n)\n\nimport errno\nimport os\nimport sys\nimport os.path as op\nimport shutil\nimport signal\nimport sys\nimport logging\n\n\ndef is_exe(fpath):\n    return op.isfile(fpath) and os.access(fpath, os.X_OK)\n\n\ndef which(program):\n    \"\"\"\n    Emulates the unix which command.\n\n    \u003e\u003e\u003e which(\"cat\")\n    \"/bin/cat\"\n    \u003e\u003e\u003e which(\"nosuchprogram\")\n    \"\"\"\n    fpath, fname = op.split(program)\n    if fpath:\n        if is_exe(program):\n            return program\n    else:\n        for path in os.environ[\"PATH\"].split(os.pathsep):\n            exe_file = op.join(path, program)\n            if is_exe(exe_file):\n                return exe_file\n\n    return None\n\n\ndef getpath(cmd, name=None, url=None, cfg=\"~/.jcvirc\", warn=\"exit\"):\n    \"\"\"\n    Get install locations of common binaries\n    First, check ~/.jcvirc file to get the full path\n    If not present, ask on the console and store\n    \"\"\"\n    p = which(cmd)  # if in PATH, just returns it\n    if p:\n        return p\n\n    PATH = \"Path\"\n    config = RawConfigParser()\n    cfg = op.expanduser(cfg)\n    changed = False\n    if op.exists(cfg):\n        config.read(cfg)\n\n    assert name is not None, \"Need a program name\"\n\n    try:\n        fullpath = config.get(PATH, name)\n    except NoSectionError:\n        config.add_section(PATH)\n        changed = True\n\n    try:\n        fullpath = config.get(PATH, name)\n    except NoOptionError:\n        msg = \"=== Configure path for {0} ===\\n\".format(name, cfg)\n        if url:\n            msg += \"URL: {0}\\n\".format(url)\n        msg += \"[Directory that contains `{0}`]: \".format(cmd)\n        fullpath = input(msg).strip()\n        config.set(PATH, name, fullpath)\n        changed = True\n\n    path = op.join(op.expanduser(fullpath), cmd)\n    if warn == \"exit\":\n        try:\n            assert is_exe(path), \"***ERROR: Cannot execute binary `{0}`. \".format(path)\n        except AssertionError as e:\n            sys.exit(\"{0!s}Please verify and rerun.\".format(e))\n\n    if changed:\n        configfile = open(cfg, \"w\")\n        config.write(configfile)\n        logging.debug(\"Configuration written to `{0}`.\".format(cfg))\n\n    return path\n\n\n# Call to getpath\npath = getpath(\"not-part-of-path\", name=\"CLUSTALW2\", warn=\"warn\")\nprint(path)\n\n```\n\nTo run the PoC, you need to remove the config file `~/.jcvirc` to emulate the first run, \n\n```bash\n# Run the PoC with the payload\necho -e \"e\\rvvvvvvvv = zzzzzzzz\\n\" | python3 poc.py\n```\n\n![image](https://user-images.githubusercontent.com/13036531/247852364-f8a384a3-fc62-41ca-b467-877d197ac6ff.png)\n\nYou can notice the random key/value characters `vvvvvvvv = zzzzzzzz` were successfully injected.\n\n### Impact\n\nThe impact of a configuration injection may vary. Under some conditions, it may lead to command injection if there is for instance shell code execution from the configuration file values.\n","aliases":["CVE-2023-35932","GHSA-x49m-3cw7-gq5q"],"modified":"2026-07-07T17:47:15.625912516Z","published":"2026-07-07T11:45:19.233547Z","references":[{"type":"WEB","url":"https://github.com/tanghaibao/jcvi/security/advisories/GHSA-x49m-3cw7-gq5q"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2023-35932"},{"type":"PACKAGE","url":"https://github.com/tanghaibao/jcvi"},{"type":"WEB","url":"https://github.com/tanghaibao/jcvi/blob/cede6c65c8e7603cb266bc3395ac8f915ea9eac7/jcvi/apps/base.py#LL2227C1-L2228C41"},{"type":"PACKAGE","url":"https://pypi.org/project/jcvi"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-x49m-3cw7-gq5q"}],"affected":[{"package":{"name":"jcvi","ecosystem":"PyPI","purl":"pkg:pypi/jcvi"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.3.5"}]}],"versions":["0.4.10","0.4.12","0.4.7","0.4.8","0.4.9","0.5.1","0.5.3","0.5.5","0.5.7","0.5.8","0.5.9","0.6.1","0.6.2","0.6.6","0.6.9","0.7.1","0.7.3","0.7.5","0.7.7","0.8.12","0.8.4","0.9.11","0.9.12","0.9.13","0.9.14","1.0.1","1.0.10","1.0.13","1.0.14","1.0.3","1.0.5","1.0.6","1.0.8","1.0.9","1.1.11","1.1.12","1.1.15","1.1.17","1.1.18","1.1.21","1.1.23","1.1.7","1.1.8","1.2.1","1.2.10","1.2.12","1.2.14","1.2.20","1.2.7","1.2.9","1.3.3","1.3.4","1.3.5"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/jcvi/PYSEC-2026-1470.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:L"}]}