<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>github Archives - DIGITAL SELF-DEFENSE</title>
	<atom:link href="https://www.digitalnisebeobrana.cz/en/tag/github/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.digitalnisebeobrana.cz/en/tag/github/</link>
	<description></description>
	<lastBuildDate>Tue, 08 Sep 2026 10:00:31 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://www.digitalnisebeobrana.cz/wp-content/uploads/2018/12/cropped-mr.black_-32x32.png</url>
	<title>github Archives - DIGITAL SELF-DEFENSE</title>
	<link>https://www.digitalnisebeobrana.cz/en/tag/github/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to Work Safely with an Untrusted Git Repository</title>
		<link>https://www.digitalnisebeobrana.cz/en/how-to-work-safely-with-an-untrusted-git-repository/</link>
		
		<dc:creator><![CDATA[Milan]]></dc:creator>
		<pubDate>Tue, 08 Sep 2026 09:59:53 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[nástroje]]></category>
		<category><![CDATA[Techniky hackerů]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[gitlab]]></category>
		<category><![CDATA[hackeři]]></category>
		<category><![CDATA[sysadmin]]></category>
		<guid isPermaLink="false">https://www.digitalnisebeobrana.cz/?p=7178</guid>

					<description><![CDATA[<p>An unfamiliar repository may be completely legitimate. It still contains code and configuration written by someone else. What should you check before installing dependencies, running builds or tests, and how can you limit the risk when using IDEs, coding agents, and Git itself?</p>
<p>The post <a href="https://www.digitalnisebeobrana.cz/en/how-to-work-safely-with-an-untrusted-git-repository/">How to Work Safely with an Untrusted Git Repository</a> appeared first on <a href="https://www.digitalnisebeobrana.cz/en">DIGITAL SELF-DEFENSE</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Working with someone else&#8217;s Git repository is completely normal. It might be an open-source project, code delivered by a contractor, a take-home assignment, a new project at work, or simply a repository someone has asked you to review.</p>
<p>Cloning it is the obvious first step. From a security perspective, the more interesting question is what starts running after that.</p>
<p>Your editor may load project-specific configuration. A package manager may execute scripts during installation. Builds and tests run additional code. Submodules may pull in more repositories. A coding agent may perform several of these steps automatically. And if you received the entire working directory, including <code>.git</code>, local repository configuration can even change how Git itself behaves.</p>
<p>That does not mean every unfamiliar project needs a dedicated malware-analysis lab. It does mean it is useful to know when you stop merely reading a project and start allowing it to execute code.</p>
<h2>Opening a project can mean more than viewing source files</h2>
<p>A modern editor or IDE is not just a text viewer. Once a project is opened, it may load workspace-specific settings, run tasks, configure a debugger, initialize a build system, or enable other project-level automation.</p>
<p>This is why development environments increasingly distinguish between trusted and untrusted projects.</p>
<p>VS Code, for example, has <strong>Workspace Trust</strong> and Restricted Mode. Until you trust a workspace, it restricts features such as tasks, debugging, and some workspace settings. JetBrains IDEs have a similar Safe Mode that lets you inspect source code without automatically running Maven or Gradle imports and other project scripts.</p>
<p>If your editor asks whether you trust an unfamiliar project, it is not merely showing you another dialog to dismiss. That decision can determine which project-controlled features are allowed to run automatically.</p>
<p>For an initial review, it therefore makes sense to keep a restricted or safe mode enabled if your development environment provides one.</p>
<h2>Installing dependencies can already execute code</h2>
<p>The next important boundary is project setup.</p>
<p>A JavaScript project will often tell you to run something like:</p>
<pre><code>npm install
npm run dev</code></pre>
<p><code>npm install</code> is not limited to downloading files into <code>node_modules</code>. npm supports lifecycle scripts such as <code>preinstall</code>, <code>install</code>, <code>postinstall</code>, and <code>prepare</code>, and these may run automatically during installation.</p>
<p>These hooks are a legitimate part of the ecosystem and many perfectly normal packages rely on them. But they are still executable code.</p>
<p>Before installing an unfamiliar project, it is therefore worth checking at least its <code>package.json</code> and the scripts it defines.</p>
<p>For an initial inspection, npm can also install dependencies without running package scripts:</p>
<pre><code>npm install --ignore-scripts</code></pre>
<p>This prevents npm from automatically executing lifecycle scripts during installation. It is not a sandbox, and it does not make the project safe. If you later explicitly run <code>npm run dev</code>, <code>npm test</code>, or another script, that command will still execute its target.</p>
<p>What it does give you is a way to separate downloading dependencies from automatically executing additional code during installation.</p>
<p>The same principle applies outside npm. Maven, Gradle, Python packaging systems, Make, Cargo, and other build or package systems all have their own ways of executing code during setup and builds. The exact mechanism changes, but the security question remains the same: <strong>what actually runs when I perform the step described as installation, build, or test?</strong></p>
<h2>A malicious command does not have to look malicious</h2>
<p>I ran into exactly this problem while analyzing a malicious repository sent as part of a fake job offer.</p>
<p>Its README contained a perfectly ordinary instruction:</p>
<pre><code>npm run dev</code></pre>
<p>The project did start. At the same time, malicious code executed in the background, discovered the address of its command-and-control server, downloaded additional code, and began establishing persistence in development tools.</p>
<p>The attacker did not need to convince the victim to run something called <code>run-malware</code>. The malicious behavior was hidden behind a step that someone reviewing a frontend project would normally perform anyway.</p>
<p>I described the full case in <a href="/en/i-ran-malware-on-purpose-it-found-its-server-address-in-the-blockchain/">I Ran Malware on Purpose. It Found Its Server Address in the Blockchain</a>.</p>
<p>This is why looking only for obviously suspicious commands is not enough. It also matters what an ordinary command launches, imports, downloads, or executes behind the scenes.</p>
<h2>A coding agent can walk through the same chain automatically</h2>
<p>Coding agents do not create this problem, but they can make the entire sequence much faster and less visible.</p>
<p>If you ask an agent to “review this project and explain how it works,” it may be able to stay entirely within static analysis. But a task such as “get the project running,” “fix the failing tests,” “find the bug,” or “verify that the application works” may legitimately require it to install dependencies, run a build, execute tests, start a development server, or launch other tools defined by the project.</p>
<p>The agent does not have to violate your instructions. It can follow them exactly and still execute a malicious install or build script.</p>
<p>That is why limiting the environment in which the agent operates is more useful than relying on it to correctly identify every dangerous command.</p>
<p>Claude Code, for example, separates tool permissions from sandboxing. Permissions determine which tools the agent may use, while the sandbox can restrict filesystem and network access for Bash and the processes Bash launches. That means the same boundaries can also apply to <code>npm</code>, build scripts, test runners, and other child processes started by the agent.</p>
<p>The sandbox can be configured from Claude Code with:</p>
<pre><code>/sandbox</code></pre>
<p>For a genuinely untrusted project, sandboxing is most useful when combined with restricted access to sensitive files and unnecessary network destinations. Requiring confirmation for every individual command is a weaker control on its own. A long sequence of otherwise routine-looking commands can also lead to approval fatigue, where prompts are eventually confirmed almost automatically.</p>
<h2>One repository can pull in more repositories</h2>
<p>Git submodules allow one repository to reference additional Git repositories. Their configuration lives in <code>.gitmodules</code>, which is part of the repository itself.</p>
<p>That makes these two commands meaningfully different:</p>
<pre><code>git clone https://example.com/project.git</code></pre>
<p>and:</p>
<pre><code>git clone --recurse-submodules https://example.com/project.git</code></pre>
<p>The second form immediately starts resolving and cloning additional repositories referenced by the project.</p>
<p>With an unfamiliar repository, there is little reason to initialize submodules before checking where they point. You can inspect <code>.gitmodules</code> first and initialize the submodules only if you actually need them.</p>
<p>Git also has protocol policies for network operations triggered indirectly. Some protocols are allowed by default, the more dangerous <code>ext</code> protocol is disabled, and others may use the <code>user</code> policy, which distinguishes between a transport explicitly requested by the user and one triggered automatically, for example by a submodule.</p>
<p>There is usually no reason to loosen these defaults globally. If a particular project requires broader protocol permissions, it is better to understand why before changing them.</p>
<h2>Receiving someone else&#8217;s <code>.git</code> is a different situation</h2>
<p>So far, we have assumed that you clone a normal remote repository yourself.</p>
<p>The situation changes if someone gives you the entire working directory, for example as a ZIP archive, through a shared drive, or in a synchronized folder:</p>
<pre><code>project/
├── src/
├── package.json
├── README.md
└── .git/</code></pre>
<p>In that case, you did not receive only the source files and Git history. You also received someone else&#8217;s local Git metadata.</p>
<p>This is where the distinction from a normal <code>git clone</code> matters. A remote clone does not copy the source repository&#8217;s local <code>.git/config</code> or its hooks. Git creates a new <code>.git</code> directory and new local configuration on your machine.</p>
<p>If someone hands you the entire directory including <code>.git</code>, however, you receive the configuration and hooks they prepared as well.</p>
<p>Git&#8217;s own security documentation explicitly warns about this. Repository-local configuration and hooks can cause arbitrary commands to run, which is why normal Git operations directly against an untrusted <code>.git</code> directory should not be treated as safe.</p>
<h3>A current example: GitSpawn and <code>core.fsmonitor</code></h3>
<p>The recent GitSpawn research from Manifold Security provides a good example.</p>
<p>Git supports <code>core.fsmonitor</code>, which can help it detect changed files more efficiently. Its value, however, may also point to an external program.</p>
<p>An untrusted <code>.git/config</code> can therefore contain something equivalent to:</p>
<pre><code>[core]
    fsmonitor = &lt;program&gt;</code></pre>
<p>Some coding agents automatically queried the state of a Git repository after opening it. When Git performed an operation that refreshed its index, it used the configured fsmonitor and executed the attacker&#8217;s program.</p>
<p>That meant an apparently harmless command such as:</p>
<pre><code>git status</code></pre>
<p>could result in execution of a program specified in the repository&#8217;s local configuration.</p>
<p>For this specific path, an automated status check can instead use:</p>
<pre><code>git -c core.fsmonitor=false status</code></pre>
<p>The <code>-c</code> option overrides the configuration value for that Git invocation, so the repository&#8217;s <code>core.fsmonitor</code> setting is not used for this command.</p>
<p>A global setting such as:</p>
<pre><code>git config --global core.fsmonitor false</code></pre>
<p>is not a reliable defense against a malicious repository-local value. Repository configuration has higher precedence than global user configuration and can override it.</p>
<p><code>git -c core.fsmonitor=false status</code> is also not a general-purpose way to “secure Git.” It addresses this specific <code>core.fsmonitor</code> path. Git configuration and hooks provide other ways to influence program execution.</p>
<h2><code>safe.directory</code> is not a list of trusted projects</h2>
<p>Git has a protection mechanism for repositories owned by another user.</p>
<p>By default, Git refuses to work with a repository owned by someone else and will not trust its repository-local configuration or hooks. Specific exceptions can be added through <code>safe.directory</code>.</p>
<p>This is useful, for example, on multi-user systems.</p>
<p>It is not a general defense against a malicious repository. If you extract an archive prepared by someone else, the resulting files will normally be owned by your own account. From an ownership perspective, Git has no reason to reject them even though the contents of <code>.git</code> came from an untrusted source.</p>
<p>If Git reports “dubious ownership,” it is therefore a bad idea to solve the problem automatically with:</p>
<pre><code>git config --global --add safe.directory '*'</code></pre>
<p>The <code>*</code> value disables the ownership protection globally. It is better to understand why a particular repository has a different owner and add a specific exception only when necessary.</p>
<h2>When you actually need the untrusted <code>.git</code></h2>
<p>Sometimes there is no trusted remote copy and you need to preserve the history, branches, or other Git metadata from the directory you received.</p>
<p>Git recommends not working directly in the original repository in that case. Instead, create a fresh clone from it:</p>
<pre><code>git clone --no-local /path/to/original-repository /path/to/clean-copy</code></pre>
<p><code>--no-local</code> prevents Git from using the usual local-clone optimizations and creates a new repository through the normal Git transport mechanism. The resulting copy therefore gets a new <code>.git</code> directory instead of inheriting the original repository&#8217;s local configuration and hooks.</p>
<p>This is still not an absolute sandbox. The server side of the clone is handled by <code>git-upload-pack</code>, and Git&#8217;s own documentation points out that this component has an attack surface of its own. For a genuinely hostile <code>.git</code> directory, Git recommends running that side under an unprivileged user or adding another isolation layer.</p>
<p>If you do not need the history at all, the simpler option is not to use the supplied <code>.git</code> directory in the first place.</p>
<h2>You can also restrict automatic bare-repository discovery</h2>
<p>Git can work with bare repositories, which contain Git data without a normal working tree.</p>
<p>If you do not use bare repositories in your normal workflow, Git provides a useful hardening option:</p>
<pre><code>git config --global safe.bareRepository explicit</code></pre>
<p>With this setting, Git will not automatically operate on a bare repository that it merely discovers somewhere in the directory structure. The repository has to be selected explicitly.</p>
<p>Git documents this option as a mitigation for attacks in which a cloned project contains another bare repository that a later Git command discovers automatically.</p>
<h2>When you really need to run the project</h2>
<p>Static inspection only gets you so far. At some point you may need to start the application, reproduce a bug, run tests, or observe its behavior.</p>
<p>At that point you already know you are going to execute untrusted code. The most important question is no longer another individual Git setting, but the environment in which that code will run.</p>
<p>A normal developer account may have access to SSH keys, private repositories, Git and cloud tokens, production <code>.env</code> files, saved sessions, other source trees, or credential agents running in the background.</p>
<p>An unfamiliar project usually needs none of these.</p>
<p>If you do not trust a project but still need to run it, use a disposable or otherwise restricted environment without real credentials and without unnecessary access to host data.</p>
<p>Depending on the risk, that may be a sandbox, a separate user account, an isolated development environment, or a disposable virtual machine.</p>
<p>A container can be part of that setup, but “it runs in Docker” is not the same as strong isolation. Docker bind mounts are writable by default, which allows a process in the container to modify files on the host. Access to the Docker daemon is even more powerful; Docker&#8217;s own documentation warns that controlling the daemon effectively grants root-level capabilities on the host.</p>
<p>I would therefore avoid exposing the Docker socket, a full home directory, an SSH agent, or credential directories to an untrusted container. If the project only needs to read host files, a read-only mount is preferable to a writable one.</p>
<p>Network access deserves the same treatment. If the project does not need unrestricted outbound connectivity, there is no reason to provide it automatically.</p>
<p>A VM snapshot is useful for restoring disk state. It is not a defense against data exfiltration. Rolling back a machine does not revoke a token that has already been sent somewhere else.</p>
<h2>Practical security baseline</h2>
<p>None of the measures below make an untrusted repository trustworthy on their own. They reduce different ways in which a project, Git, or the tools around it can execute code or reach the rest of your system.</p>
<figure class="wp-block-table">
<table>
<thead>
<tr>
<th>Situation</th>
<th>What to do</th>
<th>Why</th>
<th>What it does not solve</th>
</tr>
</thead>
<tbody>
<tr>
<td>Opening an unfamiliar project</td>
<td>Use Restricted / Safe Mode if your editor or IDE supports it.</td>
<td>Limits automatic tasks, builds, and project-controlled configuration before review.</td>
<td>Does not protect you from code you later choose to execute.</td>
</tr>
<tr>
<td>Initial npm installation</td>
<td><code>npm install --ignore-scripts</code></td>
<td>Prevents lifecycle scripts from running automatically during installation.</td>
<td>It is not a sandbox. An explicit <code>npm run</code> still executes its target script.</td>
</tr>
<tr>
<td>Git submodules</td>
<td>Do not start with <code>--recurse-submodules</code>; inspect <code>.gitmodules</code> first.</td>
<td>The main repository may point to additional code sources.</td>
<td>Says nothing about whether the main repository itself is safe.</td>
</tr>
<tr>
<td>Git protocol policy</td>
<td>Do not loosen <code>protocol.*.allow</code> globally without a specific reason.</td>
<td>Git already has security-oriented defaults for automatically triggered transports and submodules.</td>
<td>An allowed HTTPS or SSH repository can still contain malicious code.</td>
</tr>
<tr>
<td>Repository owned by another user</td>
<td>Do not use <code>safe.directory=*</code> globally. Add a specific exception only when necessary.</td>
<td>Preserves Git&#8217;s ownership protection.</td>
<td>Does not protect you from a malicious repository that is already owned by your account.</td>
</tr>
<tr>
<td>Bare repositories</td>
<td><code>git config --global safe.bareRepository explicit</code> if you do not normally rely on implicit bare repositories.</td>
<td>Restricts automatic discovery of a bare repository embedded in an untrusted project.</td>
<td>Does not address normal working-tree repositories or other execution paths.</td>
</tr>
<tr>
<td>An entire untrusted <code>.git</code> directory</td>
<td><code>git clone --no-local /source /clean-copy</code></td>
<td>Creates a fresh <code>.git</code> instead of working directly with untrusted local configuration and hooks.</td>
<td><code>git-upload-pack</code> still has its own attack surface; highly untrusted input should be isolated further.</td>
</tr>
<tr>
<td>Automated <code>git status</code> against an untrusted <code>.git</code></td>
<td><code>git -c core.fsmonitor=false status</code></td>
<td>Prevents that command from using a repository-supplied <code>core.fsmonitor</code> program.</td>
<td>Addresses only this specific GitSpawn path.</td>
</tr>
<tr>
<td>Coding agent</td>
<td>Use a sandbox and restrict filesystem and network access. In Claude Code, for example, configure sandboxing through <code>/sandbox</code>.</td>
<td>The same restrictions can apply to processes started by the agent, including npm and build scripts.</td>
<td>Sandboxing does not replace sensible permissions and review of sensitive actions.</td>
</tr>
<tr>
<td>Running genuinely untrusted code</td>
<td>Use a disposable or restricted environment without SSH keys, production tokens, cloud credentials, the host Docker socket, or other unnecessary data.</td>
<td>Reduces the impact if malicious code actually runs.</td>
<td>Does not prevent the malicious program from executing.</td>
</tr>
<tr>
<td>Docker</td>
<td>Do not expose the Docker socket to untrusted containers; restrict host mounts and make them read-only where possible.</td>
<td>The Docker socket provides extremely powerful host access, while writable bind mounts allow changes to host files.</td>
<td>A container is not, by itself, a complete security boundary.</td>
</tr>
<tr>
<td>Git and surrounding tooling</td>
<td>Keep supported versions up to date using the normal update mechanism for your operating system or distribution.</td>
<td>Git, package managers, editors, and runtimes all have security vulnerabilities of their own.</td>
<td>Updates do not protect against legitimate features being used maliciously exactly as designed.</td>
</tr>
</tbody>
</table>
</figure>
<h2>How much trust does the project actually need?</h2>
<p>You do not have to decide once and for all whether an unfamiliar repository is “safe” or “unsafe.”</p>
<p>It is more practical to grant trust gradually.</p>
<p>Reading source files does not require access to your SSH keys. Installing dependencies does not always require running every install hook. A coding agent that only needs to explain the architecture does not automatically need permission to execute arbitrary software. And a project that really does need to run does not have to run under the same account, with the same credentials, as the rest of your development work.</p>
<p>The less trust a particular step requires, the less trust there is a reason to give it.</p>
<blockquote>
<p><strong>Need to review the security of a development environment, CI workflow, or coding-agent setup?</strong></p>
<p>If you need a second opinion on a concrete workflow, permission model, or technical security design, we can go through it as part of a <a href="/en/osobni-konzultace/">Security Consultation</a>. The goal is a practical recommendation for your actual environment, not an unnecessarily broad audit.</p>
</blockquote>
<hr>
<h2>Sources</h2>
<ul>
<li><a href="https://git-scm.com/docs/git">Git documentation – SECURITY</a>: untrusted repositories, configuration, hooks, and recommendations for handling an untrusted <code>.git</code>.</li>
<li><a href="https://git-scm.com/docs/git-config">git-config documentation</a>: <code>safe.directory</code>, <code>safe.bareRepository</code>, protocol policies, and <code>core.fsmonitor</code>.</li>
<li><a href="https://git-scm.com/docs/git-clone">git-clone documentation</a>: local cloning and <code>--no-local</code>.</li>
<li><a href="https://docs.npmjs.com/misc/scripts/">npm Scripts documentation</a>: lifecycle scripts executed during installation.</li>
<li><a href="https://docs.npmjs.com/cli/v11/commands/npm-install/">npm install documentation</a>: <code>--ignore-scripts</code>, <code>allowScripts</code>, and <code>strict-allow-scripts</code>.</li>
<li><a href="https://code.claude.com/docs/en/security">Claude Code Security</a> and <a href="https://code.claude.com/docs/en/sandboxing">Sandboxing</a>: permissions, filesystem isolation, and network restrictions.</li>
<li><a href="https://code.visualstudio.com/docs/editing/workspaces/workspace-trust">VS Code Workspace Trust</a>.</li>
<li><a href="https://www.jetbrains.com/help/idea/project-security.html">JetBrains Project Security</a>.</li>
<li><a href="https://docs.docker.com/engine/security/">Docker Engine security</a> and <a href="https://docs.docker.com/engine/storage/bind-mounts/">Bind mounts</a>.</li>
<li><a href="https://www.manifold.security/blog/ai-coding-agents-git-hijack">Manifold Security: GitSpawn</a>.</li>
</ul>
<p>The post <a href="https://www.digitalnisebeobrana.cz/en/how-to-work-safely-with-an-untrusted-git-repository/">How to Work Safely with an Untrusted Git Repository</a> appeared first on <a href="https://www.digitalnisebeobrana.cz/en">DIGITAL SELF-DEFENSE</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
