threads / discuss / 64279

[RCF] Secure git against involuntary arb. code execution without feature loss

Subject: [RCF] Secure git against involuntary arb. code execution without feature loss

## tl;dr

10 messages between Oct 8, 2025 and Oct 13, 2025.

replies: 9people: 5as markdown or json

Michael Lohmann· Oct 8, 2025, 21:02 UTC · lore
Hello everyone,

Hooks, as well as certain config (e.g. `core.pager`) can do automatic code execution for you. In general, this is a great feature and should be kept without the user noticing any changes.

BUT if you download a random folder which to you unknowingly is a repo and either you or e.g. your command line prompt automatically executes a simple `git status`, it feels bad if this results in arbitrary code execution (ACE), e.g.:

 https://www.sonarsource.com/blog/securing-developer-tools-git-integrations/
and
 https://github.com/justinsteven/advisories/blob/main/2022_git_buried_bare_repos_and_fsmonitor_various_abuses.md

Apart from one core maintainer, all git user I talked to were surprised and shocked by how simple an exploit like this was.

* Proposed solution (keeping all existing features):
- On first use, git generates a secret "token" (e.g. a random string in
  ~/.gitsecret)
- On calling `git init` or `git clone`, the secret is copied into the
  new .git directory and serves as proof that this clone was created by
  this user
- Before executing any user-defined code, check for the local token:
  - If present, proceed as usual.
  - Otherwise abort.
* Benefit:
- Protects users from ACE when interacting with untrusted repositories.
- Editors would no longer need to prompt the user for "Do you trust this
  repository?" in most cases, because git could prove the clone is user
  generated.
- For new clones, the user wouldn't even notice a change.
* Drawbacks:
- Existing clones would need manual approval once (e.g., via a new
  `git allow` command).
* Migration strategy to ease adoption (risks to be weight up):
- A future minor release of `git` could already silently add the
  token to all clones it is executed in.
- Even if the repo was malicious, ACE has already occured,
- Since the ACE would have already occured, chances are, other forms of
  persistence had been taken
- By the time git 3.0 introduced the breaking change, most active clones
  would be migrated, so users would only manually need to act, if they
  have very infrequently used clones.
* Prototype:
On my machine `git` resolves to a 150 line bash wrapper script with a
rough implementation this proposal:
 https://git.lohmann.sh/michael/nixos-config/src/branch/main/modules/git.sh

With that, `git` is no longer vulnerable against these kinds of attacks. I also added some more background information/POC in a blog post:

 https://www.lohmann.sh/en/nuggits/002-dangerous-git/

Yes, I know even despite the "silent migration" this would be probably lead to some pain for some people on the initial adoption of git v3, but it would make it much safer for everyone to use and in the long run, nobody would notice. What are your thoughts of weighing long-term risks with the short-term pain of adopting something like this? Any other ideas on how to solve this even better? Feedback welcome!

Michael Lohmann
Taylor Blau· Oct 8, 2025, 21:30 UTC · re: Michael Lohmann · lore

Re: [RCF] Secure git against involuntary arb. code execution without feature loss

On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:
Show 6 quoted lines
> * Proposed solution (keeping all existing features):
> - On first use, git generates a secret "token" (e.g. a random string in
>   ~/.gitsecret)
> - On calling `git init` or `git clone`, the secret is copied into the
>   new .git directory and serves as proof that this clone was created by
>   this user

Sure, but the problem is not with direct clones (at least, not using the --local optimization), but with clones that recursively clone other submodules.

If I clone a repository with --recurse-submodules, I imagine that this proposal would *not* suggest copying this token into the recursively cloned submodules, right? proposal improves the experience

> - Editors would no longer need to prompt the user for "Do you trust this
>   repository?" in most cases, because git could prove the clone is user
>   generated.

If the above is true (that Git would not copy the token into recursively cloned submodules), then I admit to struggling a bit to see how this proposal would remove the need to consult the user in this case. Instead of the editor doing it, the user would need to do it themselves?

Thanks, Taylor

rsbecker@nexbridge.com· Oct 8, 2025, 21:34 UTC · re: Taylor Blau · lore

RE: [RCF] Secure git against involuntary arb. code execution without feature loss

On October 8, 2025 5:30 PM, Taylor Blau wrote:
Show 24 quoted lines
>On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:
>> * Proposed solution (keeping all existing features):
>> - On first use, git generates a secret "token" (e.g. a random string in
>>   ~/.gitsecret)
>> - On calling `git init` or `git clone`, the secret is copied into the
>>   new .git directory and serves as proof that this clone was created by
>>   this user
>
>Sure, but the problem is not with direct clones (at least, not using the --local
>optimization), but with clones that recursively clone other submodules.
>
>If I clone a repository with --recurse-submodules, I imagine that this proposal
>would *not* suggest copying this token into the recursively cloned submodules,
>right?
>proposal improves the experience
>
>> - Editors would no longer need to prompt the user for "Do you trust this
>>   repository?" in most cases, because git could prove the clone is user
>>   generated.
>
>If the above is true (that Git would not copy the token into recursively cloned
>submodules), then I admit to struggling a bit to see how this proposal would
>remove the need to consult the user in this case. Instead of the editor doing it, the
>user would need to do it themselves?

