{"version":3,"sources":["../../../bin/fetch-log.ts","../../../crypto.ts","../../../bin/find-root.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander'\nimport crypto, { webcrypto } from 'crypto'\nimport fs from 'fs'\nimport * as jose from 'jose'\nimport StreamZip from 'node-stream-zip'\nimport { Readable } from 'node:stream'\nimport { finished } from 'node:stream/promises'\nimport path from 'path'\n\nimport { Entry as KeyRingEntry } from '@napi-rs/keyring'\nimport prompts from 'prompts'\n\nimport { KEY_WRAPPING_ALG } from '../crypto'\nimport { pkg } from './find-root'\n\nasync function getPassphrase(): Promise<string> {\n  const service = `${pkg.name} Zotero plugin`\n  const account = `${pkg.name}-debug-log`\n  const entry = new KeyRingEntry(service, account)\n  let passphrase = entry.getPassword()\n  if (!passphrase) {\n    const response = await prompts({\n      type: 'password',\n      name: 'passphrase',\n      message: `Enter a passphrase to decrypt your private key for ${service} ${account}:`,\n    })\n    entry.setPassword(passphrase = response.passphrase)\n  }\n  return passphrase\n}\n\nconst oops = (...args) => {\n  console.error(...args)\n  process.exit(1)\n}\n\nconst program = new Command()\nprogram\n  .description('A script to fetch debug logs.')\n  .option('-p, --private <path>', 'Path for the encrypted private key .pem file', 'private.pem')\n  .option('-k, --keep', 'Keep the downloaded zip', false)\n  .argument('<debug log id>', 'debug log ID to fetch')\n  .parse(process.argv)\nconst options = program.opts()\nconst args = program.args\n\nif (!args.length) oops('No log ID')\n\nlet m = args[0].match(/^(?<key>[a-z0-9]+)-(?<host>[^.-]+)(?<tags>([.][^.]+)*)$/i)\nif (!m) oops(args[0], 'is not a valid log ID')\n\nconst { host, key, tags } = m.groups\nif (host !== 'fbin') oops('Unexpected debug log host', host)\n\noptions.encrypted = tags.split('.').includes('enc')\noptions.zip = path.join('logs', `${key}.zip`)\noptions.url = `https://filebin.net/${key}/${key}.zip`\n\nif (options.encrypted) {\n  if (!options.private) oops('No private key provided')\n  if (!fs.existsSync(options.private)) oops('Private key', options.private, 'does not exist')\n  if (!fs.existsSync('package.json')) oops('package.json does not exist in the current directory')\n}\n\nconst logs = path.join('logs', key)\nconsole.log(options.url, '=>', logs)\nif (!fs.existsSync(logs)) {\n  fs.mkdirSync(logs, { recursive: true })\n}\n\nasync function getPrivateKey(): Promise<webcrypto.CryptoKey> {\n  if (!options.encrypted) return undefined\n\n  const privateKeyObject = crypto.createPrivateKey({\n    key: fs.readFileSync(options.private, 'utf-8'),\n    format: 'pem',\n    passphrase: await getPassphrase(),\n  })\n  const unencryptedKeyPEM = privateKeyObject.export({\n    type: 'pkcs8',\n    format: 'pem',\n  }).toString()\n  return await jose.importPKCS8(\n    unencryptedKeyPEM,\n    KEY_WRAPPING_ALG,\n  )\n}\nasync function main() {\n  try {\n    const response = await fetch(options.url, {\n      method: 'GET',\n      headers: {\n        'User-Agent': 'curl/7.81.0', // filebin does not seem to accept custom user agents\n        'Accept-Encoding': 'identity',\n        Accept: '*/*',\n      },\n    })\n    if (!response.ok) oops(`Failed to download: ${response.statusText}`)\n\n    const download = fs.createWriteStream(options.zip)\n    await finished(Readable.fromWeb(response.body as any).pipe(download))\n\n    const zipfile = new StreamZip.async({ file: options.zip })\n    const entries = Object.values(await zipfile.entries()).filter(entry => !entry.isDirectory)\n\n    const privateKey = await getPrivateKey()\n\n    for (const entry of entries) {\n      const m = entry.name.match(/(?<filename>.+)\\.(?<type>jwe)$/i)\n      const filename = m?.groups!.filename || entry.name\n      const type = (m?.groups!.type || '').toLowerCase()\n      const target = path.join('logs', filename)\n\n      if (options.encrypted && !type) oops('Unexpected unencrypted contents', entry.name)\n      if (type && !options.encrypted) oops('Unexpected encrypted contents', entry.name)\n      switch (type) {\n        case '':\n          fs.writeFileSync(target, await zipfile.entryData(entry.name))\n          break\n        case 'jwe': {\n          const { plaintext } = await jose.compactDecrypt((await zipfile.entryData(entry.name)).toString('utf8'), privateKey)\n          fs.writeFileSync(target, plaintext)\n          break\n        }\n        default:\n          oops('Unexpected log entry', entry.name)\n          break\n      }\n    }\n  }\n  finally {\n    if (!options.keep && fs.existsSync(options.zip)) fs.unlinkSync(options.zip)\n  }\n}\n\nmain().catch(err => {\n  oops(err.message)\n})\n","export const KEYTYPE = 'RSA'\nexport const KEY_WRAPPING_ALG = 'RSA-OAEP-256'\nexport const CONTENT_ENCRYPTION_ALG = 'A256GCM'\nexport const RSA_HASH = 'SHA-256'\nexport const RSA_ALGORITHM = 'RSA-OAEP'\nexport const CIPHER_ALGORITHM = 'aes256'\nexport const AES_KEY_LENGTH = 256\n// export const AES_KEY_BYTES = 32\n\n// export const AES_ALGORITHM = 'AES-GCM'\n// export const IV_LENGTH = 12\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nconst folders = process.cwd().split(path.sep)\nconst rootIndex = folders.findIndex((folder: string, i: number) => fs.existsSync(path.join(folders.slice(0, i + 1).join(path.sep), 'package.json')))\nexport const root = (rootIndex > 0 ? folders.slice(0, rootIndex + 1) : folders).join(path.sep)\nconsole.log('project directory:', root)\n\nexport const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'))\n"],"mappings":"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","names":["path","fs","KeyRingEntry","prompts","args","path","fs","crypto","StreamZip","m"]}