Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Walk a directory recursively and handle each entity (files, directories, symlnks, etc).
(a port of Go's filepath.Walk
using Node.js v10+'s fs.readdir
's withFileTypes
and ES 2021)
await Walk.walk(pathname, walkFunc);
async function walkFunc(err, pathname, dirent) {
// err is failure to lstat a file or directory
// pathname is relative path, including the file or folder name
// dirent = { name, isDirectory(), isFile(), isSymbolicLink(), ... }
if (err) {
return false;
}
console.log(pathname);
}
npm install --save @root/walk
You can use this with Node v12+ using Vanilla JS (CommonJS) or ES2021 (ES Modules).
var Walk = require("@root/walk");
var path = require("path");
Walk.walk("./", walkFunc).then(function () {
console.log("Done");
});
// walkFunc must be async, or return a Promise
function walkFunc(err, pathname, dirent) {
if (err) {
// throw an error to stop walking
// (or return to ignore and keep going)
console.warn("fs stat error for %s: %s", pathname, err.message);
return Promise.resolve();
}
// return false to skip a directory
// (ex: skipping "dot file" directories)
if (dirent.isDirectory() && dirent.name.startsWith(".")) {
return Promise.resolve(false);
}
// fs.Dirent is a slimmed-down, faster version of fs.Stats
console.log("name:", dirent.name, "in", path.dirname(pathname));
// (only one of these will be true)
console.log("is file?", dirent.isFile());
console.log("is link?", dirent.isSymbolicLink());
return Promise.resolve();
}
@root/walk
can be used with async/await or Promises.
import { walk } from "@root/walk";
import path from "path";
const walkFunc = async (err, pathname, dirent) => {
if (err) {
throw err;
}
if (dirent.isDirectory() && dirent.name.startsWith(".")) {
return false;
}
console.log("name:", dirent.name, "in", path.dirname(pathname));
};
await walk("./", walkFunc);
console.log("Done");
Walk.walk
walks pathname
(inclusive) and calls walkFunc
for each file system entity.
It can be used with Promises:
Walk.walk(pathname, promiseWalker).then(doMore);
Or with async / await:
await Walk.walk(pathname, asyncWalker);
The behavior should exactly match Go's
filepath.Walk
with a few exceptions:
dirent
rather than lstat
(for performance, see withFileStats
)Handles each directory entry
async function walkFunc(err, pathname, dirent) {
// `err` is a file system stat error
// `pathname` is the full pathname, including the file name
// `dirent` is an fs.Dirent with a `name`, `isDirectory`, `isFile`, etc
return null;
}
Create a custom walker with these options:
withFileStats: true
walkFunc will receive fs.Stats[] from fs.lstat instead of fs.Dirent[]sort: (entities) => entities.sort()
sort and/or filter entities before walking themconst walk = Walk.create({
withFileStats: true,
sort: (entities) => entities.sort()),
});
By default walk
will use fs.readdir(pathname, { withFileTypes: true })
which returns fs.Dirent[]
,
which only has name and file type info, but is much faster when you don't need the complete fs.Stats
.
Enable withFileStats
to use get full fs.Stats
. This will use fs.readdir(pathname)
(returning String[]
)
and then call fs.lstat(pathname)
- including mtime
, birthtime
, uid
, etc - right after.
const walk = Walk.create({
withFileStats: true,
});
walk(".", async function (err, pathname, stat) {
console.log(stat.name, stat.uid, stat.birthtime, stat.isDirectory());
});
Sometimes you want to give priority to walking certain directories first.
The sort
option allows you to specify a funciton that modifies the fs.Dirent[]
entities (default) or String[]
filenames (withFileStats: true
).
Since you must return the sorted array, you can also filter here if you'd prefer.
const byNameWithoutDotFiles = (entities) => {
// sort by name
// filter dot files
return entities
.sort((a, b) => {
if (a.name > b.name) {
return 1;
}
if (a.name < b.name) {
return -1;
}
return 0;
})
.filter((ent) => !ent.name.startsWith("."));
};
const walk = Walk.create({ sort: byNameWithoutDotFiles });
walk(".", async function (err, pathname, stat) {
// each directories contents will be listed alphabetically
console.log(pathname);
});
Note: this gets the result of fs.readdir()
. If withFileStats
is true
you will get a String[]
of filenames - because this hapens BEFORE fs.lstat()
is called - otherwise you will get fs.Dirent[]
.
If you're like me and you hate dependencies, here's the bare minimum node fs walk function:
See snippet.js or https://therootcompany.com/blog/fs-walk-for-node-js/.
The main module, as published to NPM, is licensed the MPL-2.0.
The ~50 line snippet is licensed CC0-1.0 (Public Domain).
FAQs
fs.walk for node (as a port of Go's filepath.Walk)
We found that @root/walk demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.