I am wondering why this approach cannot be made more general. If there is a tokenization framework built into git that organizations can use for integration, they should be able to plug-in their own approved tokenization solutions, which have already been vetted by the security groups. I am concerned that we are potentially opening up git to CVEs due to insufficiently secure tokens that are outside our specific domain.

--Randall
Taylor Blau· Oct 8, 2025, 21:49 UTC · re: Taylor Blau · lore

Re: [RCF] Secure git against involuntary arb. code execution without feature loss

On Wed, Oct 08, 2025 at 05:30:08PM -0400, Taylor Blau wrote:
Show 11 quoted lines
> On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:
> > * Proposed solution (keeping all existing features):
> > - On first use, git generates a secret "token" (e.g. a random string in
> >   ~/.gitsecret)
> > - On calling `git init` or `git clone`, the secret is copied into the
> >   new .git directory and serves as proof that this clone was created by
> >   this user
>
> Sure, but the problem is not with direct clones (at least, not using the
> --local optimization), but with clones that recursively clone other
> submodules.

This is a think-o. I meant to ask whether or not we would respect the token from the top-most $GIT_DIR in nested bare repositories. I imagine we would not (otherwise this proposal would not provide any additional security guarantees), and so...

Show 8 quoted lines
> > - Editors would no longer need to prompt the user for "Do you trust this
> >   repository?" in most cases, because git could prove the clone is user
> >   generated.
>
> If the above is true (that Git would not copy the token into recursively
> cloned submodules), then I admit to struggling a bit to see how this
> proposal would remove the need to consult the user in this case. Instead
> of the editor doing it, the user would need to do it themselves?

The rest is the same as before (swapping submodules for nested bare repositories).

Thanks, Taylor

Michael Lohmann· Oct 8, 2025, 22:09 UTC · re: Taylor Blau · lore

Re: [RCF] Secure git against involuntary arb. code execution without feature loss

Show 19 quoted lines
> On 8. Oct 2025, at 23:49, Taylor Blau <me@ttaylorr.com> wrote:
> 
> On Wed, Oct 08, 2025 at 05:30:08PM -0400, Taylor Blau wrote:
>> On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:
>>> * Proposed solution (keeping all existing features):
>>> - On first use, git generates a secret "token" (e.g. a random string in
>>>  ~/.gitsecret)
>>> - On calling `git init` or `git clone`, the secret is copied into the
>>>  new .git directory and serves as proof that this clone was created by
>>>  this user
>> 
>> Sure, but the problem is not with direct clones (at least, not using the
>> --local optimization), but with clones that recursively clone other
>> submodules.
> 
> This is a think-o. I meant to ask whether or not we would respect the
> token from the top-most $GIT_DIR in nested bare repositories. I imagine
> we would not (otherwise this proposal would not provide any additional
> security guarantees), and so...

My understanding was that submodules would add their own git folder in the top-level .git/modules/my-submodule, so obviously in order to trust a submodule, you need to "sign" these too and you could automatically also with --recursive.

(Sorry @Taylor - I missed adding the whole list as recipient, so you get this twice...)

brian m. carlson· Oct 8, 2025, 21:35 UTC · re: Michael Lohmann · lore

Re: [RCF] Secure git against involuntary arb. code execution without feature loss

On 2025-10-08 at 21:02:03, Michael Lohmann wrote:
> Hello everyone,
Hi,
Show 27 quoted lines
> Hooks, as well as certain config (e.g. `core.pager`) can do automatic
> code execution for you. In general, this is a great feature and should
> be kept without the user noticing any changes.
> 
> BUT if you download a random folder which to you unknowingly is a repo
> and either you or e.g. your command line prompt automatically executes a
> simple `git status`, it feels bad if this results in arbitrary code
> execution (ACE), e.g.:
> 
>  https://www.sonarsource.com/blog/securing-developer-tools-git-integrations/
> 
> and
> 
>  https://github.com/justinsteven/advisories/blob/main/2022_git_buried_bare_repos_and_fsmonitor_various_abuses.md
> 
> Apart from one core maintainer, all git user I talked to were surprised
> and shocked by how simple an exploit like this was.
> 
> * Proposed solution (keeping all existing features):
> - On first use, git generates a secret "token" (e.g. a random string in
>   ~/.gitsecret)
> - On calling `git init` or `git clone`, the secret is copied into the
>   new .git directory and serves as proof that this clone was created by
>   this user
> - Before executing any user-defined code, check for the local token:
>   - If present, proceed as usual.
>   - Otherwise abort.

