getFileMode uses stat(), making its isSymbolicLink() branch unreachable dead code
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 84/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- tooling
Research direction
Start in src/mcp/github-file-ops-server.ts around getFileMode at lines 172-190 and compare it with the readFile use around line 261. The suggested minimal fix is to remove the unreachable isSymbolicLink branch and make the current symlink-dereferencing behavior explicit. Done means the dead branch no longer claims mode 120000 support; only touch src/mcp/path-validation.ts if attempting the larger lstat/readlink behavior.
Written by the indexing model from the issue text.
Description
Type: bug / dead code
Severity: low
Area: src/mcp/github-file-ops-server.ts
Effort: small (the safe fix is trivial; full symlink support is not)
Summary
getFileMode branches on fileStat.isSymbolicLink() to return git's symlink
mode 120000. It calls stat(), which follows symlinks, so the returned
Stats never describes a link - isSymbolicLink() is always false and the
branch can never be taken.
Affected code
src/mcp/github-file-ops-server.ts:172-190
const fileStat = await stat(filePath); // follows symlinks
if (fileStat.isFile()) {
...
} else if (fileStat.isDirectory()) {
return "040000";
} else if (fileStat.isSymbolicLink()) { // unreachable
return "120000";
} else {
return "100644";
}
Actual behaviour today
A symlink passed to commit_files is committed as a regular file whose
content is the link target's content:
getFileModestats through the link and returns100644(or100755).readFile(fullPath)(line 261) also follows the link and reads the target's
bytes.- The tree entry is a normal blob.
So the link is silently dereferenced. That is a defensible behaviour, but it is
not the behaviour the code claims to implement, and there is no test covering it.
Why the obvious fix is wrong
Swapping stat for lstat in isolation makes things worse, not better. Git
stores a symlink as a blob whose content is the target path. With lstat alone
the entry would get mode 120000 while readFile still supplies the target's
file content as the blob - producing a committed "symlink" pointing at a path
made of the target file's bytes. That is a corrupt tree.
Correct symlink support requires changing both halves together:
const fileStat = await lstat(filePath);
if (fileStat.isSymbolicLink()) {
const target = await readlink(filePath);
// blob content must be `target`, mode 120000
}
...plus a decision about whether links escaping the repo should be rejected,
which interacts with validatePathWithinRepo (src/mcp/path-validation.ts).
validatePathWithinRepo already resolves symlinks and rejects ones landing
outside the repo root, so an in-repo link is the only case that reaches here.
Suggested fix
Two options, in order of preference:
-
Minimal and honest - drop the unreachable branch and document that
symlinks are intentionally dereferenced:// stat() follows symlinks, and readFile() below does too, so a symlinked // path is committed as a regular blob holding the target's content. That is // deliberate: committing mode 120000 would require the blob content to be // the link target path instead. const fileStat = await stat(filePath); if (fileStat.isDirectory()) return "040000"; if (fileStat.isFile() && fileStat.mode & constants.S_IXUSR) return "100755"; return "100644"; -
Full support -
lstat+readlink, with the blob content changed in
commit_filesat the same time, and a test asserting the committed tree entry
for a symlink. Larger change; only worth it if preserving links in commits is
actually wanted.
Either way the current state - a branch that documents behaviour the code does
not have - should not stay.
- Dominant language
- TypeScript
- Stars
- 8.9k
- Forks
- 2.2k
- Avg merge
- 3d 6h
- Merged PRs (30d)
- 3
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from anthropics/claude-code-action
-
Branch link in finished comment broken on GitHub Enterprise Server (regex hardcodes github.com) Openbug p3
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
anthropics/claude-code-action#1843 · 4 comments ·
-
bug mcp p3
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
anthropics/claude-code-action#1841 ·
-
ensureProperlyEncodedUrl truncates PR links at "=" in the title and leaves path spaces unencoded Openbug p3
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
anthropics/claude-code-action#1839 ·
-
bug p3
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
anthropics/claude-code-action#1798 ·
-
bug duplicate p2
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
anthropics/claude-code-action#1796 ·
All issues in anthropics/claude-code-action
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100