Skip to content

fix: harden dev.containers.copyGitConfig — docs + runtime cleanup - #95

Open
shejnowicz wants to merge 6 commits into
masterfrom
fix/34-copyGitConfig-hardening
Open

fix: harden dev.containers.copyGitConfig — docs + runtime cleanup#95
shejnowicz wants to merge 6 commits into
masterfrom
fix/34-copyGitConfig-hardening

Conversation

@shejnowicz

Copy link
Copy Markdown
Collaborator

Fixes #34.

Summary

dev.containers.copyGitConfig: false placed in devcontainer.json.customizations.vscode.settings is inert where it sits — VS Code's Dev Containers extension reads this setting from the host user settings, not from devcontainer.json. Result: the host .gitconfig (with credential helpers, signing key IDs, commit.gpgsign = true, etc.) gets copied into the container despite sandcat's declared intent otherwise, breaking sandcat's promise in the README's "Consequences of hardening" section.

Two-part fix (belt-and-braces):

  1. Docs correctiondevcontainer.json comment and README updated to note that the setting must also be in host user settings for full effect (mirrors the existing terminal.integrated.allowLocalTerminal warning that already flags this same class of trap).
  2. Runtime cleanupapp-user-init.sh unconditionally rm -f "$HOME/.gitconfig" before applying GIT_USER_NAME / GIT_USER_EMAIL from env. Whatever the extension copied (if the user's host settings don't disable the copy), sandcat wipes it before the agent runs. Applies to all agents identically.

Test plan

  • 132/132 init bats tests pass on fix/34-copyGitConfig-hardening locally, including the new assertion that the devcontainer.json comment mentions "host user settings"
  • Hands-on end-to-end verified against a real sandcat stack:
    • Baseline: normal container start → ~/.gitconfig contains only sandcat-managed keys (user.name, user.email from env; commit.gpgsign = false)
    • Poisoned scenario: docker cp a fake .gitconfig with user.signingkey, credential.helper = osxkeychain, commit.gpgsign = true, core.editor = /usr/bin/nano-evil into /home/vscode/.gitconfig — mimics what VS Code Dev Containers extension does
    • Trigger cleanup: re-run /usr/local/bin/app-user-init.sh
    • Result: user.signingkey, credential.helper, core.editor all absent; user.name / user.email back to sandcat env-derived values; commit.gpgsign back to false

Notes

  • Backward compat: existing sandcat projects reuse the same app-user-init.sh — on the next container start, any .gitconfig in the agent-home volume (including one the user might have manually created inside the container) is removed. Users who want to customize git config in-container should do it via GIT_USER_NAME / GIT_USER_EMAIL env vars or a post-start hook, not by editing .gitconfig directly.
  • No configuration flag: the cleanup is unconditional. The template's copyGitConfig: false already declares the intent; the runtime cleanup honors that intent regardless of whether host settings agree. If a future opt-in is needed (e.g. SANDCAT_KEEP_HOST_GITCONFIG=true), that's a separate PR.

🤖 Generated with Claude Code

shejnowicz and others added 5 commits August 12, 2026 15:03
Design: belt-and-braces fix for issue #34. The dev.containers.copyGitConfig
setting placed in devcontainer.json.customizations.vscode.settings is
inert — VS Code's Dev Containers extension reads it from HOST user
settings, not the container-side JSON, so the host .gitconfig still
gets copied. Two-part fix:

1. Docs correction: update devcontainer.json comment + README to
   note the host-user-settings requirement, mirroring the existing
   terminal.integrated.allowLocalTerminal warning.

2. Runtime cleanup: app-user-init.sh unconditionally removes
   ~/.gitconfig before applying the sandcat-managed identity from
   env vars. Idempotent, applies to every agent, no configuration.

Plan: 4 tasks — runtime cleanup in app-user-init.sh, devcontainer.json
comment + bats test, README update, hands-on integration test that
poisons ~/.gitconfig via docker cp and verifies cleanup wipes it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 12, 2026 13:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Hardens sandcat devcontainer behavior around dev.containers.copyGitConfig by documenting the host-settings requirement and adding a runtime cleanup so host .gitconfig contents can’t leak into the container.

