|
1 | 1 | const {promisify} = require('util') |
2 | 2 | const fs = require('fs') |
3 | 3 | const readFile = promisify(fs.readFile) |
| 4 | +const lstat = promisify(fs.lstat) |
| 5 | +const readdir = promisify(fs.readdir) |
4 | 6 | const parse = require('json-parse-even-better-errors') |
| 7 | + |
| 8 | +const { resolve, dirname, join, relative } = require('path') |
| 9 | + |
5 | 10 | const rpj = path => readFile(path, 'utf8') |
6 | | - .then(data => normalize(stripUnderscores(parse(data)))) |
| 11 | + .then(data => readBinDir(path, normalize(stripUnderscores(parse(data))))) |
7 | 12 | .catch(er => { |
8 | 13 | er.path = path |
9 | 14 | throw er |
10 | 15 | }) |
| 16 | + |
11 | 17 | const normalizePackageBin = require('npm-normalize-package-bin') |
12 | 18 |
|
| 19 | +// load the directories.bin folder as a 'bin' object |
| 20 | +const readBinDir = async (path, data) => { |
| 21 | + if (data.bin) |
| 22 | + return data |
| 23 | + |
| 24 | + const m = data.directories && data.directories.bin |
| 25 | + if (!m || typeof m !== 'string') |
| 26 | + return data |
| 27 | + |
| 28 | + // cut off any monkey business, like setting directories.bin |
| 29 | + // to ../../../etc/passwd or /etc/passwd or something like that. |
| 30 | + const root = dirname(path) |
| 31 | + const dir = join('.', join('/', m)) |
| 32 | + data.bin = await walkBinDir(root, dir, {}) |
| 33 | + return data |
| 34 | +} |
| 35 | + |
| 36 | +const walkBinDir = async (root, dir, obj) => { |
| 37 | + const entries = await readdir(resolve(root, dir)).catch(() => []) |
| 38 | + for (const entry of entries) { |
| 39 | + if (entry.charAt(0) === '.') |
| 40 | + continue |
| 41 | + const f = resolve(root, dir, entry) |
| 42 | + // ignore stat errors, weird file types, symlinks, etc. |
| 43 | + const st = await lstat(f).catch(() => null) |
| 44 | + if (!st) |
| 45 | + continue |
| 46 | + else if (st.isFile()) |
| 47 | + obj[entry] = relative(root, f) |
| 48 | + else if (st.isDirectory()) |
| 49 | + await walkBinDir(root, join(dir, entry), obj) |
| 50 | + } |
| 51 | + return obj |
| 52 | +} |
| 53 | + |
13 | 54 | // do not preserve _fields set in files, they are sus |
14 | 55 | const stripUnderscores = data => { |
15 | 56 | for (const key of Object.keys(data).filter(k => /^_/.test(k))) |
|
0 commit comments