This has all of the downsides of our existing per-Unix user approach and is also more complicated. You'll notice that when we changed to the per-Unix user approach, that broke containers, shared repositories, and even the detection of whether a directory _is_ a repository, and this would do the same thing. It would be even worse for containers because if you copied a token into the container from the container user, you'd end up polluting that directory with lots of useless tokens that never get cleaned up.

It is also easy to bypass, since if we share multiple repositories as collaborators, as soon as I can read the secret from one of them, I can then write it into any other location. It would even be sufficient if we were users on the same system (such as is common in universities or some businesses) and I could read the repository. Many websites even accidentally expose their `.git` directories, which would not only expose their repository but also allow attacks against all of the administrator's repositories.

What would be better to see instead is a config option that restricts which external commands (via config options or hooks) can be executed from local config. Then it would be possible to say, "Never honour local config to execute code." With `includeIf`, this can allowlist certain repositories to do that and leave the rest disabled.

Maybe something like this:
[safe]
  config = core.editor
  config = core.fsmonitor
  hook = pre-receive
If you wanted to implement _that_, I'd be all over it.
-- 
brian m. carlson (they/them)
Toronto, Ontario, CA
Michael Lohmann· Oct 8, 2025, 22:25 UTC · re: brian m. carlson · lore

Re: Re: [RCF] Secure git against involuntary arb. code execution without feature loss

(Sorry @Brian, the first time I forgot to add the mailing list in cc, and the second time I didn't have my mail client under control adding HTML... Now trying with send-mail again in order to try and reduce the possible errors. I am sorry, I am still quite inexperienced with the mailing list and you need to suffer from my stupid mistakes!)

On Wed, 8 Oct 2025 21:35:56, brian m. carlson wrote:
Show 8 quoted lines
> This has all of the downsides of our existing per-Unix user approach and
> is also more complicated.  You'll notice that when we changed to the
> per-Unix user approach, that broke containers, shared repositories, and
> even the detection of whether a directory _is_ a repository, and this
> would do the same thing.  It would be even worse for containers because
> if you copied a token into the container from the container user, you'd
> end up polluting that directory with lots of useless tokens that never
> get cleaned up.

Since I never worked in such a scenario, I think I am missing the full implications. Instead of always requiring that, you could simply set something like GIT_ALLOW=true or pass `--allow` (or maybe even a _global only_ config `safe.allow = *` and then you explicitly opt out of this protection for cases like this.

> It is also easy to bypass, since if we share multiple repositories as
> collaborators, as soon as I can read the secret from one of them, I can
> then write it into any other location.

Then instead of having a single file with all the tokens of users, create different files with the respective user as the only one allowed to read.

Show 5 quoted lines
> It would even be sufficient if we were users on the same system (such
> as is common in universities or some businesses) and I could read the
> repository.  Many websites even accidentally expose their `.git`
> directories, which would not only expose their repository but also
> allow attacks against all of the administrator's repositories.

What is different to the current situation then, apart from that you specifically have to leak that secret and you now have to be targeted individually and if you notice it being compromised you can rotate it?

Maybe instead of a shared global secret, create individuals you can revoke independently?

Show 12 quoted lines
> What would be better to see instead is a config option that restricts
> which external commands (via config options or hooks) can be executed
> from local config.  Then it would be possible to say, "Never honour
> local config to execute code."  With `includeIf`, this can allowlist
> certain repositories to do that and leave the rest disabled.
> 
> Maybe something like this:
> 
> [safe]
>  config = core.editor
>  config = core.fsmonitor
>  hook = pre-receive
Interesting idea!
Jeff King· Oct 9, 2025, 05:24 UTC · re: Michael Lohmann · lore

Re: [RCF] Secure git against involuntary arb. code execution without feature loss

On Wed, Oct 08, 2025 at 11:02:03PM +0200, Michael Lohmann wrote:
Show 8 quoted lines
> Hooks, as well as certain config (e.g. `core.pager`) can do automatic
> code execution for you. In general, this is a great feature and should
> be kept without the user noticing any changes.
> 
> BUT if you download a random folder which to you unknowingly is a repo
> and either you or e.g. your command line prompt automatically executes a
> simple `git status`, it feels bad if this results in arbitrary code
> execution (ACE), e.g.:

We've discussed this a few times over the years. The most recent one I can remember is:

  https://lore.kernel.org/git/ZZr-JLxubCvWe0EU@tapette.crustytoothpaste.net/
I think there are two somewhat orthogonal issues to consider:
  1. How does Git behave differently in an "unsafe" context? In the
     thread above, I propose that it should skip loading config from the
     repo-level $GIT_DIR/config file, and turn off hooks inside the
     repo. Elsewhere, others have proposed finer-grained control (like
     specific config options). IMHO the most important thing here is
     maintainability, and having a scheme where we do not accidentally
     add code that lets an untrusted repository do bad things.
  2. How does the user tell Git which repos are safe or unsafe? You've
     got a scheme here for marking user-created repositories with a
     secret token. I think that could work, but there are other simpler
     methods. E.g., we could pass down information through the
     environment (like we do already for security features like
     GIT_ALLOW_PROTOCOL), or mark a list of safe directories in user- or
     system-level config (like we have already with safe.directories).
     It's perhaps even reasonable to have multiple such mechanisms, as
     they have different tradeoffs in convenience and security.

So in some sense I think talking about this token scheme and Git 3.0 compatibility is putting the cart before the horse. We need (1) first. And then once we have it, I think the simplest thing is not turning it on all the time, but letting commands opt into it through command-line options and environment variables. So you could imagine git-prompt running "git --assume-unsafe" or setting "GIT_ASSUME_UNSAFE=1" in the environment. People who want to be more paranoid can set that variable for their normal commands to opt into greater security (at the cost of convenience).

Then once we have some practical experience with the system, we can consider whether we should flip the default. And in the meantime we can experiment with idea like your token scheme.

-Peff
Michael Lohmann· Oct 9, 2025, 22:43 UTC · re: Jeff King · lore

Re: [RCF] Secure git against involuntary arb. code execution without feature loss

TL;DR: Thanks to all peoples input I just mentally untangeled/refactored a few things about the discusion for myself:

* Current situation:
    git assumes an unsafe repo, if neither
        a) the user owns it, nor
        b) it is included in "safe.directory" config
    and then refuses any operation.
