Skip to content

fix(agentcore): disable repository hooks for Git operations - #446

Merged
jeromevdl merged 3 commits into
mainfrom
fix/agentcore-disable-repository-hooks
Sep 9, 2026
Merged

fix(agentcore): disable repository hooks for Git operations#446
jeromevdl merged 3 commits into
mainfrom
fix/agentcore-disable-repository-hooks

Conversation

@JWThewes

@JWThewes JWThewes commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Problem / Motivation

AgentCore-owned Git operations currently execute repository-controlled hooks. Hooks that depend on unavailable checkout dependencies can reject engine commits after a stage has produced work, while credential-bearing operations such as push can expose the short-lived Git credential to arbitrary repository hook code.

Why it matters

A failing prepare-commit-msg can leave completed stage work uncommitted, and a malicious pre-push or post-checkout hook can observe credentials inside the AgentCore runtime. The existing --no-verify approach is incomplete because it does not suppress every hook.

What changed

  • Preserved Olivier Rossant's PR fix(git-engine): never run the cloned repo's git hooks (core.hooksPath) #363 implementation as the contributor-authored first commit.
  • Added one shared AgentCore Git process runner that applies -c core.hooksPath=/dev/null per invocation and strips inherited repository/index and author/committer GIT_* overrides for both engine and workspace commands. Explicit credential and per-command environment overrides remain supported; repository and user Git configuration remain untouched.
  • Routed both git-engine.js and workspace.js clone, checkout, branch, commit, merge, fetch, and push paths through the shared policy while preserving injection and return contracts. Directory-trust callbacks receive the wrapped runner as well.
  • Strengthened the pre-push regression to require the remote head to equal the exact local commit.
  • Added warm-workspace and fresh-clone regressions, including the reviewed nuance that ordinary clones do not inherit remote-local Git config or .git/hooks; fresh-clone exposure requires an inherited/global hook path targeting tracked checkout content.

Attribution

The first commit retains Olivier Rossant as author and records the original PR #363 commit via the cherry-pick provenance line. The subsequent commits are the maintainer-owned completion covering the separate workspace Git path and shared environment sanitization.

Tests

  • 308/308 across nine focused suites: shared runner, Git engine, credential handling, workspace, init-ws, lane, conflict resolution, run-stage, and decisive PR fix(git-engine): never run the cloned repo's git hooks (core.hooksPath) #363 reproductions. Graph-backed tests ran with the normal Docker setup.
  • 345/345 across 12 affected suites selected by the normal pre-commit hook.
  • New regressions reproduced the unprotected trust callback and ambient-environment failures in both fresh and warm checkouts before the fixes, then passed afterward. Coverage also verifies credential preservation, explicit environment overrides, and non-mutation of the parent environment.
  • Existing real-Git regressions cover pre-commit, commit-msg, prepare-commit-msg, credential-bearing pre-push, warm post-checkout, inherited fresh-clone post-checkout, configuration non-mutation, and exact remote SHA delivery.
  • oxfmt, oxlint, secretlint, and git diff --check: clean for the review changes.
  • The advisory pre-commit dependency audit reports existing findings (one high-severity js-yaml finding and two moderate findings); no dependencies changed in this PR.

Manual verification

Reviewed every changed hunk and searched production AgentCore sources for direct Git process execution. The only direct spawn('git', ...) calls outside the shared runner are test fixture helpers. Current main has no changed-path overlap since the branch base, and its merge tree is conflict-free.

Related work

Supersedes #363 after this replacement's required checks and automated review are green. Do not close #363 before that condition is met.

no linked issue: this PR replaces and credits an existing pull request rather than resolving a separately tracked issue.

Olivier Rossant and others added 2 commits September 8, 2026 10:41
A stage that had already produced its artifacts died with
git_commit_failed, and the only detail captured was npm's warnings:

  commit_failed — npm warn Unknown project config "strict-peer-dependencies"
                  npm warn Unknown project config "auto-install-peers"

The cause is the USER repo's own pre-commit hook. A project using husky +
lint-staged installs hooks that shell out to npm/npx, but the runtime
deliberately never installs the checkout's dependencies (see the
inode-budget notes in workspace.js), so the hook exits non-zero and the
stage's completed work is lost for a reason unrelated to that work.

Set `core.hooksPath` to a hook-free path for every engine-owned git
invocation, in runGit — the single choke point all of them pass through,
so operations added later inherit the policy.

Two reasons the engine must not execute repo hooks:

- CORRECTNESS: the failure above. Hooks assuming an installed dependency
  tree cannot work in this runtime.
- SECURITY: repo hooks are arbitrary untrusted code running inside the
  agent runtime, and pushes carry a short-lived credential in the
  environment. A pre-push hook could read AIDLC_GIT_PASSWORD, abort the
  push, and surface the value in the returned error detail.

`--no-verify` is NOT sufficient and would have left the same class of bug
open: it covers pre-commit and commit-msg only, so prepare-commit-msg can
still abort a commit and pre-push runs on every push. `-c core.hooksPath`
disables every hook for the invocation without mutating the repository's
own configuration.

Tests drive REAL git in throwaway repos and pin the hooks via
`core.hooksPath` rather than `.git/hooks`, which is the stronger
assertion: it proves the engine's own `-c core.hooksPath` overrides a
hooksPath the repository configured for itself. Cases cover pre-commit,
commit-msg and prepare-commit-msg (commit succeeds, hook never ran, tree
left clean), and a credential-bearing pre-push (push succeeds, the hook
never ran, and the token appears nowhere in the result). All five fail
without the change.

Rebased onto latest main (b9ab0b6).

I confirm the licensing of this contribution under the repository's MIT-0
license.

(cherry picked from commit 2570211)
Comment thread lambda/agentcore/workspace.js Outdated
Comment thread lambda/agentcore/git-runner.js Outdated

@jeromevdl jeromevdl 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.

2 small comments before approving

Sanitize inherited repository and identity overrides in the shared Git runner, and pass the protected runner into workspace directory-trust callbacks. Add regressions for credential preservation, explicit overrides, and fresh and warm checkouts.
@JWThewes

JWThewes commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @jeromevdl — addressed both in 316ce41: directory-trust setup now uses the wrapped runner, and environment sanitization lives in the shared Git runner. Added regressions; all 345 affected local tests and CI checks pass.

@jeromevdl
jeromevdl merged commit 8a81ea8 into main Sep 9, 2026
6 checks passed
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.

2 participants