@vigilbase/cli
Version:
Vigilbase CLI — security scanners, agent tools, and Vigilbase One from the terminal.
46 lines (45 loc) • 2.19 kB
JavaScript
import { execFile } from 'node:child_process';
import { userInfo } from 'node:os';
import { promisify } from 'node:util';
import { platform } from '../lib/platform.js';
/**
* macOS access control lists. An allow entry grants access whatever the mode bits say, and Node's stat can't see
* entries, so on darwin the credentials paths are checked with /bin/ls and cleaned with /bin/chmod. Other platforms
* have nothing to do: Linux's chmod 0600 masks a default POSIX ACL.
*/
const run = promisify(execFile);
/**
* Directory rights that let another user add, remove or replace an entry, remove the directory, or take over its ACL
* or ownership (and then grant themselves the rest). The boundaries keep writeattr and writeextattr from matching.
*/
const CHANGES_ENTRIES = /\b(?:add_file|add_subdirectory|delete_child|delete|write|append|writesecurity|chown)\b/;
/** Remove every entry from a path the CLI just created, so it doesn't keep one inherited from its parent. */
export async function stripAcl(path) {
if (platform() !== 'darwin')
return;
await run('/bin/chmod', ['-N', path]);
}
/** The ACL entries of each path, in order, like ` 0: group:everyone allow read`. Empty off darwin. */
export async function aclEntries(paths) {
if (platform() !== 'darwin' || !paths.length)
return paths.map(() => []);
// -q prints a newline in a name as ?, -f keeps the operands' order.
const { stdout } = await run('/bin/ls', ['-ledqf', '--', ...paths]);
const entries = [];
for (const line of stdout.split('\n')) {
if (/^\s*\d+: /.test(line))
entries.at(-1)?.push(line.trim());
else if (line)
entries.push([]);
}
if (entries.length !== paths.length)
throw new Error('Unexpected /bin/ls output.');
return entries;
}
/** Whether an entry lets someone other than the current user change the directory's entries. */
export function letsOthersChangeEntries(entry) {
const match = /^\d+: (.+?) (?:inherited )?(allow|deny) (\S+)/.exec(entry);
if (!match || match[2] !== 'allow' || !CHANGES_ENTRIES.test(match[3]))
return false;
return match[1] !== `user:${userInfo().username}`;
}