* Suggestions from the discussion:
1) The suggested "token" approach is just an additional way to set the
   current state of allow "safe.directory" for repos _created_ by this
   user
2) The suggested --assume-(un)safe / GIT_ASSUME_(UN)SAFE are additional
   ways of setting the same state temporarily
3) There are suggested improvements on not blankly refusing operation at
   all if assumed to be unsafe
4) The _enforcement_ part of the RFC is about dropping the check for the
   owner of the repo to assume if safe or not

I think 1)-3) are relatively independent of one another to discuss, whereas 4) probably requires all others to be viable.

And now for the long answer:
Show 9 quoted lines
> We've discussed this a few times over the years. The most recent one I
> can remember is:
>
>   https://lore.kernel.org/git/ZZr-JLxubCvWe0EU@tapette.crustytoothpaste.net/
>
>   1. How does Git behave differently in an "unsafe" context?
>      In the thread above, I propose that it should skip loading config
>      from the repo-level $GIT_DIR/config file, and turn off hooks
>      inside the repo.

Indeed it would be a lot nicer, if git still worked and only showed a (maybe hideable) warning. As mentioned in the above conversation, the config parser could only extract the minimum required fields in the assumed unsafe state and the hooks would be disabled, too.

Show 5 quoted lines
>   2. How does the user tell Git which repos are safe or unsafe? You've
>      got a scheme here for marking user-created repositories with a
>      secret token. I think that could work, but there are other simpler
>      methods. E.g., we could pass down information through the
>      environment

I am not sure I can follow you. Just as an overwrite like you mentioned below / as in suggestion 2)?

>      or mark a list of safe directories in user- or system-level
>      config (like we have already with safe.directories).
>      It's perhaps even reasonable to have multiple such mechanisms, as
>      they have different tradeoffs in convenience and security.
Indeed! E.g. for "automated trusting" on init/clone the path based
approach is probably not the best, since
a) if the user runs `git init /trusted && mv /trusted /elsewhere`, all
   of the sudden git would no longer work in that repo and
b) now the path "/trusted" is vulnerable, unknown to the user.
> So in some sense I think talking about this token scheme and Git 3.0
> compatibility is putting the cart before the horse. We need (1) first.

IMHO, an option on how to _detect_ if this repo is a "safe.directory" would not depend on improvements on how to _act_ upon it (or the other way around). Yes - to _enforce_ not whitelisting all user owned repos by default any more, both are probably needed. So indeed this part is more speculative.

-Michael
Michael Lohmann· Oct 13, 2025, 09:57 UTC · re: Jeff King · lore

Submitted patches for "assume unsafe"

Hello! As a first step, here are the patches for "--assume-unsafe" / "GIT_ASSUME_UNSAFE" / "safe.assumeUnsafe":

 https://lore.kernel.org/git/20251013094152.23597-1-git@lohmann.sh/
-Michael

← back to recent threads