harden agent workspace boundaries - #321
Conversation
max-california
left a comment
There was a problem hiding this comment.
🤖 Agentic Code Review
Summary: the hardening looks solid overall. Centralizing path validation, blocking .git access during direct and recursive reads, and moving credentials outside the repo all head in the right direction. I did find one correctness issue in the clone fallback flow that can leave stale credential files behind.
BLOCKER
- Credential file leak on branch-clone fallback
- File:
src/main/java/org/remus/giteabot/agent/validation/WorkspaceService.java - Lines: around 83–86
- In
prepareWorkspace, when the initialgit clone --branch ...fails andprNumber != null, you callcleanupWorkspace(workspaceDir)and then immediately reassignworkspaceDir = createWorkspaceDirectory(); credentialsFile = createCredentialsFile(...). - The problem is that the first workspace root is deleted before the old
credentialsFilereference is cleared, and thencredentialsFileis overwritten with the second file path. IfcleanupWorkspace(workspaceDir)fails partially (which it already tolerates by only logging), the first credential store file can be left behind with no remaining reference fordeleteCredentialsFile(...)to clean up later. - Because this code is specifically handling access tokens, I’d treat that as a merge-blocking cleanup bug.
- Suggested fix: either:
- explicitly
deleteCredentialsFile(credentialsFile)before overwriting it in the fallback path, and then null it out, or - keep the workspace root/credential lifecycle together in a small holder object so the old credential file cannot be dropped during retry paths.
- explicitly
- File:
LOW
-
persistCredentialHelperignores failures while making later authenticated operations depend on it- File:
src/main/java/org/remus/giteabot/agent/validation/WorkspaceService.java - Lines: around 430–438
- Both
git config --local ... credential.helpercommands discard their results. If either fails, laterfetch/pushoperations may fail in less obvious ways. - Not necessarily a blocker because clone already succeeded and cleanup still works, but logging or checking these results would make failures much easier to diagnose.
- File:
-
Path guard now rejects benign normalized paths like
src/../inside.txt- File:
src/main/java/org/remus/giteabot/util/WorkspacePaths.java - Lines: around 31–40
- This is a deliberate hardening choice, but it is stricter than the previous implementation and may reject user/tool input that still normalizes safely inside the workspace.
- If that compatibility change is intentional, consider documenting it where tool inputs are described; otherwise callers may see surprising “traversal” errors for paths that are semantically in-bounds.
- File:
Read-only agentic review by AI Git Bot
|
Addressed in BLOCKER — credential file leak on branch-clone fallback: Fixed. The workspace root and its credential-store file now live in a single LOW 1 — persistent credential.helper: Already addressed by the current code: credentials are passed as per-command LOW 2 — strict |
tmseidel
left a comment
There was a problem hiding this comment.
Just the two small code-smells, the rest looks good to me 👍
| // Git reads repository-controlled configuration after untrusted code ran in the workspace. | ||
| disabledHooksDirectory = Files.createTempDirectory("ai-git-bot-empty-hooks-"); | ||
| emptyGlobalGitConfig = Files.createTempFile(disabledHooksDirectory, "global-", ".gitconfig"); | ||
| List<String> gitCommand = new ArrayList<>(command.length + 7); |
There was a problem hiding this comment.
this must be 6 not 7, since command[0] will be already inserted
| List<String> gitCommand = new ArrayList<>(command.length + 7); | |
| List<String> gitCommand = new ArrayList<>(command.length + 6); |
| gitCommand.add("core.fsmonitor=false"); | ||
| gitCommand.add("-c"); | ||
| gitCommand.add("credential.helper="); | ||
| for (int index = 1; index < command.length; index++) { |
There was a problem hiding this comment.
The for-loop can be written more easily with
gitCommand.addAll(Arrays.asList(command).subList(1, command.length));|
Both code smells are fixed in
|
What was done and why?
Testing
AI use