Changes:

  • Add a bats test ensuring the template comment mentions “host user settings”
  • Update app-user-init.sh to remove ~/.gitconfig before applying env-derived git identity
  • Update template comment and README to clarify the host user settings requirement and the cleanup fallback

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
cli/test/init/devcontainer.bats Adds an assertion that the template comment points users to host settings.
cli/templates/devcontainer/sandcat/scripts/app-user-init.sh Removes any existing ~/.gitconfig before applying sandcat-managed git config.
cli/templates/devcontainer/devcontainer.json Expands the comment to clarify where copyGitConfig must be set and notes the runtime fallback.
README.md Updates hardening docs to explain the host settings requirement and the cleanup behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +117 to +125
@test "devcontainer.json comment notes copyGitConfig needs host user settings" {
# Verify the comment above `dev.containers.copyGitConfig` explicitly
# points at the host-user-settings requirement — mirrors the note on
# `terminal.integrated.allowLocalTerminal`. See issue #34.
local template="$SCT_TEMPLATEDIR/devcontainer/devcontainer.json"
run grep -B4 '"dev.containers.copyGitConfig"' "$template"
assert_success
assert_output --partial "host user settings"
}
Comment on lines +13 to +20
# VS Code's Dev Containers extension reads dev.containers.copyGitConfig
# from HOST user settings, not from devcontainer.json — so a host
# ~/.gitconfig may have been copied in even though our template
# declares copyGitConfig: false (see issue #34). Remove any leftover
# gitconfig so it can't leak host credential helpers / signing keys
# or override the env-derived identity we're about to apply.
# Unconditional and idempotent — `-f` swallows the missing-file case.
rm -f "$HOME/.gitconfig"
Comment on lines +37 to +44
// Signal our intent to VS Code that host .gitconfig should not
// be copied into the container (which can leak credential helpers
// and signing key references). NOTE: this setting is read by VS
// Code's Dev Containers extension from HOST user settings, not
// from this file, so it only takes full effect if you also set
// "dev.containers.copyGitConfig": false in your host user settings.
// See README for details. app-user-init.sh removes any .gitconfig
// that gets through as a defense-in-depth cleanup.
Comment thread README.md Outdated
Comment on lines +1256 to +1263
- **Disables git config copying** (`dev.containers.copyGitConfig: false` in
`devcontainer.json`) to prevent leaking host credential helpers and signing
key references into the container. The VS Code Dev Containers extension
reads this setting from your **host** user settings, not from
`devcontainer.json`, so for full effect also set it in
`~/Library/Application Support/Code/User/settings.json` (macOS) or the
equivalent for your OS. As a defense-in-depth fallback, `app-user-init.sh`
removes any `.gitconfig` that gets copied in anyway.
… enumerate OS paths

- Bats: replace brittle grep -B4 window with two independent grep -q
  presence checks (setting key + "host user settings" phrase anywhere in file)
- devcontainer.json: reword comment from "Signal our intent" to "Document
  intent only" to make clear the setting here alone does NOT prevent copying
- README: replace vague macOS-only path hint with explicit table of all three
  OS paths and a Command Palette alternative

Addresses Copilot review comments on PR #95. See issue #34.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@shejnowicz

Copy link
Copy Markdown
Collaborator Author

Three of the four Copilot comments addressed in 4f651be:

  • Bats brittleness (devcontainer.bats:125) → two independent grep -q presence checks, decoupled from any -B4 window. Comment expansions won't break the test as long as both markers stay in the file.
  • devcontainer.json wording (line 44) → "Document intent only" opening replaces the mildly self-contradictory "Signal our intent" phrasing.
  • README OS paths (line 1263) → explicit table for macOS / Linux / Windows plus a Command Palette hint (Preferences: Open User Settings (JSON)) so users on any OS have an actionable step.

app-user-init.sh:20 (rm -f "$HOME/.gitconfig" as destructive) — kept as-is; the GIT_CONFIG_GLOBAL alternative doesn't fit sandcat's threat model:

  • Setting GIT_CONFIG_GLOBAL makes git ignore ~/.gitconfig — but the poisoned file still sits on disk. The sandcat agent can cat ~/.gitconfig, read the host's signing-key fingerprint or credential-helper details, and act on that information via tools that don't respect GIT_CONFIG_GLOBAL (libgit2 bindings, IDE integrations, custom scripts, open ~/.gitconfig, etc.). Sandcat's model is "no host state carried over" — rm deletes the data, GIT_CONFIG_GLOBAL only masks it from git.
  • "Legitimate in-container user configuration" is a real concern, but sandcat already documents (README "Consequences of hardening" + Task 4 report) that in-container git customization goes via GIT_USER_NAME / GIT_USER_EMAIL env vars or a post-start hook, not by editing .gitconfig directly — because the agent-home volume + snapshot refresh mean manually-edited files can already be replaced on next start (see the existing "refresh masked /home/vscode from image snapshot on start" behavior).
  • If a future opt-in for preserving .gitconfig is desired, that's a follow-up flag (e.g. SANDCAT_KEEP_GITCONFIG=true), not a change of default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dev.containers.copyGitConfig may be ineffective in this location

2 participants