Skip to content

fix(repo-fleet-hygiene): symlinked/junctioned intermediate directories are skipped silently #2711

Description

@kyle-sexton

Problem

Discovery skips symlinked and junctioned intermediate directories with no finding, no counter,
and no header line. Entire subtrees vanish from the audit with zero disclosure.

This matters most on Windows, where a directory junction — a common shape in real trees — tests as
a symlink under Git Bash. An operator whose fleet root reaches repositories through a junction gets
a report that says nothing about them and nothing about the omission.

reference/security-review.md asserts "Filesystem discovery is bounded and does not follow symbolic
links" without disclosing that the skip is silent, so the security posture reads as complete when
the reporting is not.

Evidence

plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.sh, inside discover_repositories
(locate by content):

for child in "$dir"/* "$dir"/.[!.]* "$dir"/..?*; do
  [[ -d "$child" && ! -L "$child" ]] || continue

The continue is the only trace. DISCOVERY_SKIP_PATHS / DISCOVERY_NONREPO_COUNT /
DISCOVERY_UNREADABLE_COUNT are all untouched on this branch, so the Discovery skips: N non-repository, M unreadable header line cannot reflect it either.

Empirically, under Git Bash on Windows 11, a directory created with mklink /J satisfies both
[[ -d ]] and [[ -L ]], so it takes this branch:

$ cmd //c "mklink /J junc ..\target"
$ [[ -d junc ]] && echo yes   # yes
$ [[ -L junc ]] && echo yes   # yes

Symlinked/junctioned roots are already handled separately — a CLI root hard-fails, a configured
root degrades to stale-config-entry. That path is correct and is not in scope here.

Proposed change

Keep the non-following behavior — it is correct. Record the skipped paths in a
DISCOVERY_SYMLINK_PATHS array beside the existing DISCOVERY_SKIP_PATHS, and emit them as an
UNKNOWN finding alongside the existing discovery-skip disclosure, stating that Windows directory
junctions test as symlinks. Add the count to the discovery-skips header line. Update
security-review.md to say the skip is disclosed rather than silent.

A --follow-links escape hatch is explicitly not required; disclosure is the requirement.

Acceptance criteria

  • A symlinked or junctioned intermediate directory produces a visible UNKNOWN finding naming
    the path.
  • The finding text states that Windows directory junctions test as symlinks.
  • The discovery-skips header line accounts for the symlink skips.
  • Discovery still does not descend through the link.
  • The new finding kind has a Finding: assertion in audit-fleet.test.sh
    (scripts/check-fleet-finding-test-coverage.sh --check enforces this).
  • security-review.md no longer implies the non-following skip is silent.
  • The symlinked-root handling is untouched.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions