Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions plugin/core/git.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,9 @@ export async function isWorktree(dir) {
return false
}

// Verify it contains a gitdir reference
// Verify it contains a gitdir reference pointing to a worktree administrative directory
const content = await readFile(gitPath, 'utf-8')
return content.startsWith('gitdir:')
return content.startsWith('gitdir:') && /[/\\]worktrees[/\\][^/\\]+\s*$/.test(content)
Comment on lines +233 to +235

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file first, then inspect the relevant section.
ast-grep outline plugin/core/git.js --view expanded || true

echo '--- relevant lines ---'
sed -n '180,280p' plugin/core/git.js

echo '--- search for worktree/gitdir helpers ---'
rg -n "worktree|gitdir|modules|commonDir|worktrees" plugin/core/git.js plugin -g '!**/node_modules/**' || true

Repository: athal7/opencode-devcontainers

Length of output: 18821


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline plugin/core/git.js --view expanded || true

echo '--- relevant lines ---'
sed -n '180,280p' plugin/core/git.js

echo '--- search for worktree/gitdir helpers ---'
rg -n "worktree|gitdir|modules|commonDir|worktrees" plugin/core/git.js plugin -g '!**/node_modules/**' || true

Repository: athal7/opencode-devcontainers

Length of output: 18821


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- usages of isWorktree and getWorktreeMainRepo ---'
rg -n "\bisWorktree\b|\bgetWorktreeMainRepo\b" plugin -g '!**/node_modules/**' || true

echo '--- inspect create/list call sites ---'
sed -n '1,120p' plugin/core/worktree.js
echo '---'
sed -n '120,220p' plugin/core/worktree.js
echo '---'
sed -n '400,470p' plugin/index.js

Repository: athal7/opencode-devcontainers

Length of output: 9175


Anchor the gitdir: path to the repo’s own .git/worktrees/ directory.
/[/\\]worktrees[/\\][^/\\]+\s*$ also matches submodule admin dirs like .git/modules/worktrees/foo, so a submodule mounted at worktrees/foo can be misclassified as a worktree. Parse the gitdir: target and require it to live under the repository’s .git/worktrees/ tree; add a regression test for a nested worktrees/... submodule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/core/git.js` around lines 233 - 235, Update the worktree detection
logic around the gitdir content check to parse the target path and require it to
resolve beneath the repository’s own .git/worktrees directory, rather than
matching any path segment named worktrees. Preserve valid worktree detection
while rejecting nested submodule paths such as .git/modules/worktrees/foo, and
add a regression test covering that case.

} catch {
return false
}
Expand Down
19 changes: 19 additions & 0 deletions test/unit/git-worktree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,25 @@ describe('isWorktree', () => {
const result = await isWorktree('/nonexistent/path')
assert.strictEqual(result, false)
})

test('returns false for a submodule directory', async () => {
const subRepo = join(testDir, 'sub-repo')
const submodulePath = join(mainRepo, 'sub')

mkdirSync(subRepo, { recursive: true })
execSync('git init -b main', { cwd: subRepo })
writeFileSync(join(subRepo, 'sub.txt'), 'sub')
execSync('git add .', { cwd: subRepo })
execSync('git commit -m "sub commit"', { cwd: subRepo })

// Add submodule to main repo
execSync('git config --global protocol.file.allow always', { cwd: mainRepo })
execSync(`git submodule add ${subRepo} sub`, { cwd: mainRepo })
Comment on lines +78 to +79

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,220p' test/unit/git-worktree.test.js

Repository: athal7/opencode-devcontainers

Length of output: 7385


Avoid global Git config and shell interpolation here

git config --global protocol.file.allow always leaks into later Git commands. Use git -c protocol.file.allow=always submodule add and pass subRepo as an argument instead of interpolating it into the shell command.

🧰 Tools
🪛 OpenGrep (1.25.0)

[ERROR] 79-79: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/git-worktree.test.js` around lines 78 - 79, Update the submodule
setup in the git worktree test to remove the global protocol.file.allow
configuration and invoke submodule add with the per-command git -c setting. Pass
subRepo through the command execution API’s argument mechanism rather than
interpolating it into the shell command, while preserving mainRepo as the
working directory.

Source: Linters/SAST tools

execSync('git commit -m "add submodule"', { cwd: mainRepo })

const result = await isWorktree(submodulePath)
assert.strictEqual(result, false)
})
})

describe('getWorktreeMainRepo', () => {
Expand Down