JFIF  ` ` ;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 85 C   !"$"$ C hh"       } !1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz      w !1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz   ? .(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((+QI3L( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( @y -#yIޚXP2$袊 ( ( ( ( ( ( ( ( ( ( ( ( ( ( 3FE a(dN^M4GӁMoO$dssYz4]4+HM9վ2#OL@ܟH O@}LYW=p3! 7'Z&;" zםg|Ij~DǶr^2sI🆒TS(2?y}xmux#sھ[Cqg0mr\ ҏĺҋ{d^Rrr#mOŬ]\>uKcq*?ɯL񆥥Ƕ[ĆlO b[2+'c9Yl{I`:R fڽ;g8jc}ZC)ʑX匌J@:(P Hzh@#֌6Y$ʹ -ڛhJۆqi7s^{|c)]zyۿP+𗈬MaH_?LfG/U ( ( ( ( ( ( ( ("iX1?.ቨ.a?6I"|O+<.2H]aizhV3G8v|t".RcR7t+d(E~0 zD$r2ۜ~oC+Qsvx~%8 ~_|@Nկ81\gNMI#x ÞƠ W Cidlu;$1G5xh\NM#oL Zv6u+fd$HbaKiܻN?ʥt0F۟+g ]Q+r{8"28S4.ofXɑ|֗ x&s_Мp{W.14h~RME;XVX`q֡|:֢oB1fSBP_ WI~Ծ<04%ar UΉ>cy ~1b5w|Iϊ1ٯ4D`A y{-+N SkڲVCXR)M^ѠSc^3! }b}VBَ'9ִ/j~!]/DY_'׿} UQDRBͲ>GLsSnJQ(W Cx`rbF }G:~çivZ1Q =V&,gߥI}kdsѻ4RVEPEPEPEPEPEPH)I1@ (;=7JkO8j_ ٛVr\Q0 "=_Z;-8*ᛯ[ino ZU>#/9]x0]> 3u&%S9/PQVҭtIbxP ==*9 jPX7uMr3<`Z\g:^qV \WKg.잹ȩZ]][Pi۬h=V'O2VK0 TI`z'_֚CH/G Է4V 5, up쇖?夿w5j9]APn<:cs[V57 ]vAl*բp֖Z Pu{ _N]6@ȫaՏ@6sX+W1q@ tX 鴫`I8h3D Gm vCċiҝzRzP!Ҕ`p:P }>5|4VR!-䝒I,O98(=e"|ۦuK *pl?B k@u=$ Mz%%Sr~SsW۴ͧ1;Lg99qw:)Ծ~Ǟ+|7s]Md 9)V5"ھ;/?p MLGN=ҾÈ67c&XL1#}0瞾o};^̠yJ}}ǡǾ%!<zbfEe}I]ֽ NzZ-T0I WiiehRin-lPN׉bgfIìh% $L vˆ*n.Kr{waҷRnGz<+d63G <M4Ҽ' m4H ǪՅ$qpxZZ)gڊ(((((( W|sZ|akmD:rJELr~wkMCX _v4E|!e u.W%1zu?θߎ b%#QB#ApN9^78R ᭭DdA&ӶJ:h9>ct`lҝQ+9 erGn"  0=*(((((&4u+`=cpoōj:-pwp~Q*%*nH/=6a#4׎cÚwyC޾W})ǚEEQ4w_JȨÔjf5>Ϟ[<t(c ~m1K#SI1S@ Z@5F)E0 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( )8;qzj6:s3yv ӥ b|J ͭj'*"㔃Ty ėc^lb=$7 N4a. R#!{\ts '᳣xj ao% T9ڔ4|QwZ|Z20k G>xO^;]o..tn7h ֆ[\y5/glKv:xu^)-_ۏLwOY5y` 6gҴ/ɬi[X~77O_ x>}NU =?=6;XDa+XSHRlA Q<7ֺ8@ԊzSA7qB Z(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^WZ#GkM-] eRH-##0'TH( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ^cef6b88lr1ڀ< &[^dB>ץYYh#0JpP c4  QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE QE 'use strict' exports.generate = generate exports.generateFromInstall = generateFromInstall exports.submitForInstallReport = submitForInstallReport exports.submitForFullReport = submitForFullReport exports.printInstallReport = printInstallReport exports.printParseableReport = printParseableReport exports.printFullReport = printFullReport const auditReport = require('npm-audit-report') const npmConfig = require('../config/figgy-config.js') const figgyPudding = require('figgy-pudding') const treeToShrinkwrap = require('../shrinkwrap.js').treeToShrinkwrap const packageId = require('../utils/package-id.js') const output = require('../utils/output.js') const npm = require('../npm.js') const qw = require('qw') const regFetch = require('npm-registry-fetch') const perf = require('../utils/perf.js') const npa = require('npm-package-arg') const uuid = require('uuid') const ssri = require('ssri') const cloneDeep = require('lodash.clonedeep') // used when scrubbing module names/specifiers const runId = uuid.v4() const InstallAuditConfig = figgyPudding({ color: {}, json: {}, unicode: {} }, { other (key) { return /:registry$/.test(key) } }) function submitForInstallReport (auditData) { const opts = InstallAuditConfig(npmConfig()) const scopedRegistries = [...opts.keys()].filter( k => /:registry$/.test(k) ).map(k => opts[k]) scopedRegistries.forEach(registry => { // we don't care about the response so destroy the stream if we can, or leave it flowing // so it can eventually finish and clean up after itself regFetch('/-/npm/v1/security/audits/quick', opts.concat({ method: 'POST', registry, gzip: true, body: auditData })).then(_ => { _.body.on('error', () => {}) if (_.body.destroy) { _.body.destroy() } else { _.body.resume() } }, _ => {}) }) perf.emit('time', 'audit submit') return regFetch('/-/npm/v1/security/audits/quick', opts.concat({ method: 'POST', gzip: true, body: auditData })).then(response => { perf.emit('timeEnd', 'audit submit') perf.emit('time', 'audit body') return response.json() }).then(result => { perf.emit('timeEnd', 'audit body') return result }) } function submitForFullReport (auditData) { perf.emit('time', 'audit submit') const opts = InstallAuditConfig(npmConfig()) return regFetch('/-/npm/v1/security/audits', opts.concat({ method: 'POST', gzip: true, body: auditData })).then(response => { perf.emit('timeEnd', 'audit submit') perf.emit('time', 'audit body') return response.json() }).then(result => { perf.emit('timeEnd', 'audit body') result.runId = runId return result }) } function printInstallReport (auditResult) { const opts = InstallAuditConfig(npmConfig()) return auditReport(auditResult, { reporter: 'install', withColor: opts.color, withUnicode: opts.unicode }).then(result => output(result.report)) } function printFullReport (auditResult) { const opts = InstallAuditConfig(npmConfig()) return auditReport(auditResult, { log: output, reporter: opts.json ? 'json' : 'detail', withColor: opts.color, withUnicode: opts.unicode }).then(result => output(result.report)) } function printParseableReport (auditResult) { const opts = InstallAuditConfig(npmConfig()) return auditReport(auditResult, { log: output, reporter: 'parseable', withColor: opts.color, withUnicode: opts.unicode }).then(result => output(result.report)) } function generate (shrinkwrap, requires, diffs, install, remove) { const sw = cloneDeep(shrinkwrap) delete sw.lockfileVersion sw.requires = scrubRequires(requires) scrubDeps(sw.dependencies) // sw.diffs = diffs || {} sw.install = (install || []).map(scrubArg) sw.remove = (remove || []).map(scrubArg) return generateMetadata().then((md) => { sw.metadata = md return sw }) } const scrubKeys = qw`version` const deleteKeys = qw`from resolved` function scrubDeps (deps) { if (!deps) return Object.keys(deps).forEach(name => { if (!shouldScrubName(name) && !shouldScrubSpec(name, deps[name].version)) return const value = deps[name] delete deps[name] deps[scrub(name)] = value }) Object.keys(deps).forEach(name => { for (let toScrub of scrubKeys) { if (!deps[name][toScrub]) continue deps[name][toScrub] = scrubSpec(name, deps[name][toScrub]) } for (let toDelete of deleteKeys) delete deps[name][toDelete] scrubRequires(deps[name].requires) scrubDeps(deps[name].dependencies) }) } function scrubRequires (reqs) { if (!reqs) return reqs Object.keys(reqs).forEach(name => { const spec = reqs[name] if (shouldScrubName(name) || shouldScrubSpec(name, spec)) { delete reqs[name] reqs[scrub(name)] = scrubSpec(name, spec) } else { reqs[name] = scrubSpec(name, spec) } }) return reqs } function getScope (name) { if (name[0] === '@') return name.slice(0, name.indexOf('/')) } function shouldScrubName (name) { const scope = getScope(name) const cfg = npm.config // avoid the no-dynamic-lookups test return Boolean(scope && cfg.get(scope + ':registry')) } function shouldScrubSpec (name, spec) { const req = npa.resolve(name, spec) return !req.registry } function scrubArg (arg) { const req = npa(arg) let name = req.name if (shouldScrubName(name) || shouldScrubSpec(name, req.rawSpec)) { name = scrubName(name) } const spec = scrubSpec(req.name, req.rawSpec) return name + '@' + spec } function scrubName (name) { return shouldScrubName(name) ? scrub(name) : name } function scrubSpec (name, spec) { const req = npa.resolve(name, spec) if (req.registry) return spec if (req.type === 'git') { return 'git+ssh://' + scrub(spec) } else if (req.type === 'remote') { return 'https://' + scrub(spec) } else if (req.type === 'directory') { return 'file:' + scrub(spec) } else if (req.type === 'file') { return 'file:' + scrub(spec) + '.tar' } else { return scrub(spec) } } module.exports.scrub = scrub function scrub (value, rid) { return ssri.fromData((rid || runId) + ' ' + value, {algorithms: ['sha256']}).hexDigest() } function generateMetadata () { const meta = {} meta.npm_version = npm.version meta.node_version = process.version meta.platform = process.platform meta.node_env = process.env.NODE_ENV return Promise.resolve(meta) } /* const head = path.resolve(npm.prefix, '.git/HEAD') return readFile(head, 'utf8').then((head) => { if (!head.match(/^ref: /)) { meta.commit_hash = head.trim() return } const headFile = head.replace(/^ref: /, '').trim() meta.branch = headFile.replace(/^refs[/]heads[/]/, '') return readFile(path.resolve(npm.prefix, '.git', headFile), 'utf8') }).then((commitHash) => { meta.commit_hash = commitHash.trim() const proc = spawn('git', qw`diff --quiet --exit-code package.json package-lock.json`, {cwd: npm.prefix, stdio: 'ignore'}) return new Promise((resolve, reject) => { proc.once('error', reject) proc.on('exit', (code, signal) => { if (signal == null) meta.state = code === 0 ? 'clean' : 'dirty' resolve() }) }) }).then(() => meta, () => meta) */ function generateFromInstall (tree, diffs, install, remove) { const requires = {} tree.requires.forEach((pkg) => { requires[pkg.package.name] = tree.package.dependencies[pkg.package.name] || tree.package.devDependencies[pkg.package.name] || pkg.package.version }) const auditInstall = (install || []).filter((a) => a.name).map(packageId) const auditRemove = (remove || []).filter((a) => a.name).map(packageId) const auditDiffs = {} diffs.forEach((action) => { const mutation = action[0] const child = action[1] if (mutation !== 'add' && mutation !== 'update' && mutation !== 'remove') return if (!auditDiffs[mutation]) auditDiffs[mutation] = [] if (mutation === 'add') { auditDiffs[mutation].push({location: child.location}) } else if (mutation === 'update') { auditDiffs[mutation].push({location: child.location, previous: packageId(child.oldPkg)}) } else if (mutation === 'remove') { auditDiffs[mutation].push({previous: packageId(child)}) } }) return generate(treeToShrinkwrap(tree), requires, auditDiffs, auditInstall, auditRemove) }