#!/usr/bin/env node const nativeMessage = require('chrome-native-messaging'); const childProcess = require('child_process'); const fs = require('fs/promises'); const path = require('path'); const passwds = path.join(process.env.HOME, '.password-store'); async function recurseDir(dir) { const array = []; const paths = await fs.readdir(dir); const root = path.basename(dir); for (const file of paths) { const stat = await fs.stat(path.join(dir, file)); if (file.startsWith('.')) { continue; } if (stat.isDirectory()) { const children = await recurseDir(path.join(dir, file)); array.push(...children); } else { array.push(path.join(dir, file.replace('.gpg', ''))); } } return array; } async function findSiteInPasswords(siteDomain) { const allPasswords = await recurseDir(passwds); let sdwTLD = siteDomain.split('.'); sdwTLD = sdwTLD.slice(sdwTLD.length - 2, sdwTLD.length - 1).join('.'); return allPasswords .map((file) => file.substring(passwds.length + 1)) .filter((item) => { const name = item.toLowerCase(); return name.includes(siteDomain) || (sdwTLD && name.includes(sdwTLD)); }) .slice(0, 4); } async function getPassword(password) { return new Promise((resolve, reject) => { childProcess.exec(`pass ${password}`, (error, stdout, stderr) => { if (stderr && stderr.length) { return resolve(null); } resolve(stdout.toString().split('\n')); }); }); } process.stdin .pipe(new nativeMessage.Input()) .pipe( new nativeMessage.Transform(function (msg, push, done) { if (msg.domain) { findSiteInPasswords(msg.domain).then((results) => { push({ results }); done(); }); return; } if (msg.getPassword) { getPassword(msg.getPassword).then(([password, username]) => { push({ password, username }); done(); }); return; } push({ hello: true }); done(); }) ) .pipe(new nativeMessage.Output()) .pipe(process.stdout);