{"thread":{"id":"64279","subject":"[RCF] Secure git against involuntary arb. code execution without feature loss","startedAt":"2025-10-08T21:08:28Z","lastAt":"2025-10-13T09:57:50Z","messageCount":10,"participants":["Michael Lohmann","Taylor Blau","rsbecker@nexbridge.com","brian m. carlson","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"528318","messageId":"72F10412-8B0F-4F66-8674-FE194D016DF9@lohmann.sh","threadId":"64279","inReplyTo":null,"subject":"[RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"Michael Lohmann","fromEmail":"git@lohmann.sh","sentAt":"2025-10-08T21:02:03Z","receivedAt":"2025-10-08T21:08:28Z","isPatch":false,"sender":{"key":"git@lohmann.sh","avatar":"https://avatars.githubusercontent.com/u/6929993?v=4"},"body":"Hello everyone,\n\nHooks, as well as certain config (e.g. `core.pager`) can do automatic\ncode execution for you. In general, this is a great feature and should\nbe kept without the user noticing any changes.\n\nBUT if you download a random folder which to you unknowingly is a repo\nand either you or e.g. your command line prompt automatically executes a\nsimple `git status`, it feels bad if this results in arbitrary code\nexecution (ACE), e.g.:\n\n https://www.sonarsource.com/blog/securing-developer-tools-git-integrations/\n\nand\n\n https://github.com/justinsteven/advisories/blob/main/2022_git_buried_bare_repos_and_fsmonitor_various_abuses.md\n\nApart from one core maintainer, all git user I talked to were surprised\nand shocked by how simple an exploit like this was.\n\n* Proposed solution (keeping all existing features):\n- On first use, git generates a secret \"token\" (e.g. a random string in\n  ~/.gitsecret)\n- On calling `git init` or `git clone`, the secret is copied into the\n  new .git directory and serves as proof that this clone was created by\n  this user\n- Before executing any user-defined code, check for the local token:\n  - If present, proceed as usual.\n  - Otherwise abort.\n\n* Benefit:\n- Protects users from ACE when interacting with untrusted repositories.\n- Editors would no longer need to prompt the user for \"Do you trust this\n  repository?\" in most cases, because git could prove the clone is user\n  generated.\n- For new clones, the user wouldn't even notice a change.\n\n* Drawbacks:\n- Existing clones would need manual approval once (e.g., via a new\n  `git allow` command).\n\n* Migration strategy to ease adoption (risks to be weight up):\n- A future minor release of `git` could already silently add the\n  token to all clones it is executed in.\n- Even if the repo was malicious, ACE has already occured,\n- Since the ACE would have already occured, chances are, other forms of\n  persistence had been taken\n- By the time git 3.0 introduced the breaking change, most active clones\n  would be migrated, so users would only manually need to act, if they\n  have very infrequently used clones.\n\n* Prototype:\nOn my machine `git` resolves to a 150 line bash wrapper script with a\nrough implementation this proposal:\n\n https://git.lohmann.sh/michael/nixos-config/src/branch/main/modules/git.sh\n\nWith that, `git` is no longer vulnerable against these kinds of attacks.\nI also added some more background information/POC in a blog post:\n\n https://www.lohmann.sh/en/nuggits/002-dangerous-git/\n\nYes, I know even despite the \"silent migration\" this would be probably\nlead to some pain for some people on the initial adoption of git v3, but\nit would make it much safer for everyone to use and in the long run,\nnobody would notice. What are your thoughts of weighing long-term risks\nwith the short-term pain of adopting something like this? Any other\nideas on how to solve this even better?\nFeedback welcome!\n\nMichael Lohmann"},{"id":"528326","messageId":"aObX4C7lMHRnjbYq@nand.local","threadId":"64279","inReplyTo":"72F10412-8B0F-4F66-8674-FE194D016DF9@lohmann.sh","subject":"Re: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2025-10-08T21:30:08Z","receivedAt":"2025-10-08T21:30:11Z","isPatch":false,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:\n> * Proposed solution (keeping all existing features):\n> - On first use, git generates a secret \"token\" (e.g. a random string in\n>   ~/.gitsecret)\n> - On calling `git init` or `git clone`, the secret is copied into the\n>   new .git directory and serves as proof that this clone was created by\n>   this user\n\nSure, but the problem is not with direct clones (at least, not using the\n--local optimization), but with clones that recursively clone other\nsubmodules.\n\nIf I clone a repository with --recurse-submodules, I imagine that this\nproposal would *not* suggest copying this token into the recursively\ncloned submodules, right?\nproposal improves the experience\n\n> - Editors would no longer need to prompt the user for \"Do you trust this\n>   repository?\" in most cases, because git could prove the clone is user\n>   generated.\n\nIf the above is true (that Git would not copy the token into recursively\ncloned submodules), then I admit to struggling a bit to see how this\nproposal would remove the need to consult the user in this case. Instead\nof the editor doing it, the user would need to do it themselves?\n\nThanks,\nTaylor\n"},{"id":"528329","messageId":"020201dc389b$677305f0$365911d0$@nexbridge.com","threadId":"64279","inReplyTo":"aObX4C7lMHRnjbYq@nand.local","subject":"RE: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2025-10-08T21:34:56Z","receivedAt":"2025-10-08T21:35:13Z","isPatch":false,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On October 8, 2025 5:30 PM, Taylor Blau wrote:\n>On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:\n>> * Proposed solution (keeping all existing features):\n>> - On first use, git generates a secret \"token\" (e.g. a random string in\n>>   ~/.gitsecret)\n>> - On calling `git init` or `git clone`, the secret is copied into the\n>>   new .git directory and serves as proof that this clone was created by\n>>   this user\n>\n>Sure, but the problem is not with direct clones (at least, not using the --local\n>optimization), but with clones that recursively clone other submodules.\n>\n>If I clone a repository with --recurse-submodules, I imagine that this proposal\n>would *not* suggest copying this token into the recursively cloned submodules,\n>right?\n>proposal improves the experience\n>\n>> - Editors would no longer need to prompt the user for \"Do you trust this\n>>   repository?\" in most cases, because git could prove the clone is user\n>>   generated.\n>\n>If the above is true (that Git would not copy the token into recursively cloned\n>submodules), then I admit to struggling a bit to see how this proposal would\n>remove the need to consult the user in this case. Instead of the editor doing it, the\n>user would need to do it themselves?\n\nI am wondering why this approach cannot be made more general. If there is\na tokenization framework built into git that organizations can use for integration,\nthey should be able to plug-in their own approved tokenization solutions, which\nhave already been vetted by the security groups. I am concerned that we are\npotentially opening up git to CVEs due to insufficiently secure tokens that are\noutside our specific domain.\n\n--Randall\n\n"},{"id":"528330","messageId":"aObZPJ3JK-YVI-g7@fruit.crustytoothpaste.net","threadId":"64279","inReplyTo":"72F10412-8B0F-4F66-8674-FE194D016DF9@lohmann.sh","subject":"Re: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-10-08T21:35:56Z","receivedAt":"2025-10-08T21:35:58Z","isPatch":false,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-10-08 at 21:02:03, Michael Lohmann wrote:\n> Hello everyone,\n\nHi,\n\n> Hooks, as well as certain config (e.g. `core.pager`) can do automatic\n> code execution for you. In general, this is a great feature and should\n> be kept without the user noticing any changes.\n> \n> BUT if you download a random folder which to you unknowingly is a repo\n> and either you or e.g. your command line prompt automatically executes a\n> simple `git status`, it feels bad if this results in arbitrary code\n> execution (ACE), e.g.:\n> \n>  https://www.sonarsource.com/blog/securing-developer-tools-git-integrations/\n> \n> and\n> \n>  https://github.com/justinsteven/advisories/blob/main/2022_git_buried_bare_repos_and_fsmonitor_various_abuses.md\n> \n> Apart from one core maintainer, all git user I talked to were surprised\n> and shocked by how simple an exploit like this was.\n> \n> * Proposed solution (keeping all existing features):\n> - On first use, git generates a secret \"token\" (e.g. a random string in\n>   ~/.gitsecret)\n> - On calling `git init` or `git clone`, the secret is copied into the\n>   new .git directory and serves as proof that this clone was created by\n>   this user\n> - Before executing any user-defined code, check for the local token:\n>   - If present, proceed as usual.\n>   - Otherwise abort.\n\nThis has all of the downsides of our existing per-Unix user approach and\nis also more complicated.  You'll notice that when we changed to the\nper-Unix user approach, that broke containers, shared repositories, and\neven the detection of whether a directory _is_ a repository, and this\nwould do the same thing.  It would be even worse for containers because\nif you copied a token into the container from the container user, you'd\nend up polluting that directory with lots of useless tokens that never\nget cleaned up.\n\nIt is also easy to bypass, since if we share multiple repositories as\ncollaborators, as soon as I can read the secret from one of them, I can\nthen write it into any other location.  It would even be sufficient if\nwe were users on the same system (such as is common in universities or\nsome businesses) and I could read the repository.  Many websites even\naccidentally expose their `.git` directories, which would not only\nexpose their repository but also allow attacks against all of the\nadministrator's repositories.\n\nWhat would be better to see instead is a config option that restricts\nwhich external commands (via config options or hooks) can be executed\nfrom local config.  Then it would be possible to say, \"Never honour\nlocal config to execute code.\"  With `includeIf`, this can allowlist\ncertain repositories to do that and leave the rest disabled.\n\nMaybe something like this:\n\n[safe]\n  config = core.editor\n  config = core.fsmonitor\n  hook = pre-receive\n\nIf you wanted to implement _that_, I'd be all over it.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"528333","messageId":"aObceC/Ec/TGTEnv@nand.local","threadId":"64279","inReplyTo":"aObX4C7lMHRnjbYq@nand.local","subject":"Re: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2025-10-08T21:49:44Z","receivedAt":"2025-10-08T21:49:46Z","isPatch":false,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Oct 08, 2025 at 05:30:08PM -0400, Taylor Blau wrote:\n> On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:\n> > * Proposed solution (keeping all existing features):\n> > - On first use, git generates a secret \"token\" (e.g. a random string in\n> >   ~/.gitsecret)\n> > - On calling `git init` or `git clone`, the secret is copied into the\n> >   new .git directory and serves as proof that this clone was created by\n> >   this user\n>\n> Sure, but the problem is not with direct clones (at least, not using the\n> --local optimization), but with clones that recursively clone other\n> submodules.\n\nThis is a think-o. I meant to ask whether or not we would respect the\ntoken from the top-most $GIT_DIR in nested bare repositories. I imagine\nwe would not (otherwise this proposal would not provide any additional\nsecurity guarantees), and so...\n\n> > - Editors would no longer need to prompt the user for \"Do you trust this\n> >   repository?\" in most cases, because git could prove the clone is user\n> >   generated.\n>\n> If the above is true (that Git would not copy the token into recursively\n> cloned submodules), then I admit to struggling a bit to see how this\n> proposal would remove the need to consult the user in this case. Instead\n> of the editor doing it, the user would need to do it themselves?\n\nThe rest is the same as before (swapping submodules for nested bare\nrepositories).\n\nThanks,\nTaylor\n"},{"id":"528342","messageId":"25FB37DC-09F3-41F4-ACFE-6F8A854E50B8@lohmann.sh","threadId":"64279","inReplyTo":"aObceC/Ec/TGTEnv@nand.local","subject":"Re: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"Michael Lohmann","fromEmail":"git@lohmann.sh","sentAt":"2025-10-08T22:09:03Z","receivedAt":"2025-10-08T22:09:07Z","isPatch":false,"sender":{"key":"git@lohmann.sh","avatar":"https://avatars.githubusercontent.com/u/6929993?v=4"},"body":"\n\n> On 8. Oct 2025, at 23:49, Taylor Blau <me@ttaylorr.com> wrote:\n> \n> On Wed, Oct 08, 2025 at 05:30:08PM -0400, Taylor Blau wrote:\n>> On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:\n>>> * Proposed solution (keeping all existing features):\n>>> - On first use, git generates a secret \"token\" (e.g. a random string in\n>>>  ~/.gitsecret)\n>>> - On calling `git init` or `git clone`, the secret is copied into the\n>>>  new .git directory and serves as proof that this clone was created by\n>>>  this user\n>> \n>> Sure, but the problem is not with direct clones (at least, not using the\n>> --local optimization), but with clones that recursively clone other\n>> submodules.\n> \n> This is a think-o. I meant to ask whether or not we would respect the\n> token from the top-most $GIT_DIR in nested bare repositories. I imagine\n> we would not (otherwise this proposal would not provide any additional\n> security guarantees), and so...\n\n\nMy understanding was that submodules would add their own git folder in\nthe top-level .git/modules/my-submodule, so obviously in order to trust\na submodule, you need to \"sign\" these too and you could automatically\nalso with --recursive.\n\n(Sorry @Taylor - I missed adding the whole list as recipient, so you get\nthis twice...)\n"},{"id":"528343","messageId":"20251008222512.70500-1-git@lohmann.sh","threadId":"64279","inReplyTo":"aObZPJ3JK-YVI-g7@fruit.crustytoothpaste.net","subject":"Re: Re: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"Michael Lohmann","fromEmail":"git@lohmann.sh","sentAt":"2025-10-08T22:25:12Z","receivedAt":"2025-10-08T22:25:42Z","isPatch":false,"sender":{"key":"git@lohmann.sh","avatar":"https://avatars.githubusercontent.com/u/6929993?v=4"},"body":"(Sorry @Brian, the first time I forgot to add the mailing list in cc,\nand the second time I didn't have my mail client under control adding\nHTML... Now trying with send-mail again in order to try and reduce the\npossible errors. I am sorry, I am still quite inexperienced with the\nmailing list and you need to suffer from my stupid mistakes!)\n\nOn Wed, 8 Oct 2025 21:35:56, brian m. carlson wrote:\n> This has all of the downsides of our existing per-Unix user approach and\n> is also more complicated.  You'll notice that when we changed to the\n> per-Unix user approach, that broke containers, shared repositories, and\n> even the detection of whether a directory _is_ a repository, and this\n> would do the same thing.  It would be even worse for containers because\n> if you copied a token into the container from the container user, you'd\n> end up polluting that directory with lots of useless tokens that never\n> get cleaned up.\n\nSince I never worked in such a scenario, I think I am missing the full\nimplications. Instead of always requiring that, you could simply set\nsomething like GIT_ALLOW=true or pass `--allow` (or maybe even a _global\nonly_ config `safe.allow = *` and then you explicitly opt out of this\nprotection for cases like this.\n\n> It is also easy to bypass, since if we share multiple repositories as\n> collaborators, as soon as I can read the secret from one of them, I can\n> then write it into any other location.\n\nThen instead of having a single file with all the tokens of users,\ncreate different files with the respective user as the only one allowed\nto read.\n\n> It would even be sufficient if we were users on the same system (such\n> as is common in universities or some businesses) and I could read the\n> repository.  Many websites even accidentally expose their `.git`\n> directories, which would not only expose their repository but also\n> allow attacks against all of the administrator's repositories.\n\nWhat is different to the current situation then, apart from that you\nspecifically have to leak that secret and you now have to be targeted\nindividually and if you notice it being compromised you can rotate it?\n\nMaybe instead of a shared global secret, create individuals you can\nrevoke independently?\n\n> What would be better to see instead is a config option that restricts\n> which external commands (via config options or hooks) can be executed\n> from local config.  Then it would be possible to say, \"Never honour\n> local config to execute code.\"  With `includeIf`, this can allowlist\n> certain repositories to do that and leave the rest disabled.\n> \n> Maybe something like this:\n> \n> [safe]\n>  config = core.editor\n>  config = core.fsmonitor\n>  hook = pre-receive\n\nInteresting idea!\n"},{"id":"528350","messageId":"20251009052422.GA1614343@coredump.intra.peff.net","threadId":"64279","inReplyTo":"72F10412-8B0F-4F66-8674-FE194D016DF9@lohmann.sh","subject":"Re: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-10-09T05:24:22Z","receivedAt":"2025-10-09T05:24:34Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:\n\n> Hooks, as well as certain config (e.g. `core.pager`) can do automatic\n> code execution for you. In general, this is a great feature and should\n> be kept without the user noticing any changes.\n> \n> BUT if you download a random folder which to you unknowingly is a repo\n> and either you or e.g. your command line prompt automatically executes a\n> simple `git status`, it feels bad if this results in arbitrary code\n> execution (ACE), e.g.:\n\nWe've discussed this a few times over the years. The most recent one I\ncan remember is:\n\n  https://lore.kernel.org/git/ZZr-JLxubCvWe0EU@tapette.crustytoothpaste.net/\n\nI think there are two somewhat orthogonal issues to consider:\n\n  1. How does Git behave differently in an \"unsafe\" context? In the\n     thread above, I propose that it should skip loading config from the\n     repo-level $GIT_DIR/config file, and turn off hooks inside the\n     repo. Elsewhere, others have proposed finer-grained control (like\n     specific config options). IMHO the most important thing here is\n     maintainability, and having a scheme where we do not accidentally\n     add code that lets an untrusted repository do bad things.\n\n  2. How does the user tell Git which repos are safe or unsafe? You've\n     got a scheme here for marking user-created repositories with a\n     secret token. I think that could work, but there are other simpler\n     methods. E.g., we could pass down information through the\n     environment (like we do already for security features like\n     GIT_ALLOW_PROTOCOL), or mark a list of safe directories in user- or\n     system-level config (like we have already with safe.directories).\n     It's perhaps even reasonable to have multiple such mechanisms, as\n     they have different tradeoffs in convenience and security.\n\nSo in some sense I think talking about this token scheme and Git 3.0\ncompatibility is putting the cart before the horse. We need (1) first.\nAnd then once we have it, I think the simplest thing is not turning it\non all the time, but letting commands opt into it through command-line\noptions and environment variables. So you could imagine git-prompt\nrunning \"git --assume-unsafe\" or setting \"GIT_ASSUME_UNSAFE=1\" in the\nenvironment. People who want to be more paranoid can set that variable\nfor their normal commands to opt into greater security (at the cost of\nconvenience).\n\nThen once we have some practical experience with the system, we can\nconsider whether we should flip the default. And in the meantime we can\nexperiment with idea like your token scheme.\n\n-Peff\n"},{"id":"528430","messageId":"20251009224317.77565-1-git@lohmann.sh","threadId":"64279","inReplyTo":"20251009052422.GA1614343@coredump.intra.peff.net","subject":"Re: [RCF] Secure git against involuntary arb. code execution without feature loss","fromName":"Michael Lohmann","fromEmail":"git@lohmann.sh","sentAt":"2025-10-09T22:43:17Z","receivedAt":"2025-10-09T22:43:51Z","isPatch":false,"sender":{"key":"git@lohmann.sh","avatar":"https://avatars.githubusercontent.com/u/6929993?v=4"},"body":"TL;DR: Thanks to all peoples input I just mentally untangeled/refactored\na few things about the discusion for myself:\n\n* Current situation:\n    git assumes an unsafe repo, if neither\n        a) the user owns it, nor\n        b) it is included in \"safe.directory\" config\n    and then refuses any operation.\n\n* Suggestions from the discussion:\n1) The suggested \"token\" approach is just an additional way to set the\n   current state of allow \"safe.directory\" for repos _created_ by this\n   user\n2) The suggested --assume-(un)safe / GIT_ASSUME_(UN)SAFE are additional\n   ways of setting the same state temporarily\n3) There are suggested improvements on not blankly refusing operation at\n   all if assumed to be unsafe\n4) The _enforcement_ part of the RFC is about dropping the check for the\n   owner of the repo to assume if safe or not\n\nI think 1)-3) are relatively independent of one another to discuss,\nwhereas 4) probably requires all others to be viable.\n\n\n\nAnd now for the long answer:\n\n> We've discussed this a few times over the years. The most recent one I\n> can remember is:\n>\n>   https://lore.kernel.org/git/ZZr-JLxubCvWe0EU@tapette.crustytoothpaste.net/\n>\n>   1. How does Git behave differently in an \"unsafe\" context?\n>      In the thread above, I propose that it should skip loading config\n>      from the repo-level $GIT_DIR/config file, and turn off hooks\n>      inside the repo.\n\nIndeed it would be a lot nicer, if git still worked and only showed a\n(maybe hideable) warning. As mentioned in the above conversation, the\nconfig parser could only extract the minimum required fields in the\nassumed unsafe state and the hooks would be disabled, too.\n\n>   2. How does the user tell Git which repos are safe or unsafe? You've\n>      got a scheme here for marking user-created repositories with a\n>      secret token. I think that could work, but there are other simpler\n>      methods. E.g., we could pass down information through the\n>      environment\n\nI am not sure I can follow you. Just as an overwrite like you mentioned\nbelow / as in suggestion 2)?\n\n>      or mark a list of safe directories in user- or system-level\n>      config (like we have already with safe.directories).\n>      It's perhaps even reasonable to have multiple such mechanisms, as\n>      they have different tradeoffs in convenience and security.\n\nIndeed! E.g. for \"automated trusting\" on init/clone the path based\napproach is probably not the best, since\na) if the user runs `git init /trusted && mv /trusted /elsewhere`, all\n   of the sudden git would no longer work in that repo and\nb) now the path \"/trusted\" is vulnerable, unknown to the user.\n\n> So in some sense I think talking about this token scheme and Git 3.0\n> compatibility is putting the cart before the horse. We need (1) first.\n\nIMHO, an option on how to _detect_ if this repo is a \"safe.directory\"\nwould not depend on improvements on how to _act_ upon it (or the other\nway around). Yes - to _enforce_ not whitelisting all user owned repos by\ndefault any more, both are probably needed. So indeed this part is more\nspeculative.\n\n-Michael\n"},{"id":"528613","messageId":"20251013095737.24203-1-git@lohmann.sh","threadId":"64279","inReplyTo":"20251009052422.GA1614343@coredump.intra.peff.net","subject":"Submitted patches for \"assume unsafe\"","fromName":"Michael Lohmann","fromEmail":"git@lohmann.sh","sentAt":"2025-10-13T09:57:37Z","receivedAt":"2025-10-13T09:57:50Z","isPatch":false,"sender":{"key":"git@lohmann.sh","avatar":"https://avatars.githubusercontent.com/u/6929993?v=4"},"body":"Hello!\nAs a first step, here are the patches for \"--assume-unsafe\" /\n\"GIT_ASSUME_UNSAFE\" / \"safe.assumeUnsafe\":\n\n https://lore.kernel.org/git/20251013094152.23597-1-git@lohmann.sh/\n\n-Michael\n"}]}