Skip to content

Feature suggestion: Do not follow symlinks #622

@kmturley

Description

@kmturley

Firstly, thank-you for the useful library, and your time to read this issue.

I use glob to scan a file system which I don't control, and can include aliases / symlinks. Example plugin file/folder structure:

|- plugin.vst3
|- LICENSE.md
|- VST Plug-Ins -> /Library/Audio/Plug-Ins/VST3

RunningglobSync('**/*.*'); follows the symlinks and outputs many more files:

[
  'plugin.vst3',
  'LICENSE.md',
  'VST Plug-Ins/plugin2.vst3',
  ...
  'VST Plug-Ins/plugin9999.vst3',
]

Trying with options such as { follow: false, nodir: true } does not filter out the symlinks.

As mentioned in the README glob relies on bash behavior:
https://github.com/isaacs/node-glob/blob/main/README.md?plain=1#L537

And this comment mentioned that "do not follow symlinks" is not a supported feature:
#242 (comment)

If I run the same code with { realpath: true} then its clearer what is happening:

[
  'plugin.vst3',
  'LICENSE.md',
  '/Library/Audio/Plug-Ins/VST3/plugin2.vst3',
  '/Library/Audio/Plug-Ins/VST3/plugin3.vst3',
  ...
]

I can prevent this behavior myself outside of glob such as:

1. Remove symlinks before running glob

import fs from 'fs/promises';
import path from 'path';

async function removeSymlinks(dir) {
  try {
    const entries = await fs.readdir(dir, { withFileTypes: true });
    
    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      
      if (entry.isSymbolicLink()) {
        await fs.unlink(fullPath);
        console.log(`Removed symlink: ${fullPath}`);
      } else if (entry.isDirectory()) {
        await removeSymlinks(fullPath); // Recurse into subdirectories
      }
    }
  } catch (err) {
    console.error(`Error processing ${dir}:`, err);
  }
}

// Example usage:
// removeSymlinks('/path/to/directory').catch(console.error);

2. Use glob realpath: true and filter out non matching paths

import fs from 'fs/promises';
import path from 'path';
import { globSync } from 'glob';

function filterDirectoriesOutsideBase(baseDir) {
  const allPaths = globSync(`${baseDir}/**/*.*`, { realpath: true });
  return allPaths.filter((p) => !p.startsWith(baseDir));
}

// Example usage:
// const filteredPaths = filterDirectoriesOutsideBase('/path/to/directory');
// console.log('Filtered directories outside base:', filteredPaths);

However if low level filesystems can filter out symlinks, it would be more optimal to filter out symlinks inside the glob library. There are several benefits of a Do not follow symlinks feature for glob users:

  1. Security - For clients who don't control the filesystem, disabling symlinks prevents a malicious actor using symlinks to trick the program/script into reading a malicious file, or scanning another drive containing other files, wasting resources or causing scripts to error.
  2. Performance - Symlinks could cause a script to scan many more paths than expected and recursive loops etc
  3. Developer time - I have already spent several hours on trying to find out why my script was copying the extra files, and incorrectly using follow options. Multiply the time-saving by the 190 million weekly downloads of this library, I think there is potential to a large amount of developer time.

Thank